Changeset 622588f
- Timestamp:
- 02/10/10 17:42:16 (3 years ago)
- Branches:
- master, crem, crem2, dev, diadems, forma, generic, instru_search, lam, nlivemulti, production, release/1.4.4, security, social, storage, test, video
- Children:
- c1a2c1c
- Parents:
- 6a4a2b4
- git-author:
- olivier <> (02/10/10 17:42:16)
- git-committer:
- olivier <> (02/10/10 17:42:16)
- Location:
- telemeta/models
- Files:
-
- 4 added
- 2 edited
- 2 moved
-
__init__.py (modified) (1 diff)
-
core.py (modified) (6 diffs)
-
enum.py (added)
-
instrument.py (added)
-
location.py (added)
-
media.py (moved) (moved from telemeta/models/crem.py) (6 diffs, 1 prop)
-
query.py (moved) (moved from telemeta/models/cremquery.py) (8 diffs)
-
system.py (added)
Legend:
- Unmodified
- Added
- Removed
-
telemeta/models/__init__.py
re1d0274 r622588f 33 33 # Author: Olivier Guilyardi <olivier@samalyse.com> 34 34 35 from telemeta.models.crem import * 35 from telemeta.models.media import * 36 from telemeta.models.location import * 37 from telemeta.models.instrument import * 38 from telemeta.models.enum import * 39 from telemeta.models.system import * 36 40 #MediaCollection, MediaItem, MediaPart, Revision, \ 37 41 # PhysicalFormat, PublishingStatus -
telemeta/models/core.py
r39e0100 r622588f 1 1 # -*- coding: utf-8 -*- 2 # 2 3 # Copyright (C) 2007-2010 Samalyse SARL 3 4 # 4 5 # This software is a computer program whose purpose is to backup, analyse, 5 6 # transcode and stream any audio content with its metadata over a web frontend. 6 7 # 7 8 # This software is governed by the CeCILL license under French law and 8 9 # abiding by the rules of distribution of free software. You can use, … … 10 11 # license as circulated by CEA, CNRS and INRIA at the following URL 11 12 # "http://www.cecill.info". 12 13 # 13 14 # As a counterpart to the access to the source code and rights to copy, 14 15 # modify and redistribute granted by the license, users are provided only … … 16 17 # economic rights, and the successive licensors have only limited 17 18 # liability. 18 19 # 19 20 # In this respect, the user's attention is drawn to the risks associated 20 21 # with loading, using, modifying and/or developing or reproducing the … … 27 28 # data to be ensured and, more generally, to use and operate it in the 28 29 # same conditions as regards security. 29 30 # 30 31 # The fact that you are presently reading this means that you have had 31 32 # knowledge of the CeCILL license and that you accept its terms. … … 33 34 # Authors: Olivier Guilyardi <olivier@samalyse.com> 34 35 36 __all__ = ['ModelCore', 'MetaCore', 'DurationField', 'Duration', 'WeakForeignKey', 37 'EnhancedModel', 'CharField', 'TextField', 'IntegerField', 'BooleanField', 38 'DateTimeField', 'FileField', 'ForeignKey', 'FloatField', 'DateField', 39 'RequiredFieldError', 'CoreQuerySet', 'CoreManager'] 40 41 from django.core import exceptions 42 from django import forms 43 from xml.dom.minidom import getDOMImplementation 44 from django.db.models.fields import FieldDoesNotExist 45 from django.db.models import Q 35 46 from django.db import models 36 47 import datetime 37 48 from django.utils.translation import ugettext_lazy as _ 38 49 import re 50 from django.core.exceptions import ObjectDoesNotExist 39 51 40 52 class Duration(object): … … 303 315 super(DateField, self).__init__(*args, **normalize_field(kwargs, '0000-00-00')) 304 316 317 class RequiredFieldError(Exception): 318 def __init__(self, model, field): 319 self.model = model 320 self.field = field 321 super(Exception, self).__init__('%s.%s is required' % (model._meta.object_name, field.name)) 322 323 class ModelCore(EnhancedModel): 324 325 @classmethod 326 def required_fields(cls): 327 required = [] 328 for field in cls._meta.fields: 329 if not field.blank: 330 required.append(field) 331 return required 332 333 def save(self, force_insert=False, force_update=False, using=None): 334 required = self.required_fields() 335 for field in required: 336 if not getattr(self, field.name): 337 raise RequiredFieldError(self, field) 338 super(ModelCore, self).save(force_insert, force_update, using) 339 340 @classmethod 341 def get_dom_name(cls): 342 "Convert the class name to a DOM element name" 343 clsname = cls.__name__ 344 return clsname[0].lower() + clsname[1:] 345 346 @staticmethod 347 def get_dom_field_name(field_name): 348 "Convert the class name to a DOM element name" 349 tokens = field_name.split('_') 350 name = tokens[0] 351 for t in tokens[1:]: 352 name += t[0].upper() + t[1:] 353 return name 354 355 def to_dom(self): 356 "Return the DOM representation of this media object" 357 impl = getDOMImplementation() 358 root = self.get_dom_name() 359 doc = impl.createDocument(None, root, None) 360 top = doc.documentElement 361 top.setAttribute("id", str(self.pk)) 362 fields = self.to_dict() 363 for name, value in fields.iteritems(): 364 element = doc.createElement(self.get_dom_field_name(name)) 365 if isinstance(value, EnhancedModel): 366 element.setAttribute('key', str(value.pk)) 367 value = unicode(value) 368 element.appendChild(doc.createTextNode(value)) 369 top.appendChild(element) 370 return doc 371 372 def to_dict(self): 373 "Return model fields as a dict of name/value pairs" 374 fields_dict = {} 375 for field in self._meta.fields: 376 fields_dict[field.name] = getattr(self, field.name) 377 return fields_dict 378 379 def to_list(self): 380 "Return model fields as a list" 381 fields_list = [] 382 for field in self._meta.fields: 383 fields_list.append({'name': field.name, 'value': getattr(self, field.name)}) 384 return fields_list 385 386 @classmethod 387 def field_label(cls, field_name): 388 try: 389 return cls._meta.get_field(field_name).verbose_name 390 except FieldDoesNotExist: 391 try: 392 return getattr(cls, field_name).verbose_name 393 except AttributeError: 394 return field_name 395 396 class Meta: 397 abstract = True 398 399 class MetaCore: 400 app_label = 'telemeta' 401 402 class CoreQuerySet(EnhancedQuerySet): 403 "Base class for all query sets" 404 405 def none(self): # redundant with none() in recent Django svn 406 "Return an empty result set" 407 return self.extra(where = ["0 = 1"]) 408 409 def word_search_q(self, field, pattern): 410 words = re.split("[ .*-]+", pattern) 411 q = Q() 412 for w in words: 413 if len(w) >= 3: 414 kwargs = {field + '__icontains': w} 415 q &= Q(**kwargs) 416 417 return q 418 419 def word_search(self, field, pattern): 420 return self.filter(self.word_search_q(field, pattern)) 421 422 def _by_change_time(self, type, from_time = None, until_time = None): 423 "Search between two revision dates" 424 where = ["element_type = '%s'" % type] 425 if from_time: 426 where.append("time >= '%s'" % from_time.strftime('%Y-%m-%d %H:%M:%S')) 427 if until_time: 428 where.append("time <= '%s'" % until_time.strftime('%Y-%m-%d %H:%M:%S')) 429 return self.extra( 430 where = ["id IN (SELECT DISTINCT element_id FROM revisions WHERE %s)" % " AND ".join(where)]); 431 432 class CoreManager(EnhancedManager): 433 "Base class for all models managers" 434 435 def none(self, *args, **kwargs): 436 "" 437 return self.get_query_set().none(*args, **kwargs) 438 439 def get(self, **kwargs): 440 if kwargs.has_key('public_id'): 441 try: 442 args = kwargs.copy() 443 args['code'] = kwargs['public_id'] 444 args.pop('public_id') 445 return super(CoreManager, self).get(**args) 446 except ObjectDoesNotExist: 447 args = kwargs.copy() 448 args['id'] = kwargs['public_id'] 449 args.pop('public_id') 450 return super(CoreManager, self).get(**args) 451 452 return super(CoreManager, self).get(**kwargs) 453 -
telemeta/models/media.py
- Property mode changed from 100755 to 100644
red3547b r622588f 34 34 # David LIPSZYC <davidlipszyc@gmail.com> 35 35 36 from django. core.exceptions import ObjectDoesNotExist37 import cremquery as query 38 from xml.dom.minidom import getDOMImplementation36 from django.utils.translation import ugettext_lazy as _ 37 from telemeta.models.core import * 38 from telemeta.models.enum import ContextKeyword 39 39 from telemeta.util.unaccent import unaccent_icmp 40 40 import re 41 from django.db.models import FieldDoesNotExist, Q 42 from telemeta.models.core import DurationField, Duration, WeakForeignKey, EnhancedModel, \ 43 CharField, TextField, IntegerField, BooleanField, \ 44 DateTimeField, FileField, ForeignKey, FloatField, DateField 45 from telemeta.models import dublincore as dc 46 from django.utils.translation import ugettext_lazy as _ 47 48 class ModelCore(EnhancedModel): 49 50 @classmethod 51 def required_fields(cls): 52 required = [] 53 for field in cls._meta.fields: 54 if not field.blank: 55 required.append(field) 56 return required 57 58 def save(self, force_insert=False, force_update=False, using=None): 59 required = self.required_fields() 60 for field in required: 61 if not getattr(self, field.name): 62 raise RequiredFieldError(self, field) 63 super(ModelCore, self).save(force_insert, force_update, using) 64 65 def save_with_revision(self, user, force_insert=False, force_update=False, using=None): 66 "Save a media object and add a revision" 67 self.save(force_insert, force_update, using) 68 Revision.touch(self, user) 69 70 def get_revision(self): 71 return Revision.objects.filter(element_type=self.element_type, element_id=self.id).order_by('-time')[0] 72 73 @classmethod 74 def get_dom_name(cls): 75 "Convert the class name to a DOM element name" 76 clsname = cls.__name__ 77 return clsname[0].lower() + clsname[1:] 78 79 @staticmethod 80 def get_dom_field_name(field_name): 81 "Convert the class name to a DOM element name" 82 tokens = field_name.split('_') 83 name = tokens[0] 84 for t in tokens[1:]: 85 name += t[0].upper() + t[1:] 86 return name 87 88 def to_dom(self): 89 "Return the DOM representation of this media object" 90 impl = getDOMImplementation() 91 root = self.get_dom_name() 92 doc = impl.createDocument(None, root, None) 93 top = doc.documentElement 94 top.setAttribute("id", str(self.pk)) 95 fields = self.to_dict() 96 for name, value in fields.iteritems(): 97 element = doc.createElement(self.get_dom_field_name(name)) 98 if isinstance(value, EnhancedModel): 99 element.setAttribute('key', str(value.pk)) 100 value = unicode(value) 101 element.appendChild(doc.createTextNode(value)) 102 top.appendChild(element) 103 return doc 104 105 def to_dict(self): 106 "Return model fields as a dict of name/value pairs" 107 fields_dict = {} 108 for field in self._meta.fields: 109 fields_dict[field.name] = getattr(self, field.name) 110 return fields_dict 111 112 def to_list(self): 113 "Return model fields as a list" 114 fields_list = [] 115 for field in self._meta.fields: 116 fields_list.append({'name': field.name, 'value': getattr(self, field.name)}) 117 return fields_list 118 119 @classmethod 120 def field_label(cls, field_name): 121 try: 122 return cls._meta.get_field(field_name).verbose_name 123 except FieldDoesNotExist: 124 try: 125 return getattr(cls, field_name).verbose_name 126 except AttributeError: 127 return field_name 128 129 class Meta: 130 abstract = True 41 from telemeta.models.location import LocationRelation, Location 42 from telemeta.models.system import Revision 43 from telemeta.models import query 131 44 132 45 class MediaResource(ModelCore): … … 140 53 141 54 return _('Private data') 142 143 55 public_access_label.verbose_name = _('public access') 56 57 def save_with_revision(self, user, force_insert=False, force_update=False, using=None): 58 "Save a media object and add a revision" 59 self.save(force_insert, force_update, using) 60 Revision.touch(self, user) 61 62 def get_revision(self): 63 return Revision.objects.filter(element_type=self.element_type, element_id=self.id).order_by('-time')[0] 144 64 145 65 class Meta: 146 66 abstract = True 147 148 class MetaCore:149 app_label = 'telemeta'150 67 151 68 class MediaCollection(MediaResource): … … 345 262 "Check if the item code is well formed" 346 263 if not re.match('^' + self.collection.code, self.code): 347 return false264 return False 348 265 349 266 if self.collection.is_published: … … 390 307 return title 391 308 392 class MediaPart(MediaResource): 393 "Describe an item part" 394 element_type = 'part' 395 item = ForeignKey('MediaItem', related_name="parts", verbose_name=_('item')) 396 title = CharField(_('title'), required=True) 397 start = FloatField(_('start'), required=True) 398 end = FloatField(_('end'), required=True) 399 400 class Meta(MetaCore): 401 db_table = 'media_parts' 402 403 def __unicode__(self): 404 return self.title 405 406 class Enumeration(ModelCore): 407 "Abstract enumerations base class" 408 value = CharField(_('value'), required=True, unique=True) 409 410 def __unicode__(self): 411 return self.value 412 413 class Meta(MetaCore): 414 abstract = True 415 416 class PhysicalFormat(Enumeration): 417 "Collection physical format" 418 419 class Meta(MetaCore): 420 db_table = 'physical_formats' 421 422 class PublishingStatus(Enumeration): 423 "Collection publishing status" 424 425 class Meta(MetaCore): 426 db_table = 'publishing_status' 427 428 class AcquisitionMode(Enumeration): 429 "Mode of acquisition of the collection" 430 431 class Meta(MetaCore): 432 db_table = 'acquisition_modes' 433 434 class MetadataAuthor(Enumeration): 435 "Collection metadata author" 436 437 class Meta(MetaCore): 438 db_table = 'metadata_authors' 439 440 class MetadataWriter(Enumeration): 441 "Collection metadata writer" 442 443 class Meta(MetaCore): 444 db_table = 'metadata_writers' 445 446 class LegalRight(Enumeration): 447 "Collection legal rights" 448 449 class Meta(MetaCore): 450 db_table = 'legal_rights' 451 452 class RecordingContext(Enumeration): 453 "Collection recording context" 454 455 class Meta(MetaCore): 456 db_table = 'recording_contexts' 457 458 class AdConversion(Enumeration): 459 "Collection digital to analog conversion status" 460 461 class Meta(MetaCore): 462 db_table = 'ad_conversions' 463 464 class VernacularStyle(Enumeration): 465 "Item vernacular style" 466 467 class Meta(MetaCore): 468 db_table = 'vernacular_styles' 469 470 class GenericStyle(Enumeration): 471 "Item generic style" 472 473 class Meta(MetaCore): 474 db_table = 'generic_styles' 475 476 class Instrument(ModelCore): 477 "Instrument used in the item" 478 name = CharField(_('name'), required=True) 479 480 class Meta(MetaCore): 481 db_table = 'instruments' 482 483 def __unicode__(self): 484 return self.name 485 486 class InstrumentAlias(ModelCore): 487 "Instrument other name" 488 name = CharField(_('name'), required=True) 489 490 class Meta(MetaCore): 491 db_table = 'instrument_aliases' 492 493 def __unicode__(self): 494 return self.name 495 496 class InstrumentRelation(ModelCore): 497 "Instrument family" 498 instrument = ForeignKey('Instrument', related_name="parent_relation", 499 verbose_name=_('instrument')) 500 parent_instrument = ForeignKey('Instrument', related_name="child_relation", 501 verbose_name=_('parent instrument')) 502 503 class Meta(MetaCore): 504 db_table = 'instrument_relations' 505 unique_together = (('instrument', 'parent_instrument'),) 506 507 class InstrumentAliasRelation(ModelCore): 508 "Instrument family other name" 509 alias = ForeignKey('InstrumentAlias', related_name="other_name", 510 verbose_name=_('alias')) 511 instrument = ForeignKey('InstrumentAlias', related_name="relation", 512 verbose_name=_('instrument')) 513 514 class Meta(MetaCore): 515 db_table = 'instrument_alias_relations' 516 unique_together = (('alias', 'instrument'),) 309 class MediaItemKeyword(ModelCore): 310 "Item keyword" 311 item = ForeignKey('MediaItem', verbose_name=_('item'), related_name="keyword_relations") 312 keyword = ForeignKey('ContextKeyword', verbose_name=_('keyword'), related_name="item_relations") 313 314 class Meta(MetaCore): 315 db_table = 'media_item_keywords' 316 unique_together = (('item', 'keyword'),) 517 317 518 318 class MediaItemPerformance(ModelCore): … … 530 330 db_table = 'media_item_performances' 531 331 532 class User(ModelCore): 533 "Telemeta user" 534 LEVEL_CHOICES = (('user', 'user'), ('maintainer', 'maintainer'), ('admin', 'admin')) 535 536 username = CharField(_('username'), primary_key=True, max_length=64, required=True) 537 level = CharField(_('level'), choices=LEVEL_CHOICES, max_length=32, required=True) 538 first_name = CharField(_('first name')) 539 last_name = CharField(_('last name')) 540 phone = CharField(_('phone')) 541 email = CharField(_('email')) 542 543 class Meta(MetaCore): 544 db_table = 'users' 332 class MediaPart(MediaResource): 333 "Describe an item part" 334 element_type = 'part' 335 item = ForeignKey('MediaItem', related_name="parts", verbose_name=_('item')) 336 title = CharField(_('title'), required=True) 337 start = FloatField(_('start'), required=True) 338 end = FloatField(_('end'), required=True) 339 340 class Meta(MetaCore): 341 db_table = 'media_parts' 545 342 546 343 def __unicode__(self): 547 return self. username344 return self.title 548 345 549 346 class Playlist(ModelCore): … … 569 366 db_table = 'playlist_resources' 570 367 571 class Location(ModelCore):572 "Locations"573 OTHER_TYPE = 0574 CONTINENT = 1575 COUNTRY = 2576 TYPE_CHOICES = ((COUNTRY, _('country')), (CONTINENT, _('continent')), (OTHER_TYPE, _('other')))577 578 name = CharField(_('name'), unique=True, max_length=150, required=True)579 type = IntegerField(_('type'), choices=TYPE_CHOICES, default=OTHER_TYPE, db_index=True)580 complete_type = ForeignKey('LocationType', related_name="locations", verbose_name=_('complete type'))581 current_location = WeakForeignKey('self', related_name="past_names",582 verbose_name=_('current location'))583 latitude = FloatField(null=True)584 longitude = FloatField(null=True)585 is_authoritative = BooleanField(_('authoritative'))586 587 objects = query.LocationManager()588 589 def items(self):590 return MediaItem.objects.by_location(self)591 592 def collections(self):593 return MediaCollection.objects.by_location(self)594 595 def ancestors(self, direct=False):596 q = Q(descendant_relations__location=self)597 if direct:598 q &= Q(descendant_relations__is_direct=True)599 return Location.objects.filter(q)600 601 def descendants(self, direct=False):602 q = Q(ancestor_relations__ancestor_location=self)603 if direct:604 q &= Q(ancestor_relations__is_direct=True)605 return Location.objects.filter(q)606 607 def add_child(self, other):608 LocationRelation.objects.create(location=other, ancestor_location=self, is_direct=True)609 for location in self.ancestors():610 #FIXME: might raise Duplicate Entry611 LocationRelation.objects.create(location=other, ancestor_location=location)612 613 def add_parent(self, other):614 LocationRelation.objects.create(location=self, ancestor_location=other, is_direct=True)615 for location in self.descendants():616 #FIXME: might raise Duplicate Entry617 LocationRelation.objects.create(location=location, ancestor_location=other)618 619 def countries(self):620 if self.type == self.COUNTRY:621 return Location.objects.filter(pk=self.id)622 return self.ancestors().filter(type=self.COUNTRY)623 624 def continents(self):625 if self.type == self.CONTINENT:626 return Location.objects.filter(pk=self.id)627 return self.ancestors().filter(type=self.CONTINENT)628 629 class Meta(MetaCore):630 db_table = 'locations'631 632 def __unicode__(self):633 return self.name634 635 def flatname(self):636 if self.type != self.COUNTRY and self.type != self.CONTINENT:637 raise Exceptions("Flat names are only supported for countries and continents")638 639 map = Location.objects.flatname_map()640 for flatname in map:641 if self.id == map[flatname]:642 return flatname643 644 return None645 646 def paths(self):647 #FIXME: need to handle multiple (polyhierarchical) paths648 path = []649 location = self650 while location:651 path.append(location)652 try:653 location = location.ancestors(direct=True)[0]654 except IndexError:655 location = None656 return [path]657 658 def fullnames(self):659 names = []660 for path in self.paths():661 names.append(u', '.join([unicode(l) for l in path]))662 return names663 664 class LocationType(ModelCore):665 "Location types"666 code = CharField(_('identifier'), max_length=64, unique=True, required=True)667 name = CharField(_('name'), max_length=150, required=True)668 669 class Meta(MetaCore):670 db_table = 'location_types'671 672 class LocationAlias(ModelCore):673 "Location aliases"674 location = ForeignKey('Location', related_name="aliases", verbose_name=_('location'))675 alias = CharField(_('alias'), max_length=150, required=True)676 is_authoritative = BooleanField(_('authoritative'))677 678 def __unicode__(self):679 return self.alias680 681 class Meta(MetaCore):682 db_table = 'location_aliases'683 unique_together = (('location', 'alias'),)684 685 class LocationRelation(ModelCore):686 "Location relations"687 location = ForeignKey('Location', related_name="ancestor_relations", verbose_name=_('location'))688 ancestor_location = ForeignKey('Location', related_name="descendant_relations", verbose_name=_('ancestor location'))689 is_direct = BooleanField(db_index=True)690 691 class Meta(MetaCore):692 db_table = 'location_relations'693 unique_together = ('location', 'ancestor_location')694 695 def __unicode__(self):696 sep = ' > '697 if not self.is_direct:698 sep = ' >..> '699 return unicode(self.ancestor_location) + sep + unicode(self.location)700 701 class ContextKeyword(Enumeration):702 "Keyword"703 704 class Meta(MetaCore):705 db_table = 'context_keywords'706 707 class MediaItemKeyword(ModelCore):708 "Item keyword"709 item = ForeignKey('MediaItem', verbose_name=_('item'), related_name="keyword_relations")710 keyword = ForeignKey('ContextKeyword', verbose_name=_('keyword'), related_name="item_relations")711 712 class Meta(MetaCore):713 db_table = 'media_item_keywords'714 unique_together = (('item', 'keyword'),)715 716 class Publisher(Enumeration):717 "Collection publisher"718 719 class Meta(MetaCore):720 db_table = 'publishers'721 722 class PublisherCollection(ModelCore):723 "Collection which belongs to publisher"724 publisher = ForeignKey('Publisher', related_name="publisher_collections", verbose_name=_('publisher'))725 value = CharField(_('value'), required=True)726 727 def __unicode__(self):728 return self.value729 730 class Meta(MetaCore):731 db_table = 'publisher_collections'732 733 class Revision(ModelCore):734 "Revision made by user"735 ELEMENT_TYPE_CHOICES = (('collection', 'collection'), ('item', 'item'), ('part', 'part'))736 CHANGE_TYPE_CHOICES = (('import', 'import'), ('create', 'create'), ('update', 'update'), ('delete','delete'))737 738 element_type = CharField(_('element type'), choices=ELEMENT_TYPE_CHOICES, max_length=16, required=True)739 element_id = IntegerField(_('element identifier'), required=True)740 change_type = CharField(_('modification type'), choices=CHANGE_TYPE_CHOICES, max_length=16, required=True)741 time = DateTimeField(_('time'), auto_now_add=True)742 user = ForeignKey('User', db_column='username', related_name="revisions", verbose_name=_('user'))743 744 @classmethod745 def touch(cls, element, user):746 "Create or update a revision"747 revision = cls(element_type=element.element_type, element_id=element.pk,748 user=user, change_type='create')749 if element.pk:750 try:751 element.__class__.objects.get(pk=element.pk)752 except ObjectDoesNotExist:753 pass754 else:755 revision.change_type = 'update'756 757 revision.save()758 return revision759 760 class Meta(MetaCore):761 db_table = 'revisions'762 763 class EthnicGroup(ModelCore):764 "Item ethnic group"765 name = CharField(_('name'), required=True)766 767 class Meta(MetaCore):768 db_table = 'ethnic_groups'769 770 def __unicode__(self):771 return self.name772 773 class EthnicGroupAlias(ModelCore):774 "Item ethnic group other name"775 ethnic_group = ForeignKey('EthnicGroup', related_name="aliases", verbose_name=_('population / social group'))776 name = CharField(_('name'), required=True)777 778 class Meta(MetaCore):779 db_table = 'ethnic_group_aliases'780 781 782 class MissingUserError(Exception):783 pass784 785 class RequiredFieldError(Exception):786 def __init__(self, model, field):787 self.model = model788 self.field = field789 super(Exception, self).__init__('%s.%s is required' % (model._meta.object_name, field.name))790 791 368 class MediaInvalidCodeError(Exception): 792 369 pass 370 -
telemeta/models/query.py
r619d1a8 r622588f 1 1 # -*- coding: utf-8 -*- 2 # Copyright (C) 2007 Samalyse SARL3 2 # Copyright (C) 2007-2010 Samalyse SARL 3 # 4 4 # This software is a computer program whose purpose is to backup, analyse, 5 5 # transcode and stream any audio content with its metadata over a web frontend. 6 6 # 7 7 # This software is governed by the CeCILL license under French law and 8 8 # abiding by the rules of distribution of free software. You can use, … … 10 10 # license as circulated by CEA, CNRS and INRIA at the following URL 11 11 # "http://www.cecill.info". 12 12 # 13 13 # As a counterpart to the access to the source code and rights to copy, 14 14 # modify and redistribute granted by the license, users are provided only … … 16 16 # economic rights, and the successive licensors have only limited 17 17 # liability. 18 18 # 19 19 # In this respect, the user's attention is drawn to the risks associated 20 20 # with loading, using, modifying and/or developing or reproducing the … … 27 27 # data to be ensured and, more generally, to use and operate it in the 28 28 # same conditions as regards security. 29 29 # 30 30 # The fact that you are presently reading this means that you have had 31 31 # knowledge of the CeCILL license and that you accept its terms. … … 34 34 # David LIPSZYC <davidlipszyc@gmail.com> 35 35 36 from django import db37 from django.db.models import Manager, Q38 from telemeta. models.core import EnhancedQuerySet, EnhancedManager36 from django.db.models import Q 37 from telemeta.models.core import * 38 from telemeta.util.unaccent import unaccent, unaccent_icmp 39 39 import re 40 from django.core.exceptions import ObjectDoesNotExist 41 from django import db 42 import _mysql_exceptions 43 from telemeta.util.unaccent import unaccent_icmp, unaccent 44 45 class CoreQuerySet(EnhancedQuerySet): 46 "Base class for all query sets" 47 48 def none(self): # redundant with none() in recent Django svn 49 "Return an empty result set" 50 return self.extra(where = ["0 = 1"]) 51 52 def word_search_q(self, field, pattern): 53 words = re.split("[ .*-]+", pattern) 54 q = Q() 55 for w in words: 56 if len(w) >= 3: 57 kwargs = {field + '__icontains': w} 58 q &= Q(**kwargs) 59 60 return q 61 62 def word_search(self, field, pattern): 63 return self.filter(self.word_search_q(field, pattern)) 64 65 def _by_change_time(self, type, from_time = None, until_time = None): 66 "Search between two revision dates" 67 where = ["element_type = '%s'" % type] 68 if from_time: 69 where.append("time >= '%s'" % from_time.strftime('%Y-%m-%d %H:%M:%S')) 70 if until_time: 71 where.append("time <= '%s'" % until_time.strftime('%Y-%m-%d %H:%M:%S')) 40 41 class MediaItemQuerySet(CoreQuerySet): 42 "Base class for all media item query sets" 43 44 def quick_search(self, pattern): 45 "Perform a quick search on id and title" 46 return self.filter( 47 self.word_search_q('id', pattern) | 48 self.word_search_q('title', pattern) | 49 self.word_search_q('author', pattern) 50 ) 51 52 def without_collection(self): 53 "Find items which do not belong to any collection" 72 54 return self.extra( 73 where = ["id IN (SELECT DISTINCT element_id FROM revisions WHERE %s)" % " AND ".join(where)]); 74 75 class CoreManager(EnhancedManager): 76 "Base class for all models managers" 77 78 def none(self, *args, **kwargs): 79 "" 80 return self.get_query_set().none(*args, **kwargs) 81 82 def get(self, **kwargs): 83 if kwargs.has_key('public_id'): 84 try: 85 args = kwargs.copy() 86 args['code'] = kwargs['public_id'] 87 args.pop('public_id') 88 return super(CoreManager, self).get(**args) 89 except ObjectDoesNotExist: 90 args = kwargs.copy() 91 args['id'] = kwargs['public_id'] 92 args.pop('public_id') 93 return super(CoreManager, self).get(**args) 94 95 return super(CoreManager, self).get(**kwargs) 96 55 where = ["collection_id NOT IN (SELECT id FROM media_collections)"]); 56 57 def by_recording_date(self, from_date, to_date = None): 58 "Find items by recording date" 59 if to_date is None: 60 return (self.filter(recorded_from_date__lte=from_date, recorded_to_date__gte=from_date)) 61 else : 62 return (self.filter(Q(recorded_from_date__range=(from_date, to_date)) 63 | Q(recorded_to_date__range=(from_date, to_date)))) 64 65 def by_title(self, pattern): 66 "Find items by title" 67 # to (sort of) sync with models.media.MediaItem.get_title() 68 return self.filter(self.word_search_q("title", pattern) | self.word_search_q("collection__title", pattern)) 69 70 def by_publish_year(self, from_year, to_year = None): 71 "Find items by publishing year" 72 if to_year is None: 73 to_year = from_year 74 return self.filter(collection__year_published__range=(from_year, to_year)) 75 76 def by_change_time(self, from_time = None, until_time = None): 77 "Find items by last change time" 78 return self._by_change_time('item', from_time, until_time) 79 80 def by_location(self, location): 81 "Find items by location" 82 from telemeta.models import LocationRelation 83 descendants = LocationRelation.objects.filter(ancestor_location=location) 84 return self.filter(Q(location=location) | Q(location__in=descendants)) 85 86 @staticmethod 87 def __name_cmp(obj1, obj2): 88 return unaccent_icmp(obj1.name, obj2.name) 89 90 def countries(self, group_by_continent=False): 91 countries = [] 92 from telemeta.models import Location 93 for id in self.filter(location__isnull=False).values_list('location', flat=True).distinct(): 94 location = Location.objects.get(pk=id) 95 for l in location.countries(): 96 if not l in countries: 97 countries.append(l) 98 99 if group_by_continent: 100 grouped = {} 101 102 for country in countries: 103 for continent in country.continents(): 104 if not grouped.has_key(continent): 105 grouped[continent] = [] 106 107 grouped[continent].append(country) 108 109 keys = grouped.keys() 110 keys.sort(self.__name_cmp) 111 ordered = [] 112 for c in keys: 113 grouped[c].sort(self.__name_cmp) 114 ordered.append({'continent': c, 'countries': grouped[c]}) 115 116 countries = ordered 117 118 return countries 119 120 def virtual(self, *args): 121 qs = self 122 need_collection = False 123 related = [] 124 from telemeta.models import Location 125 for f in args: 126 if f == 'apparent_collector': 127 related.append('collection') 128 qs = qs.extra(select={f: 129 'IF(collector_from_collection, ' 130 'IF(media_collections.collector_is_creator, ' 131 'media_collections.creator, ' 132 'media_collections.collector),' 133 'media_items.collector)'}) 134 elif f == 'country_or_continent': 135 related.append('location') 136 qs = qs.extra(select={f: 137 'IF(locations.type = ' + str(Location.COUNTRY) + ' ' 138 'OR locations.type = ' + str(Location.CONTINENT) + ',' 139 'locations.name, ' 140 '(SELECT l2.name FROM location_relations AS r INNER JOIN locations AS l2 ' 141 'ON r.ancestor_location_id = l2.id ' 142 'WHERE r.location_id = media_items.location_id AND l2.type = ' + str(Location.COUNTRY) + ' ))' 143 }) 144 else: 145 raise Exception("Unsupported virtual field: %s" % f) 146 147 if related: 148 qs = qs.select_related(*related) 149 150 return qs 151 152 def ethnic_groups(self): 153 return self.filter(ethnic_group__isnull=False) \ 154 .values_list('ethnic_group__name', flat=True) \ 155 .distinct().order_by('ethnic_group__name') 156 157 class MediaItemManager(CoreManager): 158 "Manage media items queries" 159 160 def get_query_set(self): 161 "Return media query sets" 162 return MediaItemQuerySet(self.model) 163 164 def enriched(self): 165 "Query set with additional virtual fields such as apparent_collector and country_or_continent" 166 return self.get_query_set().virtual('apparent_collector', 'country_or_continent') 167 168 def quick_search(self, *args, **kwargs): 169 return self.get_query_set().quick_search(*args, **kwargs) 170 quick_search.__doc__ = MediaItemQuerySet.quick_search.__doc__ 171 172 def without_collection(self, *args, **kwargs): 173 return self.get_query_set().without_collection(*args, **kwargs) 174 without_collection.__doc__ = MediaItemQuerySet.without_collection.__doc__ 175 176 def by_recording_date(self, *args, **kwargs): 177 return self.get_query_set().by_recording_date(*args, **kwargs) 178 by_recording_date.__doc__ = MediaItemQuerySet.by_recording_date.__doc__ 179 180 def by_title(self, *args, **kwargs): 181 return self.get_query_set().by_title(*args, **kwargs) 182 by_title.__doc__ = MediaItemQuerySet.by_title.__doc__ 183 184 def by_publish_year(self, *args, **kwargs): 185 return self.get_query_set().by_publish_year(*args, **kwargs) 186 by_publish_year.__doc__ = MediaItemQuerySet.by_publish_year.__doc__ 187 188 def by_change_time(self, *args, **kwargs): 189 return self.get_query_set().by_change_time(*args, **kwargs) 190 by_change_time.__doc__ = MediaItemQuerySet.by_change_time.__doc__ 191 192 def by_location(self, *args, **kwargs): 193 return self.get_query_set().by_location(*args, **kwargs) 194 by_location.__doc__ = MediaItemQuerySet.by_location.__doc__ 195 97 196 class MediaCollectionQuerySet(CoreQuerySet): 98 197 … … 183 282 def stat_continents(self, only_continent=None): 184 283 "Return the number of collections by continents and countries as a tree" 185 from telemeta.models import MediaItem, Location186 284 187 285 countries = [] … … 214 312 return ordered 215 313 216 217 class MediaItemQuerySet(CoreQuerySet):218 "Base class for all media item query sets"219 220 def quick_search(self, pattern):221 "Perform a quick search on id and title"222 return self.filter(223 self.word_search_q('id', pattern) |224 self.word_search_q('title', pattern) |225 self.word_search_q('author', pattern)226 )227 228 def without_collection(self):229 "Find items which do not belong to any collection"230 return self.extra(231 where = ["collection_id NOT IN (SELECT id FROM media_collections)"]);232 233 def by_recording_date(self, from_date, to_date = None):234 "Find items by recording date"235 if to_date is None:236 return (self.filter(recorded_from_date__lte=from_date, recorded_to_date__gte=from_date))237 else :238 return (self.filter(Q(recorded_from_date__range=(from_date, to_date))239 | Q(recorded_to_date__range=(from_date, to_date))))240 241 def by_title(self, pattern):242 "Find items by title"243 # to (sort of) sync with models.media.MediaItem.get_title()244 return self.filter(self.word_search_q("title", pattern) | self.word_search_q("collection__title", pattern))245 246 def by_publish_year(self, from_year, to_year = None):247 "Find items by publishing year"248 if to_year is None:249 to_year = from_year250 return self.filter(collection__year_published__range=(from_year, to_year))251 252 def by_change_time(self, from_time = None, until_time = None):253 "Find items by last change time"254 return self._by_change_time('item', from_time, until_time)255 256 def by_location(self, location):257 "Find items by location"258 from telemeta.models import LocationRelation259 descendants = LocationRelation.objects.filter(ancestor_location=location)260 return self.filter(Q(location=location) | Q(location__in=descendants))261 262 @staticmethod263 def __name_cmp(obj1, obj2):264 return unaccent_icmp(obj1.name, obj2.name)265 266 def countries(self, group_by_continent=False):267 from telemeta.models import Location268 countries = []269 for id in self.filter(location__isnull=False).values_list('location', flat=True).distinct():270 location = Location.objects.get(pk=id)271 for l in location.countries():272 if not l in countries:273 countries.append(l)274 275 if group_by_continent:276 grouped = {}277 278 for country in countries:279 for continent in country.continents():280 if not grouped.has_key(continent):281 grouped[continent] = []282 283 grouped[continent].append(country)284 285 keys = grouped.keys()286 keys.sort(self.__name_cmp)287 ordered = []288 for c in keys:289 grouped[c].sort(self.__name_cmp)290 ordered.append({'continent': c, 'countries': grouped[c]})291 292 countries = ordered293 294 return countries295 296 def virtual(self, *args):297 qs = self298 need_collection = False299 related = []300 for f in args:301 if f == 'apparent_collector':302 related.append('collection')303 qs = qs.extra(select={f:304 'IF(collector_from_collection, '305 'IF(media_collections.collector_is_creator, '306 'media_collections.creator, '307 'media_collections.collector),'308 'media_items.collector)'})309 elif f == 'country_or_continent':310 from telemeta.models import Location311 related.append('location')312 qs = qs.extra(select={f:313 'IF(locations.type = ' + str(Location.COUNTRY) + ' '314 'OR locations.type = ' + str(Location.CONTINENT) + ','315 'locations.name, '316 '(SELECT l2.name FROM location_relations AS r INNER JOIN locations AS l2 '317 'ON r.ancestor_location_id = l2.id '318 'WHERE r.location_id = media_items.location_id AND l2.type = ' + str(Location.COUNTRY) + ' ))'319 })320 else:321 raise Exception("Unsupported virtual field: %s" % f)322 323 if related:324 qs = qs.select_related(*related)325 326 return qs327 328 def ethnic_groups(self):329 return self.filter(ethnic_group__isnull=False) \330 .values_list('ethnic_group__name', flat=True) \331 .distinct().order_by('ethnic_group__name')332 333 class MediaItemManager(CoreManager):334 "Manage media items queries"335 336 def get_query_set(self):337 "Return media query sets"338 return MediaItemQuerySet(self.model)339 340 def enriched(self):341 "Query set with additional virtual fields such as apparent_collector and country_or_continent"342 return self.get_query_set().virtual('apparent_collector', 'country_or_continent')343 344 def quick_search(self, *args, **kwargs):345 return self.get_query_set().quick_search(*args, **kwargs)346 quick_search.__doc__ = MediaItemQuerySet.quick_search.__doc__347 348 def without_collection(self, *args, **kwargs):349 return self.get_query_set().without_collection(*args, **kwargs)350 without_collection.__doc__ = MediaItemQuerySet.without_collection.__doc__351 352 def by_recording_date(self, *args, **kwargs):353 return self.get_query_set().by_recording_date(*args, **kwargs)354 by_recording_date.__doc__ = MediaItemQuerySet.by_recording_date.__doc__355 356 def by_title(self, *args, **kwargs):357 return self.get_query_set().by_title(*args, **kwargs)358 by_title.__doc__ = MediaItemQuerySet.by_title.__doc__359 360 def by_publish_year(self, *args, **kwargs):361 return self.get_query_set().by_publish_year(*args, **kwargs)362 by_publish_year.__doc__ = MediaItemQuerySet.by_publish_year.__doc__363 364 def by_change_time(self, *args, **kwargs):365 return self.get_query_set().by_change_time(*args, **kwargs)366 by_change_time.__doc__ = MediaItemQuerySet.by_change_time.__doc__367 368 def by_location(self, *args, **kwargs):369 return self.get_query_set().by_location(*args, **kwargs)370 by_location.__doc__ = MediaItemQuerySet.by_location.__doc__371 372 314 class LocationQuerySet(CoreQuerySet): 315 __flatname_map = None 316 373 317 def by_flatname(self, flatname): 374 map = LocationManager.flatname_map()318 map = self.flatname_map() 375 319 return self.filter(pk=map[flatname]) 376 320 377 class LocationManager(CoreManager): 378 __flatname_map = None 379 380 def get_query_set(self): 381 "Return location query set" 382 return LocationQuerySet(self.model) 383 384 @classmethod 385 def flatname_map(cls): 386 if cls.__flatname_map: 387 return cls.__flatname_map 388 389 from telemeta.models import Location 321 def flatname_map(self): 322 if self.__class__.__flatname_map: 323 return self.__class__.__flatname_map 324 390 325 map = {} 391 locations = Location.objects.filter(Q(type=Location.COUNTRY) | Q(type=Location.CONTINENT))326 locations = self.filter(Q(type=self.model.COUNTRY) | Q(type=self.model.CONTINENT)) 392 327 for l in locations: 393 328 flatname = unaccent(l.name).lower() … … 397 332 map[flatname] = l.id 398 333 399 cls.__flatname_map = map334 self.__class__.__flatname_map = map 400 335 return map 401 336 337 class LocationManager(CoreManager): 338 339 def get_query_set(self): 340 "Return location query set" 341 return LocationQuerySet(self.model) 342 402 343 def by_flatname(self, *args, **kwargs): 403 344 return self.get_query_set().by_flatname(*args, **kwargs) 404 345 by_flatname.__doc__ = LocationQuerySet.by_flatname.__doc__ 405 346 406 347 def flatname_map(self, *args, **kwargs): 348 return self.get_query_set().flatname_map(*args, **kwargs) 349 flatname_map.__doc__ = LocationQuerySet.flatname_map.__doc__ 350
Note: See TracChangeset
for help on using the changeset viewer.
