From 37aa19d8924c3f40938e9e83375ab36db1ce35a1 Mon Sep 17 00:00:00 2001 From: olivier <> Date: Thu, 6 Mar 2008 19:19:32 +0000 Subject: [PATCH] model modularization ; add is_published() method for collections --- telemeta/models/__init__.py | 12 ++ telemeta/models/enum.py | 34 +++++ telemeta/models/media.py | 297 ++++++++++++++++++++++++++++++++++++ telemeta/models/query.py | 176 +++++++++++++++++++++ 4 files changed, 519 insertions(+) create mode 100644 telemeta/models/__init__.py create mode 100644 telemeta/models/enum.py create mode 100644 telemeta/models/media.py create mode 100644 telemeta/models/query.py diff --git a/telemeta/models/__init__.py b/telemeta/models/__init__.py new file mode 100644 index 00000000..e1e04a56 --- /dev/null +++ b/telemeta/models/__init__.py @@ -0,0 +1,12 @@ +# Copyright (C) 2007 Samalyse SARL +# All rights reserved. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://svn.parisson.org/telemeta/TelemetaLicense. +# +# Author: Olivier Guilyardi + +from telemeta.models.media import MediaCollection, MediaItem, MediaPart +from telemeta.models.enum import PhysicalFormat, PublishingStatus + diff --git a/telemeta/models/enum.py b/telemeta/models/enum.py new file mode 100644 index 00000000..94b3b5d1 --- /dev/null +++ b/telemeta/models/enum.py @@ -0,0 +1,34 @@ +# Copyright (C) 2007 Samalyse SARL +# All rights reserved. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://svn.parisson.org/telemeta/TelemetaLicense. +# +# Author: Olivier Guilyardi + +from django.db.models import Model, CharField + +class PhysicalFormat(Model): + "Physical support of media items" + + value = CharField(maxlength=250) + is_enumeration = True + def __str__(self): + return self.value + class Meta: + app_label = 'telemeta' + ordering = ['value'] + app_label = 'telemeta' + +class PublishingStatus(Model): + "Publishing status of media items" + value = CharField(maxlength=250) + is_enumeration = True + def __str__(self): + return self.value + class Meta: + app_label = 'telemeta' + ordering = ['value'] + verbose_name_plural = "Publishing status" + diff --git a/telemeta/models/media.py b/telemeta/models/media.py new file mode 100644 index 00000000..1970bcb1 --- /dev/null +++ b/telemeta/models/media.py @@ -0,0 +1,297 @@ +# Copyright (C) 2007 Samalyse SARL +# All rights reserved. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://svn.parisson.org/telemeta/TelemetaLicense. +# +# Author: Olivier Guilyardi + +from django.db.models import Model, CharField, FileField, \ + TextField, DecimalField, ForeignKey, DateField +from django.core.exceptions import ObjectDoesNotExist +from django.core import validators +from django.conf import settings +from xml.dom.minidom import getDOMImplementation + +import telemeta +from telemeta.core import * +from telemeta import dublincore as dc +from telemeta.models.query import MediaItemManager, MediaItemQuerySet, \ + MediaCollectionManager, MediaCollectionQuerySet + +# Regular (sub) expression for matching/validating media objects IDs +media_id_regex = r'[0-9A-Za-z._-]+' + +class MediaModel(Component): + "Represent the whole model as a component" + pass + +class MediaCore(object): + "Base class of all media objects" + + def to_dict(self): + "Return model fields as a dict of name/value pairs" + fields_dict = {} + for field in self._meta.fields: + fields_dict[field.name] = getattr(self, field.name) + return fields_dict + + def to_list(self): + "Return model fields as a list" + fields_list = [] + for field in self._meta.fields: + fields_list.append({'name': field.name, 'value': getattr(self, field.name)}) + return fields_list + + def get_dom_element_name(cls): + "Convert the class name to a DOM element name" + clsname = cls.__name__ + return clsname[0].lower() + clsname[1:] + get_dom_element_name = classmethod(get_dom_element_name) + + def to_dom(self): + "Return the DOM representation of this media object" + impl = getDOMImplementation() + root = self.get_dom_element_name() + doc = impl.createDocument(None, root, None) + top = doc.documentElement + top.setAttribute("id", self.id) + fields = self.to_dict() + for name, value in fields.iteritems(): + element = doc.createElement(name) + value = unicode(str(value), "utf-8") + element.appendChild(doc.createTextNode(value)) + top.appendChild(element) + return doc + +class MediaCollection(Model, MediaCore): + "Group related media items" + + id_regex = media_id_regex + id_validator = validators.MatchesRegularExpression('^' + id_regex + '$') + + publisher_reference = CharField(maxlength=250, blank=True) + physical_format = CharField(maxlength=250, blank=True) + id = CharField(maxlength=250, primary_key=True, + verbose_name='identifier', validator_list=[id_validator]) + title = CharField(maxlength=250) + native_title = CharField(maxlength=250, blank=True) + physical_items_num = CharField(maxlength=250, blank=True) + publishing_status = CharField(maxlength=250, blank=True) + is_original = CharField(maxlength=250, blank=True) + is_full_copy = CharField(maxlength=250, blank=True) + copied_from = ForeignKey('self', null=True, blank=True) + creator = CharField(maxlength=250) + booklet_writer = CharField(maxlength=250, blank=True) + booklet_description = TextField(blank=True) + collector = CharField(maxlength=250, blank=True) + publisher = CharField(maxlength=250, blank=True) + date_published = CharField(maxlength=250, blank=True) + publisher_collection= CharField(maxlength=250, blank=True) + publisher_serial_id = CharField(maxlength=250, blank=True) + ref_biblio = TextField(blank=True) + acquisition_mode = CharField(maxlength=250, blank=True) + comment = TextField(blank=True) + record_author = CharField(maxlength=250, blank=True) + record_writer = CharField(maxlength=250, blank=True) + rights = CharField(maxlength=250, blank=True) + annee_enr = CharField(maxlength=250, blank=True) + terrain_ou_autre = CharField(maxlength=250, blank=True) + duree_approx = CharField(maxlength=250, blank=True) + tri_dibm = CharField(maxlength=250, blank=True) + travail = CharField(maxlength=250, blank=True) + compil_face_plage = TextField(blank=True) + deposant_cnrs = CharField(maxlength=250, blank=True) + fiches = CharField(maxlength=250, blank=True) + a_informer = CharField(maxlength=250, blank=True) + numerisation = CharField(maxlength=250, blank=True) + champ36 = CharField(maxlength=250, blank=True) + + objects = MediaCollectionManager() + + def to_dublincore(self): + "Express this collection as a Dublin Core resource" + if (self.date_published): + date = self.date_published + else: + date = self.annee_enr + + if (self.copied_from): + copied_from = self.copied_from.id + else: + copied_from = '' + + resource = dc.Resource( + dc.Element('identifier','id', self.id), + dc.Element('type', value='Collection'), + dc.Element('title', 'title', self.title), + dc.Element('title', 'native_title', self.native_title), + dc.Element('creator', 'creator', self.creator), + dc.Element('relation', 'copied_from', copied_from, 'isVersionOf'), + dc.Element('contributor', 'booklet_writer', self.booklet_writer), + dc.Element('contributor', 'collector', self.collector), + dc.Element('publisher', 'publisher', self.publisher), + dc.Element('date', value=date), + dc.Element('rights', 'rights', self.rights), + ) + return resource + + def has_mediafile(self): + "Tell wether this collection has any media files attached to its items" + items = self.items.all() + for item in items: + if item.file: + return True + return False + + def is_published(self): + if len(self.publisher_reference) < 3: + return True + if self.publisher_reference[:3] == 'BM.': + return False + return True + + def __str__(self): + #return self.title + return self.id + + class Meta: + app_label = 'telemeta' + ordering = ['title'] + db_table = 'telemeta_collection' + + class Admin: + pass + +class MediaItem(Model, MediaCore): + "Describe an item with metadata" + + id_regex = media_id_regex + id_validator = validators.MatchesRegularExpression('^' + id_regex + '$') + + ref = CharField(maxlength=250, blank=True) + format = CharField(maxlength=250, blank=True) + collection = ForeignKey(MediaCollection, related_name="items") + face_plage = CharField(maxlength=250, blank=True) + id = CharField(maxlength=250, primary_key=True, + verbose_name='identifier', validator_list=[id_validator]) + duree = CharField(maxlength=250, blank=True) + dates_enregistr = CharField(maxlength=250, blank=True) + etat = CharField(maxlength=250, blank=True) + region_village = CharField(maxlength=250, blank=True) + ethnie_grsocial = CharField(maxlength=250, blank=True) + titre_support = CharField(maxlength=250, blank=True) + _title = CharField(maxlength=250, db_column='title', blank=True) + transcrip_trad = CharField(maxlength=250, blank=True) + auteur = CharField(maxlength=250, blank=True) + form_genr_style = CharField(maxlength=250, blank=True) + struct_modale = CharField(maxlength=250, blank=True) + struct_rythm = CharField(maxlength=250, blank=True) + comm_fonctusage = TextField(blank=True) + documentation = TextField(maxlength=250, blank=True) + remarques = TextField(maxlength=250, blank=True) + moda_execut = CharField(maxlength=250, blank=True) + copie_de = CharField(maxlength=250, blank=True) + enregistre_par = CharField(maxlength=250, blank=True) + aire_geo_cult = CharField(maxlength=250, blank=True) + annee_enreg = CharField(maxlength=250, blank=True) + formstyl_generi = CharField(maxlength=250, blank=True) + choixcollecteur = CharField(maxlength=250, blank=True) + repere_bande = CharField(maxlength=250, blank=True) + nroband_nropiec = CharField(maxlength=250, blank=True) + continent = CharField(maxlength=250, blank=True) + file = FileField(upload_to='items/%Y/%m/%d', blank=True) + + objects = MediaItemManager() + + def _get_title(self): + if self._title == "": + try: + title = self.collection.title + " - Face/Plage: " \ + + self.face_plage + except ObjectDoesNotExist: + title = self.id + else: + title = self._title + + return title + title = property(_get_title) + + def to_dublincore(self): + "Express this item as a Dublin Core resource" + if self.auteur: + creator = self.auteur + else: + creator = self.collection.creator + + resource = dc.Resource( + dc.Element('identifier','id', self.id), + dc.Element('type', value='Sound'), + dc.Element('relation', 'collection', self.collection.id, 'isPartOf'), + dc.Element('title', 'title', self.title), + dc.Element('creator', value=creator), + dc.Element('publisher', value=self.collection.publisher), + dc.Element('coverage', value=self.etat), + ) + return resource + + def get_duration(self): + "Tell the length in seconds of this item media data" + if self.file: + import wave + media = wave.open(settings.MEDIA_ROOT + "/" + self.file, "rb") + duration = media.getnframes() / media.getframerate() + media.close() + else: + duration = 0 + + return duration + + def __str__(self): + return self.title + + class Admin: + pass + + class Meta: + app_label = 'telemeta' + ordering = ['_title'] + db_table = 'telemeta_item' + + +class MediaPart(Model, MediaCore): + "Describe the part of a media item" + + contributor = CharField(maxlength=250, blank=True) + coverage = CharField(maxlength=250, blank=True) + creator = CharField(maxlength=250, blank=True) + date = DateField() + description = CharField(maxlength=250, blank=True) + format = CharField(maxlength=250, blank=True) + identifier = CharField(maxlength=250, blank=True) + language = CharField(maxlength=250, blank=True) + publisher = CharField(maxlength=250, blank=True) + rights = CharField(maxlength=250, blank=True) + source = CharField(maxlength=250, blank=True) + subject = CharField(maxlength=250, blank=True) + title = CharField(maxlength=250, blank=True) + media_item = ForeignKey(MediaItem) + #media_item.dublin_core = 'relation' + parent = ForeignKey('self', null=True, related_name='children') + #media_item.dublin_core = 'relation' + start = DecimalField(max_digits=11, decimal_places=3) + end = DecimalField(max_digits=11, decimal_places=3) + #comment = TextField(blank=True) + + def __str__(self): + return self.title + + class Meta: + app_label = 'telemeta' + ordering = ['title'] + db_table = 'telemeta_part' + + class Admin: + pass + diff --git a/telemeta/models/query.py b/telemeta/models/query.py new file mode 100644 index 00000000..915b5165 --- /dev/null +++ b/telemeta/models/query.py @@ -0,0 +1,176 @@ +# Copyright (C) 2007 Samalyse SARL +# All rights reserved. +# +# This software is licensed as described in the file COPYING, which +# you should have received as part of this distribution. The terms +# are also available at http://svn.parisson.org/telemeta/TelemetaLicense. +# +# Author: Olivier Guilyardi + +from django.db.models import Manager, Q +from django.db.models.query import QuerySet + +class CoreQuerySet(QuerySet): + "Base class for all query sets" + + def none(self): # redundant with none() in recent Django svn + "Return an empty result set" + return self.extra(where = ["0 = 1"]) + +class CoreManager(Manager): + "Base class for all models managers" + + def none(self, *args, **kwargs): + return self.get_query_set().none(*args, **kwargs) + +class MediaCollectionQuerySet(CoreQuerySet): + + def quick_search(self, pattern): + "Perform a quick search on id, title and creator name" + return self.filter( + Q(id__icontains=pattern) | + Q(title__icontains=pattern) | + Q(creator__icontains=pattern) + ) + + def by_country(self, country): + "Find collections by country" + return self.extra(where = ["id IN (SELECT collection_id " + "FROM telemeta_item WHERE etat = %s)"], + params=[country]); + + def by_continent(self, continent): + "Find collections by continent" + return self.extra(where = ["id IN (SELECT collection_id " + "FROM telemeta_item WHERE continent = %s)"], + params=[continent]); + + def by_recording_date(self, pattern): + return self.filter(annee_enr__icontains=pattern) + + def by_publish_date(self, pattern): + return self.filter(date_published__icontains=pattern) + +class MediaCollectionManager(CoreManager): + "Manage collection queries" + + def get_query_set(self): + return MediaCollectionQuerySet(self.model) + + def quick_search(self, *args, **kwargs): + return self.get_query_set().quick_search(*args, **kwargs) + + def by_country(self, *args, **kwargs): + return self.get_query_set().by_country(*args, **kwargs) + + def by_continent(self, *args, **kwargs): + return self.get_query_set().by_continent(*args, **kwargs) + + def by_recording_date(self, *args, **kwargs): + return self.get_query_set().by_recording_date(*args, **kwargs) + + def by_publish_date(self, *args, **kwargs): + return self.get_query_set().by_publish_date(*args, **kwargs) + + def stat_continents(self, order_by='num'): + "Return the number of collections by continents and countries as a tree" + from django.db import connection + cursor = connection.cursor() + if order_by == 'num': + order_by = 'items_num DESC' + else: + order_by = 'etat' + cursor.execute("SELECT continent, etat, count(*) AS items_num " + "FROM telemeta_collection INNER JOIN telemeta_item " + "ON telemeta_collection.id = telemeta_item.collection_id " + "WHERE (continent IN " + " ('EUROPE', 'OCEANIE', 'ASIE', 'AMERIQUE', 'AFRIQUE')) " + "AND etat <> '' " + "GROUP BY etat ORDER BY continent, " + order_by) + result_set = cursor.fetchall() + stat = {} + for continent, country, count in result_set: + if stat.has_key(continent): + stat[continent].append({'name':country, 'count':count}) + else: + stat[continent] = [{'name':country, 'count':count}] + + keys = stat.keys() + keys.sort() + ordered = [{'name': k, 'countries': stat[k]} for k in keys] + return ordered + + def list_countries(self): + "Return a 2D list of all countries with continents" + + from django.db import connection + cursor = connection.cursor() + + cursor.execute("SELECT continent, etat FROM telemeta_item " + "GROUP BY continent, etat ORDER BY REPLACE(etat, '\"', '')"); + return cursor.fetchall() + + def list_continents(self): + "Return a list of all continents" + + from django.db import connection + cursor = connection.cursor() + + cursor.execute("SELECT DISTINCT(continent) FROM telemeta_item ORDER BY continent") + result_set = cursor.fetchall() + result = [] + for a, in result_set: + if a != '' and a != 'N': # CREM fix + result.append(a) + + return result + +class MediaItemQuerySet(CoreQuerySet): + + def quick_search(self, pattern): + "Perform a quick search on id and title" + return self.filter( + Q(id__icontains=pattern) | + Q(_title__icontains=pattern) + ) + + def without_collection(self): + "Find items which do not belong to any collection" + return self.extra( + where = ["collection_id NOT IN (SELECT id FROM telemeta_collection)"]); + + def by_recording_date(self, pattern): + return self.filter(Q(dates_enregistr__icontains=pattern) + | Q(annee_enreg__icontains=pattern)) + +class MediaItemManager(CoreManager): + "Manage media items queries" + + def get_query_set(self): + return MediaItemQuerySet(self.model) + + def quick_search(self, *args, **kwargs): + return self.get_query_set().quick_search(*args, **kwargs) + + def without_collection(self, *args, **kwargs): + return self.get_query_set().without_collection(*args, **kwargs) + + def by_recording_date(self, *args, **kwargs): + return self.get_query_set().by_recording_date(*args, **kwargs) + + def list_ethnic_groups(self): + "Return a list of all ethnic groups" + + from django.db import connection + cursor = connection.cursor() + + cursor.execute("SELECT DISTINCT(ethnie_grsocial) FROM telemeta_item " + "ORDER BY REPLACE(ethnie_grsocial, '\\'', '')") + result_set = cursor.fetchall() + result = [] + for a, in result_set: + if a != '/' and a != '': # CREM fix + result.append(a) + + return result + -- 2.39.5