source: telemeta/models/crem.py @ 6d4e11a

cremcrem2devdev2diademsdj1.6feature/breadcrumbsfeature/ts-0.5feature/ts-0.5.4feature/writecacheformagenericinstru_searchlamlam2mapsv3mergenlivemultiproductionrelease/1.4.4sabiodsecurityserversocialstoragetelecastertestvideo
Last change on this file since 6d4e11a was 6d4e11a, checked in by olivier <>, 5 years ago

fix crem models DOM export and add support for embedding foreign keys

  • Property mode set to 100755
File size: 21.3 KB
Line 
1# -*- coding: utf-8 -*-
2# Copyright (C) 2007-2010 Samalyse SARL
3
4# This software is a computer program whose purpose is to backup, analyse,
5# transcode and stream any audio content with its metadata over a web frontend.
6
7# This software is governed by the CeCILL  license under French law and
8# abiding by the rules of distribution of free software.  You can  use,
9# modify and/ or redistribute the software under the terms of the CeCILL
10# license as circulated by CEA, CNRS and INRIA at the following URL
11# "http://www.cecill.info".
12
13# As a counterpart to the access to the source code and  rights to copy,
14# modify and redistribute granted by the license, users are provided only
15# with a limited warranty  and the software's author,  the holder of the
16# economic rights,  and the successive licensors  have only  limited
17# liability.
18
19# In this respect, the user's attention is drawn to the risks associated
20# with loading,  using,  modifying and/or developing or reproducing the
21# software by the user in light of its specific status of free software,
22# that may mean  that it is complicated to manipulate,  and  that  also
23# therefore means  that it is reserved for developers  and  experienced
24# professionals having in-depth computer knowledge. Users are therefore
25# encouraged to load and test the software's suitability as regards their
26# requirements in conditions enabling the security of their systems and/or
27# data to be ensured and,  more generally, to use and operate it in the
28# same conditions as regards security.
29
30# The fact that you are presently reading this means that you have had
31# knowledge of the CeCILL license and that you accept its terms.
32#
33# Authors: Olivier Guilyardi <olivier@samalyse.com>
34#          David LIPSZYC <davidlipszyc@gmail.com>
35
36from django.db import models
37import cremquery as query
38from xml.dom.minidom import getDOMImplementation
39
40class ModelCore(models.Model):
41
42    @classmethod
43    def required_fields(cls):
44        required = []
45        for field in cls._meta.fields:
46            if (field != cls._meta.pk and field.default == models.fields.NOT_PROVIDED and
47                    not field.null and not getattr(field, 'auto_now_add', False)):
48                required.append(field)
49        return required
50
51    def save(self, force_insert=False, force_update=False, using=None):
52        required = self.required_fields()
53        for field in required:
54            if not getattr(self, field.name):
55                raise RequiredFieldError(self, field)
56        super(ModelCore, self).save(force_insert, force_update, using)
57
58    class Meta:
59        abstract = True
60
61class MediaCore(ModelCore):
62    "Base class of all media objects"
63
64    def to_dict(self): 
65        "Return model fields as a dict of name/value pairs"
66        fields_dict = {}
67        for field in self._meta.fields:
68            fields_dict[field.name] = getattr(self, field.name)
69        return fields_dict
70
71    def to_list(self): 
72        "Return model fields as a list"
73        fields_list = []
74        for field in self._meta.fields:
75            fields_list.append({'name': field.name, 'value': getattr(self, field.name)})
76        return fields_list
77
78    @classmethod
79    def get_dom_name(cls):
80        "Convert the class name to a DOM element name"
81        clsname = cls.__name__
82        return clsname[0].lower() + clsname[1:]
83
84    @staticmethod
85    def get_dom_field_name(field_name):
86        "Convert the class name to a DOM element name"
87        tokens = field_name.split('_')
88        name = tokens[0]
89        for t in tokens[1:]:
90            name += t[0].upper() + t[1:]
91        return name
92
93    def to_dom(self):
94        "Return the DOM representation of this media object"
95        impl = getDOMImplementation()
96        root = self.get_dom_name()
97        doc = impl.createDocument(None, root, None)
98        top = doc.documentElement
99        top.setAttribute("id", str(self.id))
100        fields = self.to_dict()
101        for name, value in fields.iteritems():
102            element = doc.createElement(self.get_dom_field_name(name))
103            if isinstance(value, models.Model):
104                element.setAttribute('key', str(value.pk))
105            value = unicode(value)
106            element.appendChild(doc.createTextNode(value))
107            top.appendChild(element)
108        return doc
109   
110    def is_well_formed_id(cls, value):
111        "Check if the media id is well formed"
112        regex = re.compile(r"^" + media_id_regex + r"$")
113        if regex.match(value):
114            return True 
115        else:
116            return False
117    is_well_formed_id = classmethod(is_well_formed_id)
118
119    class Meta:
120        abstract = True
121
122class MetaCore:
123    app_label = 'telemeta'
124
125class MediaCollection(MediaCore):
126    "Describe a collection of items"
127    PUBLIC_ACCESS_CHOICES = (('none', 'none'), ('metadata', 'metadata'), ('metadata', 'full'))
128
129    reference             = models.CharField(unique=True, max_length=250,
130                                             null=True)
131    physical_format       = models.ForeignKey('PhysicalFormat', related_name="collections", 
132                                              null=True, default=None)
133    old_code              = models.CharField(unique=True, max_length=250, null=True)
134    code                  = models.CharField(unique=True, max_length=250)
135    title                 = models.CharField(max_length=250)
136    alt_title             = models.CharField(max_length=250, default="")
137    physical_items_num    = models.IntegerField(default=0)
138    publishing_status     = models.ForeignKey('PublishingStatus', related_name="collections",
139                                              null=True)
140    creator               = models.CharField(max_length=250, default="")
141    booklet_author        = models.CharField(max_length=250, default="")
142    booklet_description   = models.TextField(default="")
143    collector             = models.CharField(max_length=250, default="")
144    collector_is_creator  = models.BooleanField(default="")
145    publisher             = models.ForeignKey('Publisher', related_name="collections",
146                                              null=True)     
147    year_published        = models.IntegerField(default=0)
148    publisher_collection  = models.ForeignKey('PublisherCollection', related_name="collections",
149                                              null=True)
150    publisher_serial      = models.CharField(max_length=250, default="")
151    external_references   = models.TextField(default="")
152    acquisition_mode      = models.ForeignKey('AcquisitionMode', related_name="collections",
153                                              null=True)
154    comment               = models.TextField(default="")
155    metadata_author       = models.ForeignKey('MetadataAuthor', related_name="collections",
156                                              null=True)
157    metadata_writer       = models.ForeignKey('MetadataWriter', related_name="collections",
158                                              null=True)
159    legal_rights          = models.ForeignKey('LegalRight', related_name="collections",
160                                              null=True)
161    alt_ids               = models.CharField(max_length=250, default="")
162    recorded_from_year    = models.IntegerField(default=0)
163    recorded_to_year      = models.IntegerField(default=0)
164    recording_context     = models.ForeignKey('RecordingContext', related_name="collections",
165                                              null=True)
166    approx_duration       = models.TimeField(default='00:00')
167    doctype_code          = models.IntegerField(default=0)
168    travail               = models.CharField(max_length=250, default="")
169    state                 = models.TextField(default="")
170    cnrs_contributor      = models.CharField(max_length=250, default="")
171    items_done            = models.CharField(max_length=250, default="")
172    a_informer_07_03      = models.CharField(max_length=250, default="")
173    ad_conversion         = models.ForeignKey('AdConversion', related_name='collections',
174                                              null=True)
175    public_access         = models.CharField(choices=PUBLIC_ACCESS_CHOICES, max_length=16, default="metadata")
176
177    objects               = query.MediaCollectionManager()
178
179    def __unicode__(self):
180        return self.code
181
182    def save(self, force_insert=False, force_update=False, using=None):
183        raise MissingUserError("save() method disabled, use save_by_user()")
184
185    def save_by_user(self, user, force_insert=False, force_update=False, using=None):
186        "Save a collection and add a revision"
187        super(MediaCollection, self).save(force_insert, force_update, using)
188        Revision(element_type='collection', element_id=self.id, user=user).touch()   
189
190    class Meta(MetaCore):
191        db_table = 'media_collections'
192
193class MediaItem(MediaCore):
194    "Describe an item"
195    PUBLIC_ACCESS_CHOICES = (('none', 'none'), ('metadata', 'metadata'), ('full', 'full'))
196
197    collection            = models.ForeignKey('MediaCollection', related_name="items")
198    track                 = models.CharField(max_length=250, default="")
199    old_code              = models.CharField(unique=True, max_length=250, null=True)
200    code                  = models.CharField(unique=True, max_length=250, null=True)
201    approx_duration       = models.TimeField(default='00:00')
202    recorded_from_date    = models.DateField(default=0)
203    recorded_to_date      = models.DateField(default=0)
204    location              = models.ForeignKey('Location', related_name="items",
205                                              db_column='location_name', null=True, default="")
206    location_comment      = models.CharField(max_length=250, default="")
207    ethnic_group          = models.ForeignKey('EthnicGroup', related_name="items",
208                                              null=True)
209    title                 = models.CharField(max_length=250)
210    alt_title             = models.CharField(max_length=250, default="")
211    author                = models.CharField(max_length=250, default="")
212    vernacular_style      = models.ForeignKey('VernacularStyle', related_name="items",
213                                              null=True)
214    context_comment       = models.TextField(default="")
215    external_references   = models.TextField(default="")
216    moda_execut           = models.CharField(max_length=250, default="")
217    copied_from_item      = models.ForeignKey('self', related_name="copies",
218                                              null=True)
219    collector             = models.CharField(max_length=250, default="")
220    cultural_area         = models.CharField(max_length=250, default="")
221    generic_style         = models.ForeignKey('GenericStyle', related_name="items",
222                                              null=True)
223    collector_selection   = models.CharField(max_length=250, default="")
224    creator_reference     = models.CharField(max_length=250, default="")
225    comment               = models.TextField(default="")
226    filename              = models.CharField(max_length=250, default="")
227    public_access         = models.CharField(choices=PUBLIC_ACCESS_CHOICES, 
228                                             max_length=16, default="metadata")
229
230    objects               = query.MediaItemManager()
231
232    class Meta(MetaCore):
233        db_table = 'media_items'
234
235    def __unicode__(self):
236        if self.code:
237            return self.code
238        return self.old_code
239
240    def save(self, force_insert=False, force_update=False):
241        raise MissingUserError("save() method disabled, use save_by_user()")
242
243    def save_by_user(self, user, force_insert=False, force_update=False):
244        "Save an item and add a revision"
245        super(MediaItem, self).save(force_insert, force_update)
246        Revision(element_type='item', element_id=self.id, user=user).touch()   
247
248class MediaPart(MediaCore):
249    "Describe an item part"
250    item  = models.ForeignKey('MediaItem', related_name="parts")
251    title = models.CharField(max_length=250)
252    start = models.FloatField()
253    end   = models.FloatField()
254   
255    class Meta(MetaCore):
256        db_table = 'media_parts'
257
258    def __unicode__(self):
259        return self.title
260
261class PhysicalFormat(ModelCore):
262    "Collection physical format"
263    value = models.CharField(max_length=250, unique=True)
264   
265    class Meta(MetaCore):
266        db_table = 'physical_formats'
267
268class PublishingStatus(ModelCore):
269    "Collection publishing status"
270    value = models.CharField(max_length=250, unique=True)
271
272    class Meta(MetaCore):
273        db_table = 'publishing_status'
274
275class AcquisitionMode(ModelCore):
276    "Mode of acquisition of the collection"
277    value = models.CharField(max_length=250, unique=True)
278
279    class Meta(MetaCore):
280        db_table = 'acquisition_modes'
281
282class MetadataAuthor(ModelCore):
283    "Collection metadata author"
284    value = models.CharField(max_length=250, unique=True)
285
286    class Meta(MetaCore):
287        db_table = 'metadata_authors'
288
289class MetadataWriter(ModelCore): 
290    "Collection metadata writer"
291    value = models.CharField(max_length=250, unique=True)
292
293    class Meta(MetaCore):
294        db_table = 'metadata_writers'
295
296class LegalRight(ModelCore):
297    "Collection legal rights" 
298    value = models.CharField(max_length=250, unique=True)
299
300    class Meta(MetaCore):
301        db_table = 'legal_rights'
302
303class RecordingContext(ModelCore):
304    "Collection recording context"
305    value = models.CharField(max_length=250, unique=True)
306
307    class Meta(MetaCore):
308        db_table = 'recording_contexts'
309
310class AdConversion(ModelCore):
311    "Collection digital to analog conversion status"
312    value = models.CharField(max_length=250, unique=True)
313
314    class Meta(MetaCore):
315        db_table = 'ad_conversions'
316
317class VernacularStyle(ModelCore):
318    "Item vernacular style"
319    value = models.CharField(max_length=250, unique=True)
320
321    class Meta(MetaCore):
322        db_table = 'vernacular_styles'
323
324class GenericStyle(ModelCore):
325    "Item generic style"
326    value = models.CharField(max_length=250, unique=True)
327
328    class Meta(MetaCore):
329        db_table = 'generic_styles'
330
331class Instrument(ModelCore):
332    "Instrument used in the item"
333    name    = models.CharField(max_length=250)
334
335    class Meta(MetaCore):
336        db_table = 'instruments'
337
338class InstrumentAlias(ModelCore):
339    "Instrument other name"
340    name = models.CharField(max_length=250)
341
342    class Meta(MetaCore):
343        db_table = 'instrument_aliases'
344
345class InstrumentRelation(ModelCore):
346    "Instrument family"
347    instrument        = models.ForeignKey('Instrument', related_name="parent_relation")
348    parent_instrument = models.ForeignKey('Instrument', related_name="child_relation")
349
350    class Meta(MetaCore):
351        db_table = 'instrument_relations'
352        unique_together = (('instrument', 'parent_instrument'),)
353
354class InstrumentAliasRelation(ModelCore):
355    "Instrument family other name"
356    alias      = models.ForeignKey('InstrumentAlias', related_name="other_name")
357    instrument = models.ForeignKey('InstrumentAlias', related_name="relation")
358
359    class Meta(MetaCore):
360        db_table = 'instrument_alias_relations'
361        unique_together = (('alias', 'instrument'),)
362
363class MediaItemPerformance(ModelCore):
364    "Item performance"
365    media_item      = models.ForeignKey('MediaItem', related_name="performances")
366    instrument      = models.ForeignKey('Instrument', related_name="performances",
367                                        null=True)
368    alias           = models.ForeignKey('InstrumentAlias', related_name="performances",
369                                        null=True)
370    instruments_num = models.CharField(max_length=250, default="")
371    musicians       = models.CharField(max_length=250, default="")
372
373    class Meta(MetaCore):
374        db_table = 'media_item_performances'
375
376class User(ModelCore):
377    "Telemeta user"
378    LEVEL_CHOICES = (('user', 'user'), ('maintainer', 'maintainer'), ('admin', 'admin'))   
379
380    username   = models.CharField(primary_key=True, max_length=64)
381    level      = models.CharField(choices=LEVEL_CHOICES, max_length=250)
382    first_name = models.CharField(max_length=250, default="")
383    last_name  = models.CharField(max_length=250, default="")
384    phone      = models.CharField(max_length=250, default="")
385    email      = models.CharField(max_length=250, default="")
386
387    class Meta(MetaCore):
388        db_table = 'users'
389
390class Playlist(ModelCore):
391    "Item or collection playlist"
392    owner_username = models.ForeignKey('User', related_name="playlists", db_column="owner_username") 
393    name           = models.CharField(max_length=250)
394
395    class Meta(MetaCore):
396        db_table = 'playlists'
397
398class PlaylistResource(ModelCore):
399    "Playlist components"
400    RESOURCE_TYPE_CHOICES = (('item', 'item'), ('collection', 'collection'))
401
402    playlist              = models.ForeignKey('Playlist', related_name="resources")
403    resource_type         = models.CharField(choices=RESOURCE_TYPE_CHOICES, max_length=250)
404    resource              = models.IntegerField()
405
406    class Meta(MetaCore):
407        db_table = 'playlist_resources'
408
409class Location(ModelCore):
410    "Item location"
411    TYPE_CHOICES     = (('country', 'country'), ('continent', 'continent'), ('other', 'other'))
412
413    name             = models.CharField(primary_key=True, max_length=150)
414    type             = models.CharField(choices=TYPE_CHOICES, max_length=16)
415    complete_type    = models.ForeignKey('LocationType', related_name="types")
416    current_name     = models.ForeignKey('self', related_name="past_names", 
417                                         db_column="current_name", null=True) 
418    is_authoritative = models.BooleanField(default=0)
419
420    class Meta(MetaCore):
421        db_table = 'locations'
422
423    def __unicode__(self):
424        return self.name
425
426class LocationType(ModelCore):
427    "Location type of an item location"
428    id   = models.CharField(max_length=64, primary_key=True)
429    name = models.CharField(max_length=150)
430
431    class Meta(MetaCore):
432        db_table = 'location_types'
433
434class LocationAlias(ModelCore):
435    "Location other name"
436    location_name    = models.ForeignKey('Location', related_name="aliases",
437                                          db_column="location_name", max_length=150)
438    alias            = models.CharField(max_length=150)
439    is_authoritative = models.BooleanField(default=0)
440
441    class Meta(MetaCore):
442        db_table = 'location_aliases'
443        unique_together = (('location_name', 'alias'),)
444   
445class LocationRelation(ModelCore):
446    "Location family"
447    location_name        = models.ForeignKey('Location', related_name="parent_relations",
448                                              db_column="location_name", max_length=150)
449    parent_location_name = models.ForeignKey('Location', related_name="child_relations",
450                                              db_column="parent_location_name", null=True, max_length=150)
451    is_authoritative     = models.BooleanField()
452
453    class Meta(MetaCore):
454        db_table = 'location_relations'
455   
456class ContextKeyword(ModelCore):
457    "Keyword"
458    value = models.CharField(max_length=250)
459
460    class Meta(MetaCore):
461        db_table = 'context_keywords'
462
463class MediaItemKeyword(ModelCore):
464    "Item keyword"
465    item    = models.ForeignKey('MediaItem')
466    keyword = models.ForeignKey('ContextKeyword')
467
468    class Meta(MetaCore):
469        db_table = 'media_item_keywords'
470        unique_together = (('item', 'keyword'),)
471
472class Publisher(ModelCore): 
473    "Collection publisher"
474    value = models.CharField(max_length=250, unique=True)
475
476    class Meta(MetaCore):
477        db_table = 'publishers'
478
479class PublisherCollection(ModelCore):
480    "Collection which belongs to publisher"
481    publisher = models.ForeignKey('Publisher', related_name="publisher_collections")
482    value     = models.CharField(max_length=250)
483
484    class Meta(MetaCore):
485        db_table = 'publisher_collections'
486
487class Revision(ModelCore):
488    "Revision made by user"
489    ELEMENT_TYPE_CHOICES = (('collection', 'collection'), ('item', 'item'), ('part', 'part'))
490    CHANGE_TYPE_CHOICES  = (('import', 'import'), ('create', 'create'), ('update', 'update'), ('delete','delete'))
491
492    element_type         = models.CharField(choices=ELEMENT_TYPE_CHOICES, max_length=16)
493    element_id           = models.IntegerField()
494    change_type          = models.CharField(choices=CHANGE_TYPE_CHOICES, max_length=16)
495    time                 = models.DateTimeField(auto_now_add=True)
496    user                 = models.ForeignKey('User', db_column='username', related_name="revisions")
497   
498    def touch(self):   
499        "Create or update a revision"
500        q = Revision.objects.filter(element_type=self.element_type, element_id=self.element_id)
501        if q.count():
502            self.change_type = 'update'
503        else:
504            self.change_type = 'create'
505        self.save()
506
507    class Meta(MetaCore):
508        db_table = 'revisions'
509   
510class EthnicGroup(ModelCore):
511    "Item ethnic group"
512    name = models.CharField(max_length=250)
513
514    class Meta(MetaCore):
515        db_table = 'ethnic_groups'
516
517    def __unicode__(self):
518        return self.name
519
520class EthnicGroupAlias(ModelCore):
521    "Item ethnic group other name" 
522    ethnic_group = models.ForeignKey('EthnicGroup', related_name="aliases")
523    name         = models.CharField(max_length=250)
524
525    class Meta(MetaCore):
526        db_table = 'ethnic_group_aliases'
527
528
529class MissingUserError(Exception):
530    pass
531
532class RequiredFieldError(Exception):
533    def __init__(self, model, field):
534        self.model = model
535        self.field = field
536        super(Exception, self).__init__('%s.%s is required' % (model._meta.object_name, field.name))
Note: See TracBrowser for help on using the repository browser.