# -*- coding: utf-8 -*-
-from telemeta.models.media import *
+from telemeta.models.fonds import *
+from telemeta.models.corpus import *
+from telemeta.models.collection import *
+from telemeta.models.item import *
from telemeta.models.instrument import *
from telemeta.models.location import *
from telemeta.models.language import *
from telemeta.models.system import *
+from telemeta.models.format import *
from django.contrib import admin
from django.contrib.auth.models import User
from django.contrib.auth.admin import UserAdmin
# Author: Olivier Guilyardi <olivier@samalyse.com>
# Guillaume Pellerin <yomguy@parisson.com>
-from media import *
+
+from identifier import *
from location import *
from instrument import *
from enum import *
from dublincore import *
from language import *
from format import *
-
+from identifier import *
+from fonds import *
+from corpus import *
+from collection import *
+from item import *
+from resource import *
+from playlist import *
--- /dev/null
+# -*- coding: utf-8 -*-
+# Copyright (C) 2010 Samalyse SARL
+# Copyright (C) 2010-2014 Parisson SARL
+
+# This software is a computer program whose purpose is to backup, analyse,
+# transcode and stream any audio content with its metadata over a web frontend.
+
+# This software is governed by the CeCILL license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+
+# As a counterpart to the access to the source code and rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using, modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL license and that you accept its terms.
+#
+# Authors: Olivier Guilyardi <olivier@samalyse.com>
+# David LIPSZYC <davidlipszyc@gmail.com>
+# Guillaume Pellerin <yomguy@parisson.com>
+
+
+from __future__ import division
+from django.utils.translation import ugettext_lazy as _
+from telemeta.models.core import *
+from telemeta.models.query import *
+from telemeta.models.identifier import *
+from telemeta.models.resource import *
+
+# Special code regex of collections for the branch
+collection_published_code_regex = getattr(settings, 'COLLECTION_PUBLISHED_CODE_REGEX', '[A-Za-z0-9._-]*')
+collection_unpublished_code_regex = getattr(settings, 'COLLECTION_UNPUBLISHED_CODE_REGEX', '[A-Za-z0-9._-]*')
+
+# CREM
+#collection_published_code_regex = 'CNRSMH_E_[0-9]{4}(?:_[0-9]{3}){2}'
+#collection_unpublished_code_regex = 'CNRSMH_I_[0-9]{4}_[0-9]{3}'
+
+collection_code_regex = '(?:%s|%s)' % (collection_published_code_regex,
+ collection_unpublished_code_regex)
+
+
+class MediaCollection(MediaResource):
+ "Describe a collection of items"
+
+ element_type = 'collection'
+
+ def is_valid_collection_code(value):
+ "Check if the collection code is well formed"
+ regex = '^' + collection_code_regex + '$'
+ if not re.match(regex, value):
+ raise ValidationError(u'%s is not a valid collection code' % value)
+
+ # General informations
+ title = CharField(_('title'), required=True)
+ alt_title = CharField(_('original title / translation'))
+ creator = CharField(_('depositor / contributor'), help_text=_('First name, Last name ; First name, Last name'))
+ description = TextField(_('description'))
+ recording_context = WeakForeignKey('RecordingContext', related_name="collections", verbose_name=_('recording context'))
+ recorded_from_year = IntegerField(_('recording year (from)'), help_text=_('YYYY'))
+ recorded_to_year = IntegerField(_('recording year (until)'), help_text=_('YYYY'))
+ year_published = IntegerField(_('year published'), help_text=_('YYYY'))
+ public_access = CharField(_('access type'), choices=PUBLIC_ACCESS_CHOICES, max_length=16, default="metadata")
+
+ # Geographic and cultural informations
+ # See "countries" and "ethnic_groups" methods below
+
+ # Legal notices
+ collector = CharField(_('recordist'), help_text=_('First name, Last name ; First name, Last name'))
+ publisher = WeakForeignKey('Publisher', related_name="collections", verbose_name=_('publisher'))
+ publisher_collection = WeakForeignKey('PublisherCollection', related_name="collections", verbose_name=_('publisher collection'))
+ publisher_serial = CharField(_('publisher serial number'))
+ booklet_author = CharField(_('booklet author'), blank=True)
+ reference = CharField(_('publisher reference'))
+ external_references = TextField(_('bibliographic references'))
+
+ auto_period_access = BooleanField(_('automatic access after a rolling period'), default=True)
+ legal_rights = WeakForeignKey('LegalRight', related_name="collections", verbose_name=_('legal rights'))
+
+ # Archiving data
+ code = CharField(_('code'), unique=True, required=True, validators=[is_valid_collection_code])
+ old_code = CharField(_('old code'), unique=False, null=True, blank=True)
+ acquisition_mode = WeakForeignKey('AcquisitionMode', related_name="collections", verbose_name=_('mode of acquisition'))
+ cnrs_contributor = CharField(_('CNRS depositor'))
+ copy_type = WeakForeignKey('CopyType', related_name="collections", verbose_name=_('copy type'))
+ metadata_author = WeakForeignKey('MetadataAuthor', related_name="collections", verbose_name=_('record author'))
+ booklet_description = TextField(_('related documentation'))
+ publishing_status = WeakForeignKey('PublishingStatus', related_name="collections", verbose_name=_('secondary edition'))
+ status = WeakForeignKey('Status', related_name="collections", verbose_name=_('collection status'))
+ alt_copies = TextField(_('copies'))
+ comment = TextField(_('comment'))
+ metadata_writer = WeakForeignKey('MetadataWriter', related_name="collections", verbose_name=_('record writer'))
+ archiver_notes = TextField(_('archiver notes'))
+ items_done = CharField(_('items finished'))
+ collector_is_creator = BooleanField(_('recordist identical to depositor'))
+ is_published = BooleanField(_('published'))
+ conservation_site = CharField(_('conservation site'))
+
+ # Technical data
+ media_type = WeakForeignKey('MediaType', related_name="collections", verbose_name=_('media type'))
+ approx_duration = DurationField(_('estimated duration'), help_text='hh:mm:ss')
+ physical_items_num = IntegerField(_('number of components (medium / piece)'))
+ original_format = WeakForeignKey('OriginalFormat', related_name="collections", verbose_name=_('original format'))
+ physical_format = WeakForeignKey('PhysicalFormat', related_name="collections", verbose_name=_('archive format'))
+ ad_conversion = WeakForeignKey('AdConversion', related_name='collections', verbose_name=_('digitization'))
+
+ # No more used old fields
+ alt_ids = CharField(_('copies (obsolete field)'))
+ travail = CharField(_('archiver notes (obsolete field)'))
+
+ # All
+ objects = MediaCollectionManager()
+
+ exclude = ['alt_ids', 'travail']
+
+ class Meta(MetaCore):
+ db_table = 'media_collections'
+ ordering = ['code']
+ verbose_name = _('collection')
+
+ def __unicode__(self):
+ return self.code
+
+ def save(self, force_insert=False, force_update=False, user=None, code=None):
+ super(MediaCollection, self).save(force_insert, force_update)
+
+ @property
+ def public_id(self):
+ return self.code
+
+ @property
+ 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 __name_cmp(self, obj1, obj2):
+ return unaccent_icmp(obj1.name, obj2.name)
+
+ def countries(self):
+ "Return the countries of the items"
+ countries = []
+ for item in self.items.filter(location__isnull=False):
+ for country in item.location.countries():
+ if not country in countries:
+ countries.append(country)
+ countries.sort(self.__name_cmp)
+ return countries
+ countries.verbose_name = _("states / nations")
+
+ def main_countries(self):
+ "Return the main countries of the items (no aliases or ancestors)"
+ countries = []
+ for item in self.items.filter(location__isnull=False):
+ if not item.location in countries:
+ countries.append(item.location)
+ countries.sort(self.__name_cmp)
+ return countries
+ main_countries.verbose_name = _("states / nations")
+
+ def 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)
+
+ cmp = lambda a, b: unaccent_icmp(a.value, b.value)
+ groups.sort(cmp)
+
+ return groups
+ ethnic_groups.verbose_name = _('populations / social groups')
+
+ def computed_duration(self):
+ duration = Duration()
+ for item in self.items.all():
+ duration += item.computed_duration()
+ return duration
+ computed_duration.verbose_name = _('computed duration')
+
+ def computed_size(self):
+ "Return the total size of a collection"
+ size = 0
+ for item in self.items.all():
+ size += item.size()
+ return size
+ computed_size.verbose_name = _('collection size')
+
+ def document_status(self):
+ if '_I_' in self.public_id:
+ return ugettext('Unpublished')
+ elif '_E_' in self.public_id:
+ return ugettext('Published')
+ else:
+ return ''
+
+ def get_url(self):
+ return get_full_url(reverse('telemeta-collection-detail', kwargs={'public_id':self.pk}))
+
+ def to_dict_with_more(self):
+ # metadata = model_to_dict(self, fields=[], exclude=self.exclude)
+ metadata = self.to_dict()
+ for key in self.exclude:
+ if key in metadata.keys():
+ del metadata[key]
+
+ metadata['url'] = get_full_url(reverse('telemeta-collection-detail', kwargs={'public_id':self.pk}))
+ metadata['doc_status'] = self.document_status()
+ metadata['countries'] = ';'.join([location.name for location in self.main_countries()])
+ metadata['ethnic_groups'] = ';'.join([group.value for group in self.ethnic_groups()])
+ metadata['last_modification_date'] = unicode(self.get_revision().time)
+ metadata['computed_duration'] = unicode(self.computed_duration())
+ metadata['computed_size'] = unicode(self.computed_size())
+ metadata['number_of_items'] = unicode(self.items.all().count())
+
+ i = 0
+ for media in self.related.all():
+ metadata['related_media_title' + '_' + str(i)] = media.title
+ if media.url:
+ tag = 'related_media_url' + '_' + str(i)
+ metadata[tag] = media.url
+ elif media.url:
+ metadata[tag] = get_full_url(reverse('telemeta-collection-related',
+ kwargs={'public_id': self.public_id, 'media_id': media.id}))
+ i += 1
+
+ # One ID only
+ identifiers = self.identifiers.all()
+ if identifiers:
+ identifier = identifiers[0]
+ metadata['identifier_id'] = identifier.identifier
+ metadata['identifier_type'] = identifier.type
+ metadata['identifier_date'] = unicode(identifier.date_last)
+ metadata['identifier_note'] = identifier.notes
+
+ # All IDs
+ # i = 0
+ # for indentifier in self.identifiers.all():
+ # metadata['identifier' + '_' + str(i)] = identifier.identifier
+ # metadata['identifier_type' + '_' + str(i)] = identifier.type
+ # metadata['identifier_date_last' + '_' + str(i)] = unicode(identifier.date_last)
+ # metadata['identifier_notes' + '_' + str(i)] = identifier.notes
+ # i += 1
+
+ return metadata
+
+
+class MediaCollectionRelated(MediaRelated):
+ "Collection related media"
+
+ collection = ForeignKey('MediaCollection', related_name="related", verbose_name=_('collection'))
+
+ class Meta(MetaCore):
+ db_table = 'media_collection_related'
+ verbose_name = _('collection related media')
+ verbose_name_plural = _('collection related media')
+
+
+class MediaCollectionIdentifier(Identifier):
+ """Collection identifier"""
+
+ collection = ForeignKey(MediaCollection, related_name="identifiers", verbose_name=_('collection'))
+
+ class Meta(MetaCore):
+ db_table = 'media_collection_identifier'
+ verbose_name = _('collection identifier')
+ verbose_name_plural = _('collection identifiers')
+ unique_together = ('identifier', 'collection')
+
# Authors: Olivier Guilyardi <olivier@samalyse.com>
# Guillaume Pellerin <yomguy@parisson.com>
-__all__ = ['ModelCore', 'MetaCore', 'DurationField', 'Duration', 'WeakForeignKey',
- 'EnhancedModel', 'CharField', 'TextField', 'IntegerField', 'BooleanField',
- 'DateTimeField', 'FileField', 'ForeignKey', 'FloatField', 'DateField',
- 'RequiredFieldError', 'CoreQuerySet', 'CoreManager', 'word_search_q']
-from django.core import exceptions
+import datetime
+import mimetypes
+import re, os, random
+
from django import forms
-from xml.dom.minidom import getDOMImplementation
-from django.db.models.fields import FieldDoesNotExist
-from django.db.models import Q
+from django.conf import settings
+from django.contrib.auth.models import User
+from django.contrib.sites.models import Site
+from django.core import exceptions
+from django.core.exceptions import ObjectDoesNotExist, ValidationError
+from django.core.urlresolvers import reverse, reverse_lazy
from django.db import models
-import datetime
+from django.db.models import Q, URLField
+from django.db.models.fields import FieldDoesNotExist
+from django.forms.models import model_to_dict
+from django.utils.translation import ugettext
from django.utils.translation import ugettext_lazy as _
-import re
-from django.core.exceptions import ObjectDoesNotExist
-from south.modelsinspector import add_introspection_rules
-
-
-class Duration(object):
- """Represent a time duration"""
- def __init__(self, *args, **kwargs):
- if len(args) and isinstance(args[0], datetime.timedelta):
- self._delta = datetime.timedelta(days=args[0].days, seconds=args[0].seconds)
- else:
- self._delta = datetime.timedelta(*args, **kwargs)
-
- def __decorate(self, method, other):
- if isinstance(other, Duration):
- res = method(other._delta)
- else:
- res = method(other)
- if type(res) == datetime.timedelta:
- return Duration(res)
-
- return res
-
- def __add__(self, other):
- return self.__decorate(self._delta.__add__, other)
-
- def __nonzero__(self):
- return self._delta.__nonzero__()
-
- def __str__(self):
- hours = self._delta.days * 24 + self._delta.seconds / 3600
- minutes = (self._delta.seconds % 3600) / 60
- seconds = self._delta.seconds % 60
-
- return "%.2d:%.2d:%.2d" % (hours, minutes, seconds)
-
- @staticmethod
- def fromstr(str):
- if not str:
- return Duration()
-
- test = re.match('^([0-9]+)(?::([0-9]+)(?::([0-9]+))?)?$', str)
- if test:
- groups = test.groups()
- try:
- hours = minutes = seconds = 0
- if groups[0]:
- hours = int(groups[0])
- if groups[1]:
- minutes = int(groups[1])
- if groups[2]:
- seconds = int(groups[2])
-
- return Duration(hours=hours, minutes=minutes, seconds=seconds)
- except TypeError:
- print groups
- raise
- else:
- raise ValueError("Malformed duration string: " + str)
-
- def as_seconds(self):
- return self._delta.days * 24 * 3600 + self._delta.seconds
-
-
-def normalize_field(args, default_value=None):
- """Normalize field constructor arguments, so that the field is marked blank=True
- and has a default value by default.
-
- This behaviour can be disabled by passing the special argument required=True.
-
- The default value can also be overriden with the default=value argument.
- """
- required = False
- if args.has_key('required'):
- required = args['required']
- args.pop('required')
-
- args['blank'] = not required
-
- if not required:
- if not args.has_key('default'):
- if args.get('null'):
- args['default'] = None
- elif default_value is not None:
- args['default'] = default_value
-
- return args
-
-
-class DurationField(models.Field):
- """Duration Django model field based on Django TimeField.
- Essentially the same as a TimeField, but with values over 24h allowed.
-
- The constructor arguments are also normalized with normalize_field().
- """
-
- description = _("Duration")
-
- __metaclass__ = models.SubfieldBase
-
- default_error_messages = {
- 'invalid': _('Enter a valid duration in HH:MM[:ss] format.'),
- }
-
- def __init__(self, *args, **kwargs):
- super(DurationField, self).__init__(*args, **normalize_field(kwargs, '0'))
-
- def db_type(self):
- return 'int'
-
- def to_python(self, value):
- if value is None:
- return None
- if isinstance(value, int) or isinstance(value, long):
- return Duration(seconds=value)
- if isinstance(value, datetime.time):
- return Duration(hours=value.hour, minutes=value.minute, seconds=value.second)
- if isinstance(value, datetime.datetime):
- # Not usually a good idea to pass in a datetime here (it loses
- # information), but this can be a side-effect of interacting with a
- # database backend (e.g. Oracle), so we'll be accommodating.
- return self.to_python(value.time())
- else:
- value = str(value)
- try:
- return Duration.fromstr(value)
- except ValueError:
- raise exceptions.ValidationError(self.error_messages['invalid'])
-
- def get_prep_value(self, value):
- return self.to_python(value)
-
- def get_db_prep_value(self, value, connection=None, prepared=False):
- # Casts times into the format expected by the backend
- try:
- return value.as_seconds()
- except:
- return value
-
- def value_to_string(self, obj):
- val = self._get_val_from_obj(obj)
- if val is None:
- data = ''
- else:
- data = unicode(val)
- return data
-
- def formfield(self, **kwargs):
- defaults = {'form_class': forms.CharField}
- defaults.update(kwargs)
- return super(DurationField, self).formfield(**defaults)
-
-
-class ForeignKey(models.ForeignKey):
- """The constructor arguments of this ForeignKey are normalized
- with normalize_field(), however the field is marked required by default
- unless it is allowed to be null."""
+from telemeta.models.utils import *
+from telemeta.models.fields import *
+from telemeta.util.kdenlive.session import *
+from telemeta.util.unaccent import unaccent_icmp
+from xml.dom.minidom import getDOMImplementation
- def __init__(self, to, **kwargs):
- if not kwargs.has_key('required'):
- if not kwargs.get('null'):
- kwargs['required'] = True
- super(ForeignKey, self).__init__(to, **normalize_field(kwargs, 0))
+PUBLIC_ACCESS_CHOICES = (('none', _('none')), ('metadata', _('metadata')),
+ ('mixed', _('mixed')), ('full', _('full')))
+mimetypes.add_type('video/webm','.webm')
-class WeakForeignKey(ForeignKey):
- """A weak foreign key is the same as foreign key but without cascading
- delete. Instead the reference is set to null when the referenced record
- get deleted. This emulates the ON DELETE SET NULL sql behaviour.
+app_name = 'telemeta'
- This field is automatically allowed to be null, there's no need to pass
- null=True.
+strict_code = getattr(settings, 'TELEMETA_STRICT_CODE', False)
- The constructor arguments are normalized with normalize_field() by the
- parent ForeignKey
- Warning: must be used in conjunction with EnhancedQuerySet, EnhancedManager,
- and EnhancedModel
- """
- def __init__(self, to, **kwargs):
- kwargs['null'] = True
- super(WeakForeignKey, self).__init__(to, **kwargs)
class EnhancedQuerySet(models.query.QuerySet):
abstract = True
-class CharField(models.CharField):
- """This is a CharField with a default max_length of 250.
-
- The arguments are also normalized with normalize_field()"""
-
- def __init__(self, *args, **kwargs):
- if not kwargs.has_key('max_length'):
- kwargs['max_length'] = 250
- super(CharField, self).__init__(*args, **normalize_field(kwargs, ''))
-
-
-class IntegerField(models.IntegerField):
- """IntegerField normalized with normalize_field()"""
-
- def __init__(self, *args, **kwargs):
- super(IntegerField, self).__init__(*args, **normalize_field(kwargs, 0))
-
-
-class BooleanField(models.BooleanField):
- """BooleanField normalized with normalize_field()"""
-
- def __init__(self, *args, **kwargs):
- super(BooleanField, self).__init__(*args, **normalize_field(kwargs, False))
-
-
-class TextField(models.TextField):
- """TextField normalized with normalize_field()"""
-
- def __init__(self, *args, **kwargs):
- super(TextField, self).__init__(*args, **normalize_field(kwargs, ''))
-
-
-class DateTimeField(models.DateTimeField):
- """DateTimeField normalized with normalize_field(). This field is allowed to
- be null by default unless null=False is passed"""
-
- def __init__(self, *args, **kwargs):
- if not kwargs.has_key('null'):
- kwargs['null'] = True
- super(DateTimeField, self).__init__(*args, **normalize_field(kwargs))
-
-
-class FileField(models.FileField):
- """FileField normalized with normalize_field()"""
-
- def __init__(self, *args, **kwargs):
- super(FileField, self).__init__(*args, **normalize_field(kwargs, ''))
-
-
-class FloatField(models.FloatField):
- """FloatField normalized with normalize_field()"""
-
- def __init__(self, *args, **kwargs):
- super(FloatField, self).__init__(*args, **normalize_field(kwargs, 0))
-
-
-class DateField(models.DateField):
- """DateField normalized with normalize_field(). This field is allowed to
- be null by default unless null=False is passed"""
-
- def __init__(self, *args, **kwargs):
- if not kwargs.has_key('null'):
- kwargs['null'] = True
- super(DateField, self).__init__(*args, **normalize_field(kwargs))
-
-
-class RequiredFieldError(Exception):
- def __init__(self, model, field):
- self.model = model
- self.field = field
- super(Exception, self).__init__('%s.%s is required' % (model._meta.object_name, field.name))
-
-
class ModelCore(EnhancedModel):
@classmethod
app_label = 'telemeta'
-def word_search_q(field, pattern):
- words = re.split("[ .*-]+", pattern)
- q = Q()
- for w in words:
- if len(w) >= 3:
- kwargs = {field + '__icontains': w}
- q &= Q(**kwargs)
-
- return q
-
class CoreQuerySet(EnhancedQuerySet):
"Base class for all query sets"
return super(CoreManager, self).get(**kwargs)
-# South introspection rules
-add_introspection_rules([], ["^telemeta\.models\.core\.CharField"])
-add_introspection_rules([], ["^telemeta\.models\.core\.TextField"])
-add_introspection_rules([], ["^telemeta\.models\.core\.FileField"])
-add_introspection_rules([], ["^telemeta\.models\.core\.IntegerField"])
-add_introspection_rules([], ["^telemeta\.models\.core\.BooleanField"])
-add_introspection_rules([], ["^telemeta\.models\.core\.DateTimeField"])
-add_introspection_rules([], ["^telemeta\.models\.core\.DateField"])
-add_introspection_rules([], ["^telemeta\.models\.core\.FloatField"])
-add_introspection_rules([], ["^telemeta\.models\.core\.DurationField"])
-add_introspection_rules([], ["^telemeta\.models\.core\.ForeignKey"])
-add_introspection_rules([], ["^telemeta\.models\.core\.WeakForeignKey"])
-
--- /dev/null
+# -*- coding: utf-8 -*-
+# Copyright (C) 2010 Samalyse SARL
+# Copyright (C) 2010-2014 Parisson SARL
+
+# This software is a computer program whose purpose is to backup, analyse,
+# transcode and stream any audio content with its metadata over a web frontend.
+
+# This software is governed by the CeCILL license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+
+# As a counterpart to the access to the source code and rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using, modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL license and that you accept its terms.
+#
+# Authors: Olivier Guilyardi <olivier@samalyse.com>
+# David LIPSZYC <davidlipszyc@gmail.com>
+# Guillaume Pellerin <yomguy@parisson.com>
+
+from __future__ import division
+from django.utils.translation import ugettext_lazy as _
+from telemeta.models.core import *
+from telemeta.models.resource import *
+from telemeta.models.collection import *
+
+
+class MediaCorpus(MediaBaseResource):
+ "Describe a corpus"
+
+ element_type = 'corpus'
+ children_type = 'collections'
+
+ children = models.ManyToManyField(MediaCollection, related_name="corpus",
+ verbose_name=_('collections'), blank=True, null=True)
+ recorded_from_year = IntegerField(_('recording year (from)'), help_text=_('YYYY'))
+ recorded_to_year = IntegerField(_('recording year (until)'), help_text=_('YYYY'))
+
+ objects = MediaCorpusManager()
+
+ @property
+ def public_id(self):
+ return self.code
+
+ @property
+ def has_mediafile(self):
+ for child in self.children.all():
+ if child.has_mediafile:
+ return True
+ return False
+
+ def computed_duration(self):
+ duration = Duration()
+ for child in self.children.all():
+ duration += child.computed_duration()
+ return duration
+ computed_duration.verbose_name = _('total available duration')
+
+ class Meta(MetaCore):
+ db_table = 'media_corpus'
+ verbose_name = _('corpus')
+ verbose_name_plural = _('corpus')
+ ordering = ['code']
+
+
+class MediaCorpusRelated(MediaRelated):
+ "Corpus related media"
+
+ resource = ForeignKey(MediaCorpus, related_name="related", verbose_name=_('corpus'))
+
+ class Meta(MetaCore):
+ db_table = 'media_corpus_related'
+ verbose_name = _('corpus related media')
+ verbose_name_plural = _('corpus related media')
+
#
# Author: Olivier Guilyardi <olivier@samalyse.com>
-from telemeta.models.core import Duration
-from telemeta.models.media import *
+from telemeta.models.core import *
+from telemeta.models.item import *
+from telemeta.models.collection import *
from django.contrib.sites.models import Site
from django.conf import settings
--- /dev/null
+# -*- coding: utf-8 -*-
+#
+# Copyright (C) 2007-2010 Samalyse SARL
+# Copyright (C) 2010-2015 Parisson SARL
+#
+# This software is a computer program whose purpose is to backup, analyse,
+# transcode and stream any audio content with its metadata over a web frontend.
+#
+# This software is governed by the CeCILL license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+#
+# As a counterpart to the access to the source code and rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+#
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using, modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+#
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL license and that you accept its terms.
+#
+# Authors: Olivier Guilyardi <olivier@samalyse.com>
+# Guillaume Pellerin <yomguy@parisson.com>
+
+from __future__ import division
+
+__all__ = ['DurationField', 'Duration', 'WeakForeignKey',
+ 'CharField', 'TextField', 'IntegerField', 'BooleanField',
+ 'DateTimeField', 'FileField', 'ForeignKey', 'FloatField', 'DateField',
+ 'RequiredFieldError',]
+
+import datetime
+from django import forms
+from django.db import models
+from django.utils.translation import ugettext_lazy as _
+from south.modelsinspector import add_introspection_rules
+
+
+class Duration(object):
+ """Represent a time duration"""
+ def __init__(self, *args, **kwargs):
+ if len(args) and isinstance(args[0], datetime.timedelta):
+ self._delta = datetime.timedelta(days=args[0].days, seconds=args[0].seconds)
+ else:
+ self._delta = datetime.timedelta(*args, **kwargs)
+
+ def __decorate(self, method, other):
+ if isinstance(other, Duration):
+ res = method(other._delta)
+ else:
+ res = method(other)
+ if type(res) == datetime.timedelta:
+ return Duration(res)
+
+ return res
+
+ def __add__(self, other):
+ return self.__decorate(self._delta.__add__, other)
+
+ def __nonzero__(self):
+ return self._delta.__nonzero__()
+
+ def __str__(self):
+ hours = self._delta.days * 24 + self._delta.seconds / 3600
+ minutes = (self._delta.seconds % 3600) / 60
+ seconds = self._delta.seconds % 60
+
+ return "%.2d:%.2d:%.2d" % (hours, minutes, seconds)
+
+ @staticmethod
+ def fromstr(str):
+ if not str:
+ return Duration()
+
+ test = re.match('^([0-9]+)(?::([0-9]+)(?::([0-9]+))?)?$', str)
+ if test:
+ groups = test.groups()
+ try:
+ hours = minutes = seconds = 0
+ if groups[0]:
+ hours = int(groups[0])
+ if groups[1]:
+ minutes = int(groups[1])
+ if groups[2]:
+ seconds = int(groups[2])
+
+ return Duration(hours=hours, minutes=minutes, seconds=seconds)
+ except TypeError:
+ print groups
+ raise
+ else:
+ raise ValueError("Malformed duration string: " + str)
+
+ def as_seconds(self):
+ return self._delta.days * 24 * 3600 + self._delta.seconds
+
+
+def normalize_field(args, default_value=None):
+ """Normalize field constructor arguments, so that the field is marked blank=True
+ and has a default value by default.
+
+ This behaviour can be disabled by passing the special argument required=True.
+
+ The default value can also be overriden with the default=value argument.
+ """
+ required = False
+ if args.has_key('required'):
+ required = args['required']
+ args.pop('required')
+
+ args['blank'] = not required
+
+ if not required:
+ if not args.has_key('default'):
+ if args.get('null'):
+ args['default'] = None
+ elif default_value is not None:
+ args['default'] = default_value
+
+ return args
+
+
+class DurationField(models.Field):
+ """Duration Django model field based on Django TimeField.
+ Essentially the same as a TimeField, but with values over 24h allowed.
+
+ The constructor arguments are also normalized with normalize_field().
+ """
+
+ description = _("Duration")
+
+ __metaclass__ = models.SubfieldBase
+
+ default_error_messages = {
+ 'invalid': _('Enter a valid duration in HH:MM[:ss] format.'),
+ }
+
+ def __init__(self, *args, **kwargs):
+ super(DurationField, self).__init__(*args, **normalize_field(kwargs, '0'))
+
+ def db_type(self):
+ return 'int'
+
+ def to_python(self, value):
+ if value is None:
+ return None
+ if isinstance(value, int) or isinstance(value, long):
+ return Duration(seconds=value)
+ if isinstance(value, datetime.time):
+ return Duration(hours=value.hour, minutes=value.minute, seconds=value.second)
+ if isinstance(value, datetime.datetime):
+ # Not usually a good idea to pass in a datetime here (it loses
+ # information), but this can be a side-effect of interacting with a
+ # database backend (e.g. Oracle), so we'll be accommodating.
+ return self.to_python(value.time())
+ else:
+ value = str(value)
+ try:
+ return Duration.fromstr(value)
+ except ValueError:
+ raise exceptions.ValidationError(self.error_messages['invalid'])
+
+ def get_prep_value(self, value):
+ return self.to_python(value)
+
+ def get_db_prep_value(self, value, connection=None, prepared=False):
+ # Casts times into the format expected by the backend
+ try:
+ return value.as_seconds()
+ except:
+ return value
+
+ def value_to_string(self, obj):
+ val = self._get_val_from_obj(obj)
+ if val is None:
+ data = ''
+ else:
+ data = unicode(val)
+ return data
+
+ def formfield(self, **kwargs):
+ defaults = {'form_class': forms.CharField}
+ defaults.update(kwargs)
+ return super(DurationField, self).formfield(**defaults)
+
+
+class ForeignKey(models.ForeignKey):
+ """The constructor arguments of this ForeignKey are normalized
+ with normalize_field(), however the field is marked required by default
+ unless it is allowed to be null."""
+
+ def __init__(self, to, **kwargs):
+ if not kwargs.has_key('required'):
+ if not kwargs.get('null'):
+ kwargs['required'] = True
+
+ super(ForeignKey, self).__init__(to, **normalize_field(kwargs, 0))
+
+
+class WeakForeignKey(ForeignKey):
+ """A weak foreign key is the same as foreign key but without cascading
+ delete. Instead the reference is set to null when the referenced record
+ get deleted. This emulates the ON DELETE SET NULL sql behaviour.
+
+ This field is automatically allowed to be null, there's no need to pass
+ null=True.
+
+ The constructor arguments are normalized with normalize_field() by the
+ parent ForeignKey
+
+ Warning: must be used in conjunction with EnhancedQuerySet, EnhancedManager,
+ and EnhancedModel
+ """
+ def __init__(self, to, **kwargs):
+ kwargs['null'] = True
+ super(WeakForeignKey, self).__init__(to, **kwargs)
+
+
+
+class CharField(models.CharField):
+ """This is a CharField with a default max_length of 250.
+
+ The arguments are also normalized with normalize_field()"""
+
+ def __init__(self, *args, **kwargs):
+ if not kwargs.has_key('max_length'):
+ kwargs['max_length'] = 250
+ super(CharField, self).__init__(*args, **normalize_field(kwargs, ''))
+
+
+class IntegerField(models.IntegerField):
+ """IntegerField normalized with normalize_field()"""
+
+ def __init__(self, *args, **kwargs):
+ super(IntegerField, self).__init__(*args, **normalize_field(kwargs, 0))
+
+
+class BooleanField(models.BooleanField):
+ """BooleanField normalized with normalize_field()"""
+
+ def __init__(self, *args, **kwargs):
+ super(BooleanField, self).__init__(*args, **normalize_field(kwargs, False))
+
+
+class TextField(models.TextField):
+ """TextField normalized with normalize_field()"""
+
+ def __init__(self, *args, **kwargs):
+ super(TextField, self).__init__(*args, **normalize_field(kwargs, ''))
+
+
+class DateTimeField(models.DateTimeField):
+ """DateTimeField normalized with normalize_field(). This field is allowed to
+ be null by default unless null=False is passed"""
+
+ def __init__(self, *args, **kwargs):
+ if not kwargs.has_key('null'):
+ kwargs['null'] = True
+ super(DateTimeField, self).__init__(*args, **normalize_field(kwargs))
+
+
+class FileField(models.FileField):
+ """FileField normalized with normalize_field()"""
+
+ def __init__(self, *args, **kwargs):
+ super(FileField, self).__init__(*args, **normalize_field(kwargs, ''))
+
+
+class FloatField(models.FloatField):
+ """FloatField normalized with normalize_field()"""
+
+ def __init__(self, *args, **kwargs):
+ super(FloatField, self).__init__(*args, **normalize_field(kwargs, 0))
+
+
+class DateField(models.DateField):
+ """DateField normalized with normalize_field(). This field is allowed to
+ be null by default unless null=False is passed"""
+
+ def __init__(self, *args, **kwargs):
+ if not kwargs.has_key('null'):
+ kwargs['null'] = True
+ super(DateField, self).__init__(*args, **normalize_field(kwargs))
+
+
+class RequiredFieldError(Exception):
+ def __init__(self, model, field):
+ self.model = model
+ self.field = field
+ super(Exception, self).__init__('%s.%s is required' % (model._meta.object_name, field.name))
+
+
+
+# South introspection rules
+add_introspection_rules([], ["^telemeta\.models\.core\.CharField"])
+add_introspection_rules([], ["^telemeta\.models\.core\.TextField"])
+add_introspection_rules([], ["^telemeta\.models\.core\.FileField"])
+add_introspection_rules([], ["^telemeta\.models\.core\.IntegerField"])
+add_introspection_rules([], ["^telemeta\.models\.core\.BooleanField"])
+add_introspection_rules([], ["^telemeta\.models\.core\.DateTimeField"])
+add_introspection_rules([], ["^telemeta\.models\.core\.DateField"])
+add_introspection_rules([], ["^telemeta\.models\.core\.FloatField"])
+add_introspection_rules([], ["^telemeta\.models\.core\.DurationField"])
+add_introspection_rules([], ["^telemeta\.models\.core\.ForeignKey"])
+add_introspection_rules([], ["^telemeta\.models\.core\.WeakForeignKey"])
+
--- /dev/null
+# -*- coding: utf-8 -*-
+# Copyright (C) 2010 Samalyse SARL
+# Copyright (C) 2010-2014 Parisson SARL
+
+# This software is a computer program whose purpose is to backup, analyse,
+# transcode and stream any audio content with its metadata over a web frontend.
+
+# This software is governed by the CeCILL license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+
+# As a counterpart to the access to the source code and rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using, modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL license and that you accept its terms.
+#
+# Authors: Olivier Guilyardi <olivier@samalyse.com>
+# David LIPSZYC <davidlipszyc@gmail.com>
+# Guillaume Pellerin <yomguy@parisson.com>
+
+from __future__ import division
+from django.utils.translation import ugettext_lazy as _
+from telemeta.models.core import *
+from telemeta.models.resource import *
+from telemeta.models.corpus import *
+
+
+class MediaFonds(MediaBaseResource):
+ "Describe fonds"
+
+ element_type = 'fonds'
+ children_type = 'corpus'
+
+ children = models.ManyToManyField(MediaCorpus, related_name="fonds",
+ verbose_name=_('corpus'), blank=True, null=True)
+
+ objects = MediaFondsManager()
+
+ @property
+ def public_id(self):
+ return self.code
+
+ @property
+ def has_mediafile(self):
+ for child in self.children.all():
+ if child.has_mediafile:
+ return True
+ return False
+
+ def computed_duration(self):
+ duration = Duration()
+ for child in self.children.all():
+ duration += child.computed_duration()
+ return duration
+ computed_duration.verbose_name = _('total available duration')
+
+ class Meta(MetaCore):
+ db_table = 'media_fonds'
+ verbose_name = _('fonds')
+ verbose_name_plural = _('fonds')
+ ordering = ['code']
+
+
+class MediaFondsRelated(MediaRelated):
+ "Fonds related media"
+
+ resource = ForeignKey(MediaFonds, related_name="related", verbose_name=_('fonds'))
+
+ class Meta(MetaCore):
+ db_table = 'media_fonds_related'
+ verbose_name = _('fonds related media')
+ verbose_name_plural = _('fonds related media')
+
+
from django.core.exceptions import ValidationError
from telemeta.models.core import *
from telemeta.util.unaccent import unaccent_icmp
-from telemeta.models.location import *
-from telemeta.models.system import *
-from telemeta.models.query import *
-from telemeta.models.instrument import *
from telemeta.models.enum import *
from telemeta.models.language import *
-from telemeta.models.media import *
from django.db import models
--- /dev/null
+# -*- coding: utf-8 -*-
+# Copyright (C) 2010 Samalyse SARL
+# Copyright (C) 2010-2014 Parisson SARL
+
+# This software is a computer program whose purpose is to backup, analyse,
+# transcode and stream any audio content with its metadata over a web frontend.
+
+# This software is governed by the CeCILL license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+
+# As a counterpart to the access to the source code and rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using, modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL license and that you accept its terms.
+#
+# Authors: Olivier Guilyardi <olivier@samalyse.com>
+# David LIPSZYC <davidlipszyc@gmail.com>
+# Guillaume Pellerin <yomguy@parisson.com>
+
+
+from telemeta.models.core import *
+from telemeta.models.fields import *
+from django.utils.translation import ugettext_lazy as _
+
+
+class Identifier(ModelCore):
+ """Resource identifier"""
+
+ identifier = CharField(_('identifier'), max_length=255, blank=True, unique=True)
+ type = WeakForeignKey('IdentifierType', verbose_name=_('type'))
+ date_add = DateTimeField(_('date added'), auto_now_add=True)
+ date_first = DateTimeField(_('date of first attribution'))
+ date_last = DateTimeField(_('date of last attribution'))
+ date_modified = DateTimeField(_('date modified'), auto_now=True)
+ notes = TextField(_('notes'))
+
+ class Meta(MetaCore):
+ abstract = True
+ ordering = ['-date_last']
from telemeta.models.core import *
from django.utils.translation import ugettext_lazy as _
+
class Instrument(ModelCore):
"Instrument used in the item"
name = CharField(_('name'), required=True)
--- /dev/null
+# -*- coding: utf-8 -*-
+# Copyright (C) 2010 Samalyse SARL
+# Copyright (C) 2010-2014 Parisson SARL
+
+# This software is a computer program whose purpose is to backup, analyse,
+# transcode and stream any audio content with its metadata over a web frontend.
+
+# This software is governed by the CeCILL license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+
+# As a counterpart to the access to the source code and rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using, modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL license and that you accept its terms.
+#
+# Authors: Olivier Guilyardi <olivier@samalyse.com>
+# David LIPSZYC <davidlipszyc@gmail.com>
+# Guillaume Pellerin <yomguy@parisson.com>
+
+from __future__ import division
+from django.utils.translation import ugettext_lazy as _
+from telemeta.models.core import *
+from telemeta.models.resource import *
+from telemeta.models.query import *
+from telemeta.models.identifier import *
+from telemeta.models.resource import *
+from telemeta.models.enum import *
+
+
+item_published_code_regex = getattr(settings, 'ITEM_PUBLISHED_CODE_REGEX', '[A-Za-z0-9._-]*')
+item_unpublished_code_regex = getattr(settings, 'ITEM_UNPUBLISHED_CODE_REGEX', '[A-Za-z0-9._-]*')
+
+# CREM
+# item_published_code_regex = collection_published_code_regex + '(?:_[0-9]{2,3}){1,2}'
+# item_unpublished_code_regex = collection_unpublished_code_regex + '_[0-9]{2,3}(?:_[0-9]{2,3}){0,2}'
+
+item_code_regex = '(?:%s|%s)' % (item_published_code_regex, item_unpublished_code_regex)
+
+ITEM_PUBLIC_ACCESS_CHOICES = (('none', _('none')), ('metadata', _('metadata')),
+ ('full', _('full')))
+
+ITEM_TRANSODING_STATUS = ((0, _('broken')), (1, _('pending')), (2, _('processing')),
+ (3, _('done')), (5, _('ready')))
+
+
+class MediaItem(MediaResource):
+ "Describe an item"
+
+ element_type = 'item'
+
+ # Main Informations
+ title = CharField(_('title'))
+ alt_title = CharField(_('original title / translation'))
+ collector = CharField(_('collector'), help_text=_('First name, Last name ; First name, Last name'))
+ collection = ForeignKey('MediaCollection', related_name="items", verbose_name=_('collection'))
+ recorded_from_date = DateField(_('recording date (from)'), help_text=_('YYYY-MM-DD'))
+ recorded_to_date = DateField(_('recording date (until)'), help_text=_('YYYY-MM-DD'))
+ public_access = CharField(_('access type'), choices=ITEM_PUBLIC_ACCESS_CHOICES, max_length=16, default="metadata")
+
+ # Geographic and cultural informations
+ location = WeakForeignKey('Location', verbose_name=_('location'))
+ location_comment = CharField(_('location details'))
+ cultural_area = CharField(_('cultural area'))
+ language = CharField(_('language'))
+ language_iso = ForeignKey('Language', related_name="items", verbose_name=_('Language (ISO norm)'), blank=True, null=True, on_delete=models.SET_NULL)
+ ethnic_group = WeakForeignKey('EthnicGroup', related_name="items", verbose_name=_('population / social group'))
+ context_comment = TextField(_('Ethnographic context'))
+
+ # Musical informations
+ moda_execut = CharField(_('implementing rules'))
+ vernacular_style = WeakForeignKey('VernacularStyle', related_name="items", verbose_name=_('vernacular style'))
+ generic_style = WeakForeignKey('GenericStyle', related_name="items", verbose_name=_('generic style'))
+ author = CharField(_('author / compositor'), help_text=_('First name, Last name ; First name, Last name'))
+
+ # Legal mentions
+ organization = WeakForeignKey('Organization', verbose_name=_('organization'))
+ depositor = CharField(_('depositor'))
+ rights = WeakForeignKey('Rights', verbose_name=_('rights'))
+
+ # Archiving data
+ code = CharField(_('code'), unique=True, blank=True, required=True, help_text=_('CollectionCode_ItemCode'))
+ old_code = CharField(_('original code'), unique=False, blank=True)
+ track = CharField(_('item number'))
+ collector_selection = CharField(_('collector selection'))
+ collector_from_collection = BooleanField(_('collector as in collection'))
+ creator_reference = CharField(_('creator reference'))
+ external_references = TextField(_('published references'))
+ auto_period_access = BooleanField(_('automatic access after a rolling period'), default=True)
+ comment = TextField(_('remarks'))
+
+ # Technical data
+ media_type = WeakForeignKey('MediaType', related_name="items", verbose_name=_('media type'))
+ approx_duration = DurationField(_('approximative duration'), blank=True, help_text=_('hh:mm:ss'))
+ mimetype = CharField(_('mime type'), max_length=255, blank=True)
+ file = FileField(_('file'), upload_to='items/%Y/%m/%d', db_column="filename", max_length=1024)
+ url = URLField(_('URL'), max_length=512, blank=True)
+
+ # LAM
+ recordist = CharField(_('recordist'))
+ digitalist = CharField(_('digitalist'))
+ digitization_date = DateField(_('digitization date'))
+ publishing_date = DateField(_('publishing date'))
+ scientist = CharField(_('scientist'), help_text=_('First name, Last name ; First name, Last name'))
+ topic = WeakForeignKey('Topic', verbose_name=_('topic'))
+ summary = TextField(_('summary'))
+ contributor = CharField(_('contributor'))
+
+ # Manager
+ objects = MediaItemManager()
+
+ exclude = ['copied_from_item', 'mimetype', 'url',
+ 'organization', 'depositor', 'rights',
+ 'recordist', 'digitalist', 'digitization_date',
+ 'publishing_date', 'scientist', 'topic',
+ 'summary', 'contributor', ]
+
+ def keywords(self):
+ return ContextKeyword.objects.filter(item_relations__item = self)
+ keywords.verbose_name = _('keywords')
+
+ @property
+ def public_id(self):
+ if self.code:
+ return self.code
+ return str(self.id)
+
+ @property
+ def mime_type(self):
+ if not self.mimetype:
+ if self.file:
+ if os.path.exists(self.file.path):
+ self.mimetype = mimetypes.guess_type(self.file.path)[0]
+ self.save()
+ return self.mimetype
+ else:
+ return 'none'
+ else:
+ return 'none'
+ else:
+ return _('none')
+
+
+ class Meta(MetaCore):
+ db_table = 'media_items'
+ permissions = (("can_play_all_items", "Can play all media items"),
+ ("can_download_all_items", "Can download all media items"), )
+ verbose_name = _('item')
+
+ def is_valid_code(self, code):
+ "Check if the item code is well formed"
+ if not re.match('^' + self.collection.code, self.code):
+ return False
+ if self.collection.is_published:
+ regex = '^' + item_published_code_regex + '$'
+ else:
+ regex = '^' + item_unpublished_code_regex + '$'
+ if re.match(regex, code):
+ return True
+ return False
+
+ def clean(self):
+ if strict_code:
+ if self.code and not self.is_valid_code(self.code):
+ raise ValidationError("%s is not a valid item code for collection %s"
+ % (self.code, self.collection.code))
+
+ def save(self, force_insert=False, force_update=False):
+ super(MediaItem, self).save(force_insert, force_update)
+
+ def computed_duration(self):
+ "Tell the length in seconds of this item media data"
+ return self.approx_duration
+
+ computed_duration.verbose_name = _('computed duration')
+
+ def __unicode__(self):
+ if self.title and not re.match('^ *N *$', self.title):
+ title = self.title
+ else:
+ title = unicode(self.collection.title)
+ if self.track:
+ title += ' ' + self.track
+ return title
+
+ def get_source(self):
+ source = None
+ if self.file and os.path.exists(self.file.path):
+ source = self.file.path
+ elif self.url:
+ source = self.url
+ return source
+
+ @property
+ def instruments(self):
+ "Return the instruments of the item"
+ instruments = []
+ performances = MediaItemPerformance.objects.filter(media_item=self)
+ for performance in performances:
+ instrument = performance.instrument
+ alias = performance.alias
+ if not instrument in instruments:
+ instruments.append(instrument)
+ if not alias in instruments:
+ instruments.append(alias)
+
+ instruments.sort(self.__name_cmp)
+ return instruments
+
+ instruments.verbose_name = _("instruments")
+
+ def size(self):
+ if self.file and os.path.exists(self.file.path):
+ return self.file.size
+ else:
+ return 0
+ size.verbose_name = _('item size')
+
+ def get_url(self):
+ return get_full_url(reverse('telemeta-item-detail', kwargs={'public_id':self.pk}))
+
+ def to_dict_with_more(self):
+ # metadata = model_to_dict(self, fields=[], exclude=self.exclude)
+ metadata = self.to_dict()
+ for key in self.exclude:
+ if key in metadata.keys():
+ del metadata[key]
+
+ metadata['url'] = self.get_url()
+ metadata['last_modification_date'] = unicode(self.get_revision().time)
+ metadata['collection'] = self.collection.get_url()
+
+ keywords = []
+ for keyword in self.keywords():
+ keywords.append(keyword.value)
+ metadata['keywords'] = ';'.join(keywords)
+
+ i = 0
+ for media in self.related.all():
+ if media.title:
+ tag = 'related_media_title' + '_' + str(i)
+ metadata[tag] = media.title
+ else:
+ metadata[tag] = ''
+ if media.url:
+ tag = 'related_media_url' + '_' + str(i)
+ metadata[tag] = media.url
+ elif media.url:
+ metadata[tag] = get_full_url(reverse('telemeta-collection-related',
+ kwargs={'public_id': self.public_id, 'media_id': media.id}))
+ i += 1
+
+
+ instruments = []
+ instrument_vernacular_names = []
+ performers = []
+
+ for performance in self.performances.all():
+ if performance.instrument:
+ instruments.append(performance.instrument.name)
+ if performance.alias:
+ instrument_vernacular_names.append(performance.alias.name)
+ if performance.musicians:
+ performers.append(performance.musicians.replace(' et ', ';'))
+
+ metadata['instruments'] = ';'.join(instruments)
+ metadata['instrument_vernacular_names'] = ';'.join(instrument_vernacular_names)
+ metadata['performers'] = ';'.join(performers)
+
+ i = 0
+ for indentifier in self.identifiers.all():
+ metadata['identifier' + '_' + str(i)] = identifier.identifier
+ metadata['identifier_type' + '_' + str(i)] = identifier.type
+ metadata['identifier_date_last' + '_' + str(i)] = unicode(identifier.date_last)
+ metadata['identifier_notes' + '_' + str(i)] = identifier.notes
+ i += 1
+
+ analyzers = ['channels', 'samplerate', 'duration', 'resolution', 'mime_type']
+ for analyzer_id in analyzers:
+ analysis = MediaItemAnalysis.objects.filter(item=self, analyzer_id=analyzer_id)
+ if analysis:
+ metadata[analyzer_id] = analysis[0].value
+
+ metadata['file_size'] = unicode(self.size())
+ metadata['thumbnail'] = get_full_url(reverse('telemeta-item-visualize',
+ kwargs={'public_id': self.public_id,
+ 'grapher_id': 'waveform_centroid',
+ 'width': 346,
+ 'height': 130}))
+
+ # One ID only
+ identifiers = self.identifiers.all()
+ if identifiers:
+ identifier = identifiers[0]
+ metadata['identifier_id'] = identifier.identifier
+ metadata['identifier_type'] = identifier.type
+ metadata['identifier_date'] = unicode(identifier.date_last)
+ metadata['identifier_note'] = identifier.notes
+
+ return metadata
+
+
+class MediaItemRelated(MediaRelated):
+ "Item related media"
+
+ item = ForeignKey('MediaItem', related_name="related", verbose_name=_('item'))
+
+ def save(self, force_insert=False, force_update=False, using=False):
+ super(MediaItemRelated, self).save(force_insert, force_update)
+
+ def parse_markers(self, **kwargs):
+ # Parse KDEnLive session
+ if self.file:
+ if self.is_kdenlive_session():
+ session = KDEnLiveSession(self.file.path)
+ markers = session.markers(**kwargs)
+ for marker in markers:
+ m = MediaItemMarker(item=self.item)
+ m.public_id = get_random_hash()
+ m.time = float(marker['time'])
+ m.title = marker['comment']
+ m.save()
+ return markers
+
+ class Meta(MetaCore):
+ db_table = 'media_item_related'
+ verbose_name = _('item related media')
+ verbose_name_plural = _('item related media')
+
+
+class MediaItemKeyword(ModelCore):
+ "Item keyword"
+ item = ForeignKey('MediaItem', verbose_name=_('item'), related_name="keyword_relations")
+ keyword = ForeignKey('ContextKeyword', verbose_name=_('keyword'), related_name="item_relations")
+
+ class Meta(MetaCore):
+ db_table = 'media_item_keywords'
+ unique_together = (('item', 'keyword'),)
+
+
+class MediaItemPerformance(ModelCore):
+ "Item performance"
+ media_item = ForeignKey('MediaItem', related_name="performances", verbose_name=_('item'))
+ instrument = WeakForeignKey('Instrument', related_name="performances", verbose_name=_('composition'))
+ alias = WeakForeignKey('InstrumentAlias', related_name="performances", verbose_name=_('vernacular name'))
+ instruments_num = CharField(_('number'))
+ musicians = CharField(_('interprets'))
+
+ class Meta(MetaCore):
+ db_table = 'media_item_performances'
+
+
+class MediaItemAnalysis(ModelCore):
+ "Item analysis result computed by TimeSide"
+
+ element_type = 'analysis'
+ item = ForeignKey('MediaItem', related_name="analysis", verbose_name=_('item'))
+ analyzer_id = CharField(_('id'), required=True)
+ name = CharField(_('name'))
+ value = CharField(_('value'))
+ unit = CharField(_('unit'))
+
+ class Meta(MetaCore):
+ db_table = 'media_analysis'
+ ordering = ['name']
+
+ def to_dict(self):
+ if self.analyzer_id == 'duration':
+ if '.' in self.value:
+ value = self.value.split('.')
+ self.value = '.'.join([value[0], value[1][:2]])
+ return {'id': self.analyzer_id, 'name': self.name, 'value': self.value, 'unit': self.unit}
+
+
+
+class MediaItemMarker(MediaResource):
+ "2D marker object : text value vs. time (in seconds)"
+
+ element_type = 'marker'
+
+ item = ForeignKey('MediaItem', related_name="markers", verbose_name=_('item'))
+ public_id = CharField(_('public_id'), required=True)
+ time = FloatField(_('time (s)'))
+ title = CharField(_('title'))
+ date = DateTimeField(_('date'), auto_now=True)
+ description = TextField(_('description'))
+ author = ForeignKey(User, related_name="markers", verbose_name=_('author'),
+ blank=True, null=True)
+
+ class Meta(MetaCore):
+ db_table = 'media_markers'
+ ordering = ['time']
+
+ def __unicode__(self):
+ if self.title:
+ return self.title
+ else:
+ return self.public_id
+
+
+class MediaItemTranscoded(MediaResource):
+ "Item file transcoded"
+
+ element_type = 'transcoded item'
+
+ item = models.ForeignKey('MediaItem', related_name="transcoded", verbose_name=_('item'))
+ mimetype = models.CharField(_('mime_type'), max_length=255, blank=True)
+ date_added = DateTimeField(_('date'), auto_now_add=True)
+ status = models.IntegerField(_('status'), choices=ITEM_TRANSODING_STATUS, default=1)
+ file = models.FileField(_('file'), upload_to='items/%Y/%m/%d', max_length=1024, blank=True)
+
+ @property
+ def mime_type(self):
+ if not self.mimetype:
+ if self.file:
+ if os.path.exists(self.file.path):
+ self.mimetype = mimetypes.guess_type(self.file.path)[0]
+ self.save()
+ return self.mimetype
+ else:
+ return 'none'
+ else:
+ return 'none'
+ else:
+ return self.mimetype
+
+ def __unicode__(self):
+ if self.item.title:
+ return self.item.title + ' - ' + self.mime_type
+ else:
+ return self.item.public_id + ' - ' + self.mime_type
+
+ class Meta(MetaCore):
+ db_table = app_name + '_media_transcoded'
+
+
+class MediaItemTranscodingFlag(ModelCore):
+ "Item flag to know if the MediaItem has been transcoded to a given format"
+
+ item = ForeignKey('MediaItem', related_name="transcoding", verbose_name=_('item'))
+ mime_type = CharField(_('mime_type'), required=True)
+ date = DateTimeField(_('date'), auto_now=True)
+ value = BooleanField(_('transcoded'))
+
+ class Meta(MetaCore):
+ db_table = 'media_transcoding'
+
+
+class MediaItemIdentifier(Identifier):
+ """Item identifier"""
+
+ item = ForeignKey(MediaItem, related_name="identifiers", verbose_name=_('item'))
+
+ class Meta(MetaCore):
+ db_table = 'media_item_identifier'
+ verbose_name = _('item identifier')
+ verbose_name_plural = _('item identifiers')
+ unique_together = ('identifier', 'item')
+
+
+
+class MediaPart(MediaResource):
+ "Describe an item part"
+ element_type = 'part'
+ item = ForeignKey('MediaItem', related_name="parts", verbose_name=_('item'))
+ title = CharField(_('title'), required=True)
+ start = FloatField(_('start'), required=True)
+ end = FloatField(_('end'), required=True)
+
+ class Meta(MetaCore):
+ db_table = 'media_parts'
+ verbose_name = _('item part')
+
+ def __unicode__(self):
+ return self.title
+++ /dev/null
-# -*- coding: utf-8 -*-
-# Copyright (C) 2010 Samalyse SARL
-# Copyright (C) 2010-2014 Parisson SARL
-
-# This software is a computer program whose purpose is to backup, analyse,
-# transcode and stream any audio content with its metadata over a web frontend.
-
-# This software is governed by the CeCILL license under French law and
-# abiding by the rules of distribution of free software. You can use,
-# modify and/ or redistribute the software under the terms of the CeCILL
-# license as circulated by CEA, CNRS and INRIA at the following URL
-# "http://www.cecill.info".
-
-# As a counterpart to the access to the source code and rights to copy,
-# modify and redistribute granted by the license, users are provided only
-# with a limited warranty and the software's author, the holder of the
-# economic rights, and the successive licensors have only limited
-# liability.
-
-# In this respect, the user's attention is drawn to the risks associated
-# with loading, using, modifying and/or developing or reproducing the
-# software by the user in light of its specific status of free software,
-# that may mean that it is complicated to manipulate, and that also
-# therefore means that it is reserved for developers and experienced
-# professionals having in-depth computer knowledge. Users are therefore
-# encouraged to load and test the software's suitability as regards their
-# requirements in conditions enabling the security of their systems and/or
-# data to be ensured and, more generally, to use and operate it in the
-# same conditions as regards security.
-
-# The fact that you are presently reading this means that you have had
-# knowledge of the CeCILL license and that you accept its terms.
-#
-# Authors: Olivier Guilyardi <olivier@samalyse.com>
-# David LIPSZYC <davidlipszyc@gmail.com>
-# Guillaume Pellerin <yomguy@parisson.com>
-
-from __future__ import division
-import re, os, random
-import mimetypes
-from django.contrib.auth.models import User
-from django.utils.translation import ugettext_lazy as _
-from django.utils.translation import ugettext
-from django.core.exceptions import ValidationError
-from telemeta.models.core import *
-from telemeta.models.enum import ContextKeyword
-from telemeta.util.unaccent import unaccent_icmp
-from telemeta.models.location import LocationRelation, Location
-from telemeta.models.system import Revision
-from telemeta.models.query import *
-from telemeta.models.instrument import *
-from telemeta.models.enum import *
-from telemeta.models.language import *
-from telemeta.models.format import *
-from telemeta.util.kdenlive.session import *
-from django.db import models
-from django.db.models import URLField
-from django.conf import settings
-from django.core.urlresolvers import reverse, reverse_lazy
-from django.contrib.sites.models import Site
-from django.forms.models import model_to_dict
-
-
-strict_code = getattr(settings, 'TELEMETA_STRICT_CODE', False)
-
-# Special code regex of collections for the branch
-collection_published_code_regex = getattr(settings, 'COLLECTION_PUBLISHED_CODE_REGEX', '[A-Za-z0-9._-]*')
-collection_unpublished_code_regex = getattr(settings, 'COLLECTION_UNPUBLISHED_CODE_REGEX', '[A-Za-z0-9._-]*')
-
-# CREM
-#collection_published_code_regex = 'CNRSMH_E_[0-9]{4}(?:_[0-9]{3}){2}'
-#collection_unpublished_code_regex = 'CNRSMH_I_[0-9]{4}_[0-9]{3}'
-
-collection_code_regex = '(?:%s|%s)' % (collection_published_code_regex,
- collection_unpublished_code_regex)
-
-item_published_code_regex = getattr(settings, 'ITEM_PUBLISHED_CODE_REGEX', '[A-Za-z0-9._-]*')
-item_unpublished_code_regex = getattr(settings, 'ITEM_UNPUBLISHED_CODE_REGEX', '[A-Za-z0-9._-]*')
-
-# CREM
-# item_published_code_regex = collection_published_code_regex + '(?:_[0-9]{2,3}){1,2}'
-# item_unpublished_code_regex = collection_unpublished_code_regex + '_[0-9]{2,3}(?:_[0-9]{2,3}){0,2}'
-
-item_code_regex = '(?:%s|%s)' % (item_published_code_regex, item_unpublished_code_regex)
-
-PUBLIC_ACCESS_CHOICES = (('none', _('none')), ('metadata', _('metadata')),
- ('mixed', _('mixed')), ('full', _('full')))
-
-ITEM_PUBLIC_ACCESS_CHOICES = (('none', _('none')), ('metadata', _('metadata')),
- ('full', _('full')))
-
-ITEM_TRANSODING_STATUS = ((0, _('broken')), (1, _('pending')), (2, _('processing')),
- (3, _('done')), (5, _('ready')))
-
-mimetypes.add_type('video/webm','.webm')
-
-app_name = 'telemeta'
-
-
-def get_random_hash():
- hash = random.getrandbits(64)
- return "%016x" % hash
-
-def get_full_url(path):
- return 'http://' + Site.objects.get_current().domain + path
-
-
-class MediaResource(ModelCore):
- "Base class of all media objects"
-
- def public_access_label(self):
- if self.public_access == 'metadata':
- return _('Metadata only')
- elif self.public_access == 'full':
- return _('Sound and metadata')
-
- return _('Private data')
- public_access_label.verbose_name = _('access type')
-
- def set_revision(self, user):
- "Save a media object and add a revision"
- Revision.touch(self, user)
-
- 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
-
-
-class MediaBaseResource(MediaResource):
- "Describe a media base resource"
-
- title = CharField(_('title'), required=True)
- description = CharField(_('description_old'))
- descriptions = TextField(_('description'))
- code = CharField(_('code'), unique=True, required=True)
- public_access = CharField(_('public access'), choices=PUBLIC_ACCESS_CHOICES, max_length=16, default="metadata")
-
- def __unicode__(self):
- return self.code
-
- @property
- def public_id(self):
- return self.code
-
- def save(self, force_insert=False, force_update=False, user=None, code=None):
- super(MediaBaseResource, self).save(force_insert, force_update)
-
- def get_fields(self):
- return self._meta.fields
-
- class Meta(MetaCore):
- abstract = True
-
-
-class MediaRelated(MediaResource):
- "Related media"
-
- element_type = 'media'
-
- title = CharField(_('title'))
- date = DateTimeField(_('date'), auto_now=True)
- description = TextField(_('description'))
- mime_type = CharField(_('mime_type'))
- url = CharField(_('url'), max_length=500)
- credits = CharField(_('credits'))
- file = FileField(_('file'), upload_to='items/%Y/%m/%d', db_column="filename", max_length=255)
-
- def is_image(self):
- is_url_image = False
- if self.url:
- url_types = ['.png', '.jpg', '.gif', '.jpeg']
- for type in url_types:
- if type in self.url or type.upper() in self.url:
- is_url_image = True
- return 'image' in self.mime_type or is_url_image
-
- def save(self, force_insert=False, force_update=False, author=None):
- super(MediaRelated, self).save(force_insert, force_update)
-
- def set_mime_type(self):
- if self.file:
- self.mime_type = mimetypes.guess_type(self.file.path)[0]
-
- def is_kdenlive_session(self):
- if self.file:
- return '.kdenlive' in self.file.path
- else:
- return False
-
- def __unicode__(self):
- if self.title and not re.match('^ *N *$', self.title):
- title = self.title
- else:
- title = unicode(self.item)
- return title
-
- class Meta:
- abstract = True
-
-
-class MediaCollection(MediaResource):
- "Describe a collection of items"
-
- element_type = 'collection'
-
- def is_valid_collection_code(value):
- "Check if the collection code is well formed"
- regex = '^' + collection_code_regex + '$'
- if not re.match(regex, value):
- raise ValidationError(u'%s is not a valid collection code' % value)
-
- # General informations
- title = CharField(_('title'), required=True)
- alt_title = CharField(_('original title / translation'))
- creator = CharField(_('depositor / contributor'), help_text=_('First name, Last name ; First name, Last name'))
- description = TextField(_('description'))
- recording_context = WeakForeignKey('RecordingContext', related_name="collections", verbose_name=_('recording context'))
- recorded_from_year = IntegerField(_('recording year (from)'), help_text=_('YYYY'))
- recorded_to_year = IntegerField(_('recording year (until)'), help_text=_('YYYY'))
- year_published = IntegerField(_('year published'), help_text=_('YYYY'))
- public_access = CharField(_('access type'), choices=PUBLIC_ACCESS_CHOICES, max_length=16, default="metadata")
-
- # Geographic and cultural informations
- # See "countries" and "ethnic_groups" methods below
-
- # Legal notices
- collector = CharField(_('recordist'), help_text=_('First name, Last name ; First name, Last name'))
- publisher = WeakForeignKey('Publisher', related_name="collections", verbose_name=_('publisher'))
- publisher_collection = WeakForeignKey('PublisherCollection', related_name="collections", verbose_name=_('publisher collection'))
- publisher_serial = CharField(_('publisher serial number'))
- booklet_author = CharField(_('booklet author'), blank=True)
- reference = CharField(_('publisher reference'))
- external_references = TextField(_('bibliographic references'))
-
- auto_period_access = BooleanField(_('automatic access after a rolling period'), default=True)
- legal_rights = WeakForeignKey('LegalRight', related_name="collections", verbose_name=_('legal rights'))
-
- # Archiving data
- code = CharField(_('code'), unique=True, required=True, validators=[is_valid_collection_code])
- old_code = CharField(_('old code'), unique=False, null=True, blank=True)
- acquisition_mode = WeakForeignKey('AcquisitionMode', related_name="collections", verbose_name=_('mode of acquisition'))
- cnrs_contributor = CharField(_('CNRS depositor'))
- copy_type = WeakForeignKey('CopyType', related_name="collections", verbose_name=_('copy type'))
- metadata_author = WeakForeignKey('MetadataAuthor', related_name="collections", verbose_name=_('record author'))
- booklet_description = TextField(_('related documentation'))
- publishing_status = WeakForeignKey('PublishingStatus', related_name="collections", verbose_name=_('secondary edition'))
- status = WeakForeignKey('Status', related_name="collections", verbose_name=_('collection status'))
- alt_copies = TextField(_('copies'))
- comment = TextField(_('comment'))
- metadata_writer = WeakForeignKey('MetadataWriter', related_name="collections", verbose_name=_('record writer'))
- archiver_notes = TextField(_('archiver notes'))
- items_done = CharField(_('items finished'))
- collector_is_creator = BooleanField(_('recordist identical to depositor'))
- is_published = BooleanField(_('published'))
- conservation_site = CharField(_('conservation site'))
-
- # Technical data
- media_type = WeakForeignKey('MediaType', related_name="collections", verbose_name=_('media type'))
- approx_duration = DurationField(_('estimated duration'), help_text='hh:mm:ss')
- physical_items_num = IntegerField(_('number of components (medium / piece)'))
- original_format = WeakForeignKey('OriginalFormat', related_name="collections", verbose_name=_('original format'))
- physical_format = WeakForeignKey('PhysicalFormat', related_name="collections", verbose_name=_('archive format'))
- ad_conversion = WeakForeignKey('AdConversion', related_name='collections', verbose_name=_('digitization'))
-
- # No more used old fields
- alt_ids = CharField(_('copies (obsolete field)'))
- travail = CharField(_('archiver notes (obsolete field)'))
-
- # All
- objects = MediaCollectionManager()
-
- exclude = ['alt_ids', 'travail']
-
- class Meta(MetaCore):
- db_table = 'media_collections'
- ordering = ['code']
- verbose_name = _('collection')
-
- def __unicode__(self):
- return self.code
-
- def save(self, force_insert=False, force_update=False, user=None, code=None):
- super(MediaCollection, self).save(force_insert, force_update)
-
- @property
- def public_id(self):
- return self.code
-
- @property
- 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 __name_cmp(self, obj1, obj2):
- return unaccent_icmp(obj1.name, obj2.name)
-
- def countries(self):
- "Return the countries of the items"
- countries = []
- for item in self.items.filter(location__isnull=False):
- for country in item.location.countries():
- if not country in countries:
- countries.append(country)
- countries.sort(self.__name_cmp)
- return countries
- countries.verbose_name = _("states / nations")
-
- def main_countries(self):
- "Return the main countries of the items (no aliases or ancestors)"
- countries = []
- for item in self.items.filter(location__isnull=False):
- if not item.location in countries:
- countries.append(item.location)
- countries.sort(self.__name_cmp)
- return countries
- main_countries.verbose_name = _("states / nations")
-
- def 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)
-
- cmp = lambda a, b: unaccent_icmp(a.value, b.value)
- groups.sort(cmp)
-
- return groups
- ethnic_groups.verbose_name = _('populations / social groups')
-
- def computed_duration(self):
- duration = Duration()
- for item in self.items.all():
- duration += item.computed_duration()
- return duration
- computed_duration.verbose_name = _('computed duration')
-
- def computed_size(self):
- "Return the total size of a collection"
- size = 0
- for item in self.items.all():
- size += item.size()
- return size
- computed_size.verbose_name = _('collection size')
-
- def document_status(self):
- if '_I_' in self.public_id:
- return ugettext('Unpublished')
- elif '_E_' in self.public_id:
- return ugettext('Published')
- else:
- return ''
-
- def get_url(self):
- return get_full_url(reverse('telemeta-collection-detail', kwargs={'public_id':self.pk}))
-
- def to_dict_with_more(self):
- # metadata = model_to_dict(self, fields=[], exclude=self.exclude)
- metadata = self.to_dict()
- for key in self.exclude:
- if key in metadata.keys():
- del metadata[key]
-
- metadata['url'] = get_full_url(reverse('telemeta-collection-detail', kwargs={'public_id':self.pk}))
- metadata['doc_status'] = self.document_status()
- metadata['countries'] = ';'.join([location.name for location in self.main_countries()])
- metadata['ethnic_groups'] = ';'.join([group.value for group in self.ethnic_groups()])
- metadata['last_modification_date'] = unicode(self.get_revision().time)
- metadata['computed_duration'] = unicode(self.computed_duration())
- metadata['computed_size'] = unicode(self.computed_size())
- metadata['number_of_items'] = unicode(self.items.all().count())
-
- i = 0
- for media in self.related.all():
- metadata['related_media_title' + '_' + str(i)] = media.title
- if media.url:
- tag = 'related_media_url' + '_' + str(i)
- metadata[tag] = media.url
- elif media.url:
- metadata[tag] = get_full_url(reverse('telemeta-collection-related',
- kwargs={'public_id': self.public_id, 'media_id': media.id}))
- i += 1
-
- # One ID only
- identifiers = self.identifiers.all()
- if identifiers:
- identifier = identifiers[0]
- metadata['identifier_id'] = identifier.identifier
- metadata['identifier_type'] = identifier.type
- metadata['identifier_date'] = unicode(identifier.date_last)
- metadata['identifier_note'] = identifier.notes
-
- # All IDs
- # i = 0
- # for indentifier in self.identifiers.all():
- # metadata['identifier' + '_' + str(i)] = identifier.identifier
- # metadata['identifier_type' + '_' + str(i)] = identifier.type
- # metadata['identifier_date_last' + '_' + str(i)] = unicode(identifier.date_last)
- # metadata['identifier_notes' + '_' + str(i)] = identifier.notes
- # i += 1
-
- return metadata
-
-
-class MediaCollectionRelated(MediaRelated):
- "Collection related media"
-
- collection = ForeignKey('MediaCollection', related_name="related", verbose_name=_('collection'))
-
- class Meta(MetaCore):
- db_table = 'media_collection_related'
- verbose_name = _('collection related media')
- verbose_name_plural = _('collection related media')
-
-
-class MediaItem(MediaResource):
- "Describe an item"
-
- element_type = 'item'
-
- # Main Informations
- title = CharField(_('title'))
- alt_title = CharField(_('original title / translation'))
- collector = CharField(_('collector'), help_text=_('First name, Last name ; First name, Last name'))
- collection = ForeignKey('MediaCollection', related_name="items", verbose_name=_('collection'))
- recorded_from_date = DateField(_('recording date (from)'), help_text=_('YYYY-MM-DD'))
- recorded_to_date = DateField(_('recording date (until)'), help_text=_('YYYY-MM-DD'))
- public_access = CharField(_('access type'), choices=ITEM_PUBLIC_ACCESS_CHOICES, max_length=16, default="metadata")
-
- # Geographic and cultural informations
- location = WeakForeignKey('Location', verbose_name=_('location'))
- location_comment = CharField(_('location details'))
- cultural_area = CharField(_('cultural area'))
- language = CharField(_('language'))
- language_iso = ForeignKey('Language', related_name="items", verbose_name=_('Language (ISO norm)'), blank=True, null=True, on_delete=models.SET_NULL)
- ethnic_group = WeakForeignKey('EthnicGroup', related_name="items", verbose_name=_('population / social group'))
- context_comment = TextField(_('Ethnographic context'))
-
- # Musical informations
- moda_execut = CharField(_('implementing rules'))
- vernacular_style = WeakForeignKey('VernacularStyle', related_name="items", verbose_name=_('vernacular style'))
- generic_style = WeakForeignKey('GenericStyle', related_name="items", verbose_name=_('generic style'))
- author = CharField(_('author / compositor'), help_text=_('First name, Last name ; First name, Last name'))
-
- # Legal mentions
- organization = WeakForeignKey('Organization', verbose_name=_('organization'))
- depositor = CharField(_('depositor'))
- rights = WeakForeignKey('Rights', verbose_name=_('rights'))
-
- # Archiving data
- code = CharField(_('code'), unique=True, blank=True, required=True, help_text=_('CollectionCode_ItemCode'))
- old_code = CharField(_('original code'), unique=False, blank=True)
- track = CharField(_('item number'))
- collector_selection = CharField(_('collector selection'))
- collector_from_collection = BooleanField(_('collector as in collection'))
- creator_reference = CharField(_('creator reference'))
- external_references = TextField(_('published references'))
- auto_period_access = BooleanField(_('automatic access after a rolling period'), default=True)
- comment = TextField(_('remarks'))
-
- # Technical data
- media_type = WeakForeignKey('MediaType', related_name="items", verbose_name=_('media type'))
- approx_duration = DurationField(_('approximative duration'), blank=True, help_text=_('hh:mm:ss'))
- mimetype = CharField(_('mime type'), max_length=255, blank=True)
- file = FileField(_('file'), upload_to='items/%Y/%m/%d', db_column="filename", max_length=1024)
- url = URLField(_('URL'), max_length=512, blank=True)
-
- # LAM
- recordist = CharField(_('recordist'))
- digitalist = CharField(_('digitalist'))
- digitization_date = DateField(_('digitization date'))
- publishing_date = DateField(_('publishing date'))
- scientist = CharField(_('scientist'), help_text=_('First name, Last name ; First name, Last name'))
- topic = WeakForeignKey('Topic', verbose_name=_('topic'))
- summary = TextField(_('summary'))
- contributor = CharField(_('contributor'))
-
- # Manager
- objects = MediaItemManager()
-
- exclude = ['copied_from_item', 'mimetype', 'url',
- 'organization', 'depositor', 'rights',
- 'recordist', 'digitalist', 'digitization_date',
- 'publishing_date', 'scientist', 'topic',
- 'summary', 'contributor', ]
-
- def keywords(self):
- return ContextKeyword.objects.filter(item_relations__item = self)
- keywords.verbose_name = _('keywords')
-
- @property
- def public_id(self):
- if self.code:
- return self.code
- return str(self.id)
-
- @property
- def mime_type(self):
- if not self.mimetype:
- if self.file:
- if os.path.exists(self.file.path):
- self.mimetype = mimetypes.guess_type(self.file.path)[0]
- self.save()
- return self.mimetype
- else:
- return 'none'
- else:
- return 'none'
- else:
- return _('none')
-
-
- class Meta(MetaCore):
- db_table = 'media_items'
- permissions = (("can_play_all_items", "Can play all media items"),
- ("can_download_all_items", "Can download all media items"), )
- verbose_name = _('item')
-
- def is_valid_code(self, code):
- "Check if the item code is well formed"
- if not re.match('^' + self.collection.code, self.code):
- return False
- if self.collection.is_published:
- regex = '^' + item_published_code_regex + '$'
- else:
- regex = '^' + item_unpublished_code_regex + '$'
- if re.match(regex, code):
- return True
- return False
-
- def clean(self):
- if strict_code:
- if self.code and not self.is_valid_code(self.code):
- raise ValidationError("%s is not a valid item code for collection %s"
- % (self.code, self.collection.code))
-
- def save(self, force_insert=False, force_update=False):
- super(MediaItem, self).save(force_insert, force_update)
-
- def computed_duration(self):
- "Tell the length in seconds of this item media data"
- return self.approx_duration
-
- computed_duration.verbose_name = _('computed duration')
-
- def __unicode__(self):
- if self.title and not re.match('^ *N *$', self.title):
- title = self.title
- else:
- title = unicode(self.collection.title)
- if self.track:
- title += ' ' + self.track
- return title
-
- def get_source(self):
- source = None
- if self.file and os.path.exists(self.file.path):
- source = self.file.path
- elif self.url:
- source = self.url
- return source
-
- @property
- def instruments(self):
- "Return the instruments of the item"
- instruments = []
- performances = MediaItemPerformance.objects.filter(media_item=self)
- for performance in performances:
- instrument = performance.instrument
- alias = performance.alias
- if not instrument in instruments:
- instruments.append(instrument)
- if not alias in instruments:
- instruments.append(alias)
-
- instruments.sort(self.__name_cmp)
- return instruments
-
- instruments.verbose_name = _("instruments")
-
- def size(self):
- if self.file and os.path.exists(self.file.path):
- return self.file.size
- else:
- return 0
- size.verbose_name = _('item size')
-
- def get_url(self):
- return get_full_url(reverse('telemeta-item-detail', kwargs={'public_id':self.pk}))
-
- def to_dict_with_more(self):
- # metadata = model_to_dict(self, fields=[], exclude=self.exclude)
- metadata = self.to_dict()
- for key in self.exclude:
- if key in metadata.keys():
- del metadata[key]
-
- metadata['url'] = self.get_url()
- metadata['last_modification_date'] = unicode(self.get_revision().time)
- metadata['collection'] = self.collection.get_url()
-
- keywords = []
- for keyword in self.keywords():
- keywords.append(keyword.value)
- metadata['keywords'] = ';'.join(keywords)
-
- i = 0
- for media in self.related.all():
- if media.title:
- tag = 'related_media_title' + '_' + str(i)
- metadata[tag] = media.title
- else:
- metadata[tag] = ''
- if media.url:
- tag = 'related_media_url' + '_' + str(i)
- metadata[tag] = media.url
- elif media.url:
- metadata[tag] = get_full_url(reverse('telemeta-collection-related',
- kwargs={'public_id': self.public_id, 'media_id': media.id}))
- i += 1
-
-
- instruments = []
- instrument_vernacular_names = []
- performers = []
-
- for performance in self.performances.all():
- if performance.instrument:
- instruments.append(performance.instrument.name)
- if performance.alias:
- instrument_vernacular_names.append(performance.alias.name)
- if performance.musicians:
- performers.append(performance.musicians.replace(' et ', ';'))
-
- metadata['instruments'] = ';'.join(instruments)
- metadata['instrument_vernacular_names'] = ';'.join(instrument_vernacular_names)
- metadata['performers'] = ';'.join(performers)
-
- i = 0
- for indentifier in self.identifiers.all():
- metadata['identifier' + '_' + str(i)] = identifier.identifier
- metadata['identifier_type' + '_' + str(i)] = identifier.type
- metadata['identifier_date_last' + '_' + str(i)] = unicode(identifier.date_last)
- metadata['identifier_notes' + '_' + str(i)] = identifier.notes
- i += 1
-
- analyzers = ['channels', 'samplerate', 'duration', 'resolution', 'mime_type']
- for analyzer_id in analyzers:
- analysis = MediaItemAnalysis.objects.filter(item=self, analyzer_id=analyzer_id)
- if analysis:
- metadata[analyzer_id] = analysis[0].value
-
- metadata['file_size'] = unicode(self.size())
- metadata['thumbnail'] = get_full_url(reverse('telemeta-item-visualize',
- kwargs={'public_id': self.public_id,
- 'grapher_id': 'waveform_centroid',
- 'width': 346,
- 'height': 130}))
-
- # One ID only
- identifiers = self.identifiers.all()
- if identifiers:
- identifier = identifiers[0]
- metadata['identifier_id'] = identifier.identifier
- metadata['identifier_type'] = identifier.type
- metadata['identifier_date'] = unicode(identifier.date_last)
- metadata['identifier_note'] = identifier.notes
-
- return metadata
-
-
-class MediaItemRelated(MediaRelated):
- "Item related media"
-
- item = ForeignKey('MediaItem', related_name="related", verbose_name=_('item'))
-
- def save(self, force_insert=False, force_update=False, using=False):
- super(MediaItemRelated, self).save(force_insert, force_update)
-
- def parse_markers(self, **kwargs):
- # Parse KDEnLive session
- if self.file:
- if self.is_kdenlive_session():
- session = KDEnLiveSession(self.file.path)
- markers = session.markers(**kwargs)
- for marker in markers:
- m = MediaItemMarker(item=self.item)
- m.public_id = get_random_hash()
- m.time = float(marker['time'])
- m.title = marker['comment']
- m.save()
- return markers
-
- class Meta(MetaCore):
- db_table = 'media_item_related'
- verbose_name = _('item related media')
- verbose_name_plural = _('item related media')
-
-
-class MediaItemKeyword(ModelCore):
- "Item keyword"
- item = ForeignKey('MediaItem', verbose_name=_('item'), related_name="keyword_relations")
- keyword = ForeignKey('ContextKeyword', verbose_name=_('keyword'), related_name="item_relations")
-
- class Meta(MetaCore):
- db_table = 'media_item_keywords'
- unique_together = (('item', 'keyword'),)
-
-
-class MediaItemPerformance(ModelCore):
- "Item performance"
- media_item = ForeignKey('MediaItem', related_name="performances", verbose_name=_('item'))
- instrument = WeakForeignKey('Instrument', related_name="performances", verbose_name=_('composition'))
- alias = WeakForeignKey('InstrumentAlias', related_name="performances", verbose_name=_('vernacular name'))
- instruments_num = CharField(_('number'))
- musicians = CharField(_('interprets'))
-
- class Meta(MetaCore):
- db_table = 'media_item_performances'
-
-
-class MediaItemAnalysis(ModelCore):
- "Item analysis result computed by TimeSide"
-
- element_type = 'analysis'
- item = ForeignKey('MediaItem', related_name="analysis", verbose_name=_('item'))
- analyzer_id = CharField(_('id'), required=True)
- name = CharField(_('name'))
- value = CharField(_('value'))
- unit = CharField(_('unit'))
-
- class Meta(MetaCore):
- db_table = 'media_analysis'
- ordering = ['name']
-
- def to_dict(self):
- if self.analyzer_id == 'duration':
- if '.' in self.value:
- value = self.value.split('.')
- self.value = '.'.join([value[0], value[1][:2]])
- return {'id': self.analyzer_id, 'name': self.name, 'value': self.value, 'unit': self.unit}
-
-
-class MediaPart(MediaResource):
- "Describe an item part"
- element_type = 'part'
- item = ForeignKey('MediaItem', related_name="parts", verbose_name=_('item'))
- title = CharField(_('title'), required=True)
- start = FloatField(_('start'), required=True)
- end = FloatField(_('end'), required=True)
-
- class Meta(MetaCore):
- db_table = 'media_parts'
- verbose_name = _('item part')
-
- def __unicode__(self):
- return self.title
-
-class Playlist(ModelCore):
- "Item, collection or marker playlist"
- element_type = 'playlist'
- public_id = CharField(_('public_id'), required=True)
- author = ForeignKey(User, related_name="playlists", db_column="author")
- title = CharField(_('title'), required=True)
- description = TextField(_('description'))
-
- class Meta(MetaCore):
- db_table = 'playlists'
-
- def __unicode__(self):
- return self.title
-
-
-class PlaylistResource(ModelCore):
- "Playlist components"
- RESOURCE_TYPE_CHOICES = (('item', 'item'), ('collection', 'collection'),
- ('marker', 'marker'), ('fonds', 'fonds'), ('corpus', 'corpus'))
- element_type = 'playlist_resource'
- public_id = CharField(_('public_id'), required=True)
- playlist = ForeignKey('Playlist', related_name="resources", verbose_name=_('playlist'))
- resource_type = CharField(_('resource_type'), choices=RESOURCE_TYPE_CHOICES, required=True)
- resource_id = CharField(_('resource_id'), required=True)
-
- class Meta(MetaCore):
- db_table = 'playlist_resources'
-
-
-class MediaItemMarker(MediaResource):
- "2D marker object : text value vs. time (in seconds)"
-
- element_type = 'marker'
-
- item = ForeignKey('MediaItem', related_name="markers", verbose_name=_('item'))
- public_id = CharField(_('public_id'), required=True)
- time = FloatField(_('time (s)'))
- title = CharField(_('title'))
- date = DateTimeField(_('date'), auto_now=True)
- description = TextField(_('description'))
- author = ForeignKey(User, related_name="markers", verbose_name=_('author'),
- blank=True, null=True)
-
- class Meta(MetaCore):
- db_table = 'media_markers'
- ordering = ['time']
-
- def __unicode__(self):
- if self.title:
- return self.title
- else:
- return self.public_id
-
-
-class MediaItemTranscoded(MediaResource):
- "Item file transcoded"
-
- element_type = 'transcoded item'
-
- item = models.ForeignKey('MediaItem', related_name="transcoded", verbose_name=_('item'))
- mimetype = models.CharField(_('mime_type'), max_length=255, blank=True)
- date_added = DateTimeField(_('date'), auto_now_add=True)
- status = models.IntegerField(_('status'), choices=ITEM_TRANSODING_STATUS, default=1)
- file = models.FileField(_('file'), upload_to='items/%Y/%m/%d', max_length=1024, blank=True)
-
- @property
- def mime_type(self):
- if not self.mimetype:
- if self.file:
- if os.path.exists(self.file.path):
- self.mimetype = mimetypes.guess_type(self.file.path)[0]
- self.save()
- return self.mimetype
- else:
- return 'none'
- else:
- return 'none'
- else:
- return self.mimetype
-
- def __unicode__(self):
- if self.item.title:
- return self.item.title + ' - ' + self.mime_type
- else:
- return self.item.public_id + ' - ' + self.mime_type
-
- class Meta(MetaCore):
- db_table = app_name + '_media_transcoded'
-
-
-class MediaItemTranscodingFlag(ModelCore):
- "Item flag to know if the MediaItem has been transcoded to a given format"
-
- item = ForeignKey('MediaItem', related_name="transcoding", verbose_name=_('item'))
- mime_type = CharField(_('mime_type'), required=True)
- date = DateTimeField(_('date'), auto_now=True)
- value = BooleanField(_('transcoded'))
-
- class Meta(MetaCore):
- db_table = 'media_transcoding'
-
-
-class DublinCoreToFormatMetadata(object):
- """ a mapping class to get item DublinCore metadata dictionaries
- in various audio metadata format (MP3, OGG, etc...)"""
-
- #FIXME: should be given by timeside
- unavailable_extensions = ['wav', 'aiff', 'aif', 'flac', 'webm']
-
- metadata_mapping = {
- 'mp3' : {
- 'title': 'TIT2', #title2
- 'creator': 'TCOM', #composer
- 'creator': 'TPE1', #lead
- 'identifier': 'UFID', #unique ID
- 'relation': 'TALB', #album
- 'type': 'TCON', #genre
- 'publisher': 'TPUB', #publisher
- 'date': 'TDRC', #year
-# 'coverage': 'COMM', #comment
- },
- 'ogg': {
- 'creator': 'artist',
- 'relation': 'album',
- 'all': 'all',
- },
- 'flac': {
- 'creator': 'artist',
- 'relation': 'album',
- 'all': 'all',
- },
- 'wav': {
- 'creator': 'artist',
- 'relation': 'album',
- 'all': 'all',
- },
- 'webm': {
- 'creator': 'artist',
- 'relation': 'album',
- 'all': 'all',
- },
- }
-
- def __init__(self, format):
- self.format = format
-
- def get_metadata(self, dc_metadata):
- mapp = self.metadata_mapping[self.format]
- metadata = {}
- keys_done = []
- for data in dc_metadata:
- key = data[0]
- value = data[1].encode('utf-8')
- if value:
- if key == 'date':
- value = value.split(';')[0].split('=')
- if len(value) > 1:
- value = value[1]
- value = value.split('-')[0]
- else:
- value = value[0].split('-')[0]
- if key in mapp:
- metadata[mapp[key]] = value.decode('utf-8')
- elif 'all' in mapp.keys():
- metadata[key] = value.decode('utf-8')
- keys_done.append(key)
- return metadata
-
-
-class MediaCorpus(MediaBaseResource):
- "Describe a corpus"
-
- element_type = 'corpus'
- children_type = 'collections'
-
- children = models.ManyToManyField(MediaCollection, related_name="corpus",
- verbose_name=_('collections'), blank=True, null=True)
- recorded_from_year = IntegerField(_('recording year (from)'), help_text=_('YYYY'))
- recorded_to_year = IntegerField(_('recording year (until)'), help_text=_('YYYY'))
-
- objects = MediaCorpusManager()
-
- @property
- def public_id(self):
- return self.code
-
- @property
- def has_mediafile(self):
- for child in self.children.all():
- if child.has_mediafile:
- return True
- return False
-
- def computed_duration(self):
- duration = Duration()
- for child in self.children.all():
- duration += child.computed_duration()
- return duration
- computed_duration.verbose_name = _('total available duration')
-
- class Meta(MetaCore):
- db_table = 'media_corpus'
- verbose_name = _('corpus')
- verbose_name_plural = _('corpus')
- ordering = ['code']
-
-
-class MediaFonds(MediaBaseResource):
- "Describe fonds"
-
- element_type = 'fonds'
- children_type = 'corpus'
-
- children = models.ManyToManyField(MediaCorpus, related_name="fonds",
- verbose_name=_('corpus'), blank=True, null=True)
-
- objects = MediaFondsManager()
-
- @property
- def public_id(self):
- return self.code
-
- @property
- def has_mediafile(self):
- for child in self.children.all():
- if child.has_mediafile:
- return True
- return False
-
- def computed_duration(self):
- duration = Duration()
- for child in self.children.all():
- duration += child.computed_duration()
- return duration
- computed_duration.verbose_name = _('total available duration')
-
- class Meta(MetaCore):
- db_table = 'media_fonds'
- verbose_name = _('fonds')
- verbose_name_plural = _('fonds')
- ordering = ['code']
-
-
-class MediaCorpusRelated(MediaRelated):
- "Corpus related media"
-
- resource = ForeignKey(MediaCorpus, related_name="related", verbose_name=_('corpus'))
-
- class Meta(MetaCore):
- db_table = 'media_corpus_related'
- verbose_name = _('corpus related media')
- verbose_name_plural = _('corpus related media')
-
-
-class MediaFondsRelated(MediaRelated):
- "Fonds related media"
-
- resource = ForeignKey(MediaFonds, related_name="related", verbose_name=_('fonds'))
-
- class Meta(MetaCore):
- db_table = 'media_fonds_related'
- verbose_name = _('fonds related media')
- verbose_name_plural = _('fonds related media')
-
-
-class Identifier(ModelCore):
- """Resource identifier"""
-
- identifier = CharField(_('identifier'), max_length=255, blank=True, unique=True)
- type = WeakForeignKey('IdentifierType', verbose_name=_('type'))
- date_add = DateTimeField(_('date added'), auto_now_add=True)
- date_first = DateTimeField(_('date of first attribution'))
- date_last = DateTimeField(_('date of last attribution'))
- date_modified = DateTimeField(_('date modified'), auto_now=True)
- notes = TextField(_('notes'))
-
- class Meta(MetaCore):
- abstract = True
- ordering = ['-date_last']
-
-
-class MediaItemIdentifier(Identifier):
- """Item identifier"""
-
- item = ForeignKey(MediaItem, related_name="identifiers", verbose_name=_('item'))
-
- class Meta(MetaCore):
- db_table = 'media_item_identifier'
- verbose_name = _('item identifier')
- verbose_name_plural = _('item identifiers')
- unique_together = ('identifier', 'item')
-
-
-class MediaCollectionIdentifier(Identifier):
- """Collection identifier"""
-
- collection = ForeignKey(MediaCollection, related_name="identifiers", verbose_name=_('collection'))
-
- class Meta(MetaCore):
- db_table = 'media_collection_identifier'
- verbose_name = _('collection identifier')
- verbose_name_plural = _('collection identifiers')
- unique_together = ('identifier', 'collection')
-
--- /dev/null
+# -*- coding: utf-8 -*-
+# Copyright (C) 2010 Samalyse SARL
+# Copyright (C) 2010-2014 Parisson SARL
+
+# This software is a computer program whose purpose is to backup, analyse,
+# transcode and stream any audio content with its metadata over a web frontend.
+
+# This software is governed by the CeCILL license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+
+# As a counterpart to the access to the source code and rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using, modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL license and that you accept its terms.
+#
+# Authors: Olivier Guilyardi <olivier@samalyse.com>
+# David LIPSZYC <davidlipszyc@gmail.com>
+# Guillaume Pellerin <yomguy@parisson.com>
+
+
+from __future__ import division
+from django.utils.translation import ugettext_lazy as _
+from telemeta.models.core import *
+
+
+class Playlist(ModelCore):
+ "Item, collection or marker playlist"
+ element_type = 'playlist'
+ public_id = CharField(_('public_id'), required=True)
+ author = ForeignKey(User, related_name="playlists", db_column="author")
+ title = CharField(_('title'), required=True)
+ description = TextField(_('description'))
+
+ class Meta(MetaCore):
+ db_table = 'playlists'
+
+ def __unicode__(self):
+ return self.title
+
+
+class PlaylistResource(ModelCore):
+ "Playlist components"
+ RESOURCE_TYPE_CHOICES = (('item', 'item'), ('collection', 'collection'),
+ ('marker', 'marker'), ('fonds', 'fonds'), ('corpus', 'corpus'))
+ element_type = 'playlist_resource'
+ public_id = CharField(_('public_id'), required=True)
+ playlist = ForeignKey('Playlist', related_name="resources", verbose_name=_('playlist'))
+ resource_type = CharField(_('resource_type'), choices=RESOURCE_TYPE_CHOICES, required=True)
+ resource_id = CharField(_('resource_id'), required=True)
+
+ class Meta(MetaCore):
+ db_table = 'playlist_resources'
+
+
--- /dev/null
+# -*- coding: utf-8 -*-
+# Copyright (C) 2010 Samalyse SARL
+# Copyright (C) 2010-2014 Parisson SARL
+
+# This software is a computer program whose purpose is to backup, analyse,
+# transcode and stream any audio content with its metadata over a web frontend.
+
+# This software is governed by the CeCILL license under French law and
+# abiding by the rules of distribution of free software. You can use,
+# modify and/ or redistribute the software under the terms of the CeCILL
+# license as circulated by CEA, CNRS and INRIA at the following URL
+# "http://www.cecill.info".
+
+# As a counterpart to the access to the source code and rights to copy,
+# modify and redistribute granted by the license, users are provided only
+# with a limited warranty and the software's author, the holder of the
+# economic rights, and the successive licensors have only limited
+# liability.
+
+# In this respect, the user's attention is drawn to the risks associated
+# with loading, using, modifying and/or developing or reproducing the
+# software by the user in light of its specific status of free software,
+# that may mean that it is complicated to manipulate, and that also
+# therefore means that it is reserved for developers and experienced
+# professionals having in-depth computer knowledge. Users are therefore
+# encouraged to load and test the software's suitability as regards their
+# requirements in conditions enabling the security of their systems and/or
+# data to be ensured and, more generally, to use and operate it in the
+# same conditions as regards security.
+
+# The fact that you are presently reading this means that you have had
+# knowledge of the CeCILL license and that you accept its terms.
+#
+# Authors: Olivier Guilyardi <olivier@samalyse.com>
+# David LIPSZYC <davidlipszyc@gmail.com>
+# Guillaume Pellerin <yomguy@parisson.com>
+
+
+from django.utils.translation import ugettext_lazy as _
+from telemeta.models.core import *
+
+
+class MediaResource(ModelCore):
+ "Base class of all media objects"
+
+ def public_access_label(self):
+ if self.public_access == 'metadata':
+ return _('Metadata only')
+ elif self.public_access == 'full':
+ return _('Sound and metadata')
+
+ return _('Private data')
+ public_access_label.verbose_name = _('access type')
+
+ def set_revision(self, user):
+ "Save a media object and add a revision"
+ Revision.touch(self, user)
+
+ 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
+
+
+class MediaBaseResource(MediaResource):
+ "Describe a media base resource"
+
+ title = CharField(_('title'), required=True)
+ description = CharField(_('description_old'))
+ descriptions = TextField(_('description'))
+ code = CharField(_('code'), unique=True, required=True)
+ public_access = CharField(_('public access'), choices=PUBLIC_ACCESS_CHOICES, max_length=16, default="metadata")
+
+ def __unicode__(self):
+ return self.code
+
+ @property
+ def public_id(self):
+ return self.code
+
+ def save(self, force_insert=False, force_update=False, user=None, code=None):
+ super(MediaBaseResource, self).save(force_insert, force_update)
+
+ def get_fields(self):
+ return self._meta.fields
+
+ class Meta(MetaCore):
+ abstract = True
+
+
+class MediaRelated(MediaResource):
+ "Related media"
+
+ element_type = 'media'
+
+ title = CharField(_('title'))
+ date = DateTimeField(_('date'), auto_now=True)
+ description = TextField(_('description'))
+ mime_type = CharField(_('mime_type'))
+ url = CharField(_('url'), max_length=500)
+ credits = CharField(_('credits'))
+ file = FileField(_('file'), upload_to='items/%Y/%m/%d', db_column="filename", max_length=255)
+
+ def is_image(self):
+ is_url_image = False
+ if self.url:
+ url_types = ['.png', '.jpg', '.gif', '.jpeg']
+ for type in url_types:
+ if type in self.url or type.upper() in self.url:
+ is_url_image = True
+ return 'image' in self.mime_type or is_url_image
+
+ def save(self, force_insert=False, force_update=False, author=None):
+ super(MediaRelated, self).save(force_insert, force_update)
+
+ def set_mime_type(self):
+ if self.file:
+ self.mime_type = mimetypes.guess_type(self.file.path)[0]
+
+ def is_kdenlive_session(self):
+ if self.file:
+ return '.kdenlive' in self.file.path
+ else:
+ return False
+
+ def __unicode__(self):
+ if self.title and not re.match('^ *N *$', self.title):
+ title = self.title
+ else:
+ title = unicode(self.item)
+ return title
+
+ class Meta:
+ abstract = True
+
+
--- /dev/null
+# -*- coding: utf-8 -*-
+# Copyright (C) 2010-2014 Parisson SARL
+
+
+def get_random_hash():
+ hash = random.getrandbits(64)
+ return "%016x" % hash
+
+
+def get_full_url(path):
+ return 'http://' + Site.objects.get_current().domain + path
+
+
+def word_search_q(field, pattern):
+ words = re.split("[ .*-]+", pattern)
+ q = Q()
+ for w in words:
+ if len(w) >= 3:
+ kwargs = {field + '__icontains': w}
+ q &= Q(**kwargs)
+
+ return q