From d4ed579838e9e2f3ad056c06988c82767448d504 Mon Sep 17 00:00:00 2001 From: olivier <> Date: Tue, 19 Jan 2010 20:54:49 +0000 Subject: [PATCH] add MediaCollection.get_countries() and get_ethnic_groups() ; consolidate media models --- telemeta/models/crem.py | 60 +++++++++++++++++++++-------- telemeta/tests/model_tests.py | 8 ++++ telemeta/util/unaccent.py | 71 +++++++++++++++++++++++++++++++++++ 3 files changed, 123 insertions(+), 16 deletions(-) create mode 100644 telemeta/util/unaccent.py diff --git a/telemeta/models/crem.py b/telemeta/models/crem.py index fee05720..12a4579e 100755 --- a/telemeta/models/crem.py +++ b/telemeta/models/crem.py @@ -36,6 +36,7 @@ from django.db import models import cremquery as query from xml.dom.minidom import getDOMImplementation +from telemeta.util.unaccent import unaccent_icmp class ModelCore(models.Model): @@ -116,6 +117,17 @@ class MediaCore(ModelCore): return False is_well_formed_id = classmethod(is_well_formed_id) + def save(self, force_insert=False, force_update=False, using=None): + raise MissingUserError("save() method disabled, use save_by_user()") + + def save_by_user(self, user, force_insert=False, force_update=False, using=None): + "Save a media object and add a revision" + super(MediaCore, self).save(force_insert, force_update, using) + Revision(element_type=self.element_type, element_id=self.id, user=user).touch() + + def get_revision(self): + return Revision.objects.filter(element_type=self.element_type, element_id=self.id).order_by('-time')[0] + class Meta: abstract = True @@ -124,6 +136,7 @@ class MetaCore: class MediaCollection(MediaCore): "Describe a collection of items" + element_type = 'collection' PUBLIC_ACCESS_CHOICES = (('none', 'none'), ('metadata', 'metadata'), ('metadata', 'full')) reference = models.CharField(unique=True, max_length=250, @@ -180,14 +193,6 @@ class MediaCollection(MediaCore): def __unicode__(self): return self.code - def save(self, force_insert=False, force_update=False, using=None): - raise MissingUserError("save() method disabled, use save_by_user()") - - def save_by_user(self, user, force_insert=False, force_update=False, using=None): - "Save a collection and add a revision" - super(MediaCollection, self).save(force_insert, force_update, using) - Revision(element_type='collection', element_id=self.id, user=user).touch() - def has_mediafile(self): "Tell wether this collection has any media files attached to its items" items = self.items.all() @@ -196,11 +201,41 @@ class MediaCollection(MediaCore): return True return False + def __name_cmp(self, obj1, obj2): + return unaccent_icmp(obj1.name, obj2.name) + + def get_countries(self): + "Return the countries of the items" + countries = [] + items = self.items.all() + for item in items: + if item.location: + country = item.location.country() + if country and not country in countries: + countries.append(country) + + countries.sort(self.__name_cmp) + + return countries + + def get_ethnic_groups(self): + "Return the ethnic groups of the items" + groups = [] + items = self.items.all() + for item in items: + if item.ethnic_group and not item.ethnic_group in groups: + groups.append(item.ethnic_group) + + groups.sort(self.__name_cmp) + + return groups + class Meta(MetaCore): db_table = 'media_collections' class MediaItem(MediaCore): "Describe an item" + element_type = 'item' PUBLIC_ACCESS_CHOICES = (('none', 'none'), ('metadata', 'metadata'), ('full', 'full')) collection = models.ForeignKey('MediaCollection', related_name="items") @@ -246,16 +281,9 @@ class MediaItem(MediaCore): return self.code return self.old_code - def save(self, force_insert=False, force_update=False): - raise MissingUserError("save() method disabled, use save_by_user()") - - def save_by_user(self, user, force_insert=False, force_update=False): - "Save an item and add a revision" - super(MediaItem, self).save(force_insert, force_update) - Revision(element_type='item', element_id=self.id, user=user).touch() - class MediaPart(MediaCore): "Describe an item part" + element_type = 'part' item = models.ForeignKey('MediaItem', related_name="parts") title = models.CharField(max_length=250) start = models.FloatField() diff --git a/telemeta/tests/model_tests.py b/telemeta/tests/model_tests.py index cc1f540c..fdd7f00f 100644 --- a/telemeta/tests/model_tests.py +++ b/telemeta/tests/model_tests.py @@ -250,6 +250,7 @@ class CollectionItemTestCase(unittest.TestCase): self.assertEquals(self.items.without_collection().count(), 0) def testCodeRequired(self): + "Test that a proper failure occur when a collection code isn't provided" c = MediaCollection() try: c.save_by_user(self.olivier) @@ -259,11 +260,18 @@ class CollectionItemTestCase(unittest.TestCase): self.fail("No exception raised") def testDomForeignKey(self): + "Test DOM foreign key embedding" doc = self.item_4.to_dom() self.assertEquals(doc.getElementsByTagName('collection')[0].getAttribute('key'), str(self.persepolis.id)) def testLocationRelation(self): + "Test location country and continent resolving" self.assertEquals(self.france, self.item_1.location.country()) self.assertEquals(self.europe, self.item_1.location.continent()) self.assertEquals(self.france, self.item_2.location.country()) self.assertEquals(self.europe, self.item_2.location.continent()) + + def testCollectionCountries(self): + "Test the MediaCollection.get_countries() method" + self.assertEquals(self.volonte.get_countries(), [self.belgique, self.france]) + diff --git a/telemeta/util/unaccent.py b/telemeta/util/unaccent.py new file mode 100644 index 00000000..7c5757d8 --- /dev/null +++ b/telemeta/util/unaccent.py @@ -0,0 +1,71 @@ +# This file by Fredrik Lundh from: +# http://effbot.org/zone/unicode-convert.htm +# http://effbot.python-hosting.com/file/stuff/sandbox/text/unaccent.py + +# use a dynamically populated translation dictionary to remove accents +# from a string + +import unicodedata, sys + +CHAR_REPLACEMENT = { + # latin-1 characters that don't have a unicode decomposition + 0xc6: u"AE", # LATIN CAPITAL LETTER AE + 0xd0: u"D", # LATIN CAPITAL LETTER ETH + 0xd8: u"OE", # LATIN CAPITAL LETTER O WITH STROKE + 0xde: u"Th", # LATIN CAPITAL LETTER THORN + 0xdf: u"ss", # LATIN SMALL LETTER SHARP S + 0xe6: u"ae", # LATIN SMALL LETTER AE + 0xf0: u"d", # LATIN SMALL LETTER ETH + 0xf8: u"oe", # LATIN SMALL LETTER O WITH STROKE + 0xfe: u"th", # LATIN SMALL LETTER THORN + } + +## +# Translation dictionary. Translation entries are added to this +# dictionary as needed. + +class UnaccentedMap(dict): + + ## + # Maps a unicode character code (the key) to a replacement code + # (either a character code or a unicode string). + + def mapchar(self, key): + ch = self.get(key) + if ch is not None: + return ch + de = unicodedata.decomposition(unichr(key)) + if de: + try: + ch = int(de.split(None, 1)[0], 16) + except (IndexError, ValueError): + ch = key + else: + ch = CHAR_REPLACEMENT.get(key, key) + self[key] = ch + return ch + + if sys.version >= "2.5": + # use __missing__ where available + __missing__ = mapchar + else: + # otherwise, use standard __getitem__ hook (this is slower, + # since it's called for each character) + __getitem__ = mapchar + + +_map = UnaccentedMap() + +def unaccent(str): + return str.translate(_map) + +def unaccent_icmp(str1, str2): + str1 = unaccent(str1).lower() + str2 = unaccent(str2).lower() + if str1 > str2: + return 1 + + if str1 < str2: + return -1 + + return 0 -- 2.39.5