source: telemeta/models/query.py @ c9f9871

cremcrem2devdev2diademsfeature/breadcrumbsfeature/ts-0.5feature/ts-0.5.4feature/writecachegenericinstru_searchlamlam2mapsv3mergenlivemultirelease/1.4.4sabiodsecurityserversocialstoragetelecastertest
Last change on this file since c9f9871 was c9f9871, checked in by yomguy <yomguy@…>, 3 years ago

try instrument filter

  • Property mode set to 100644
File size: 19.9 KB
Line 
1# -*- coding: utf-8 -*-
2# Copyright (C) 2007-2010 Samalyse SARL
3# Copyright (C) 2010-2011 Parisson SARL
4#
5# This software is a computer program whose purpose is to backup, analyse,
6# transcode and stream any audio content with its metadata over a web frontend.
7#
8# This software is governed by the CeCILL  license under French law and
9# abiding by the rules of distribution of free software.  You can  use,
10# modify and/ or redistribute the software under the terms of the CeCILL
11# license as circulated by CEA, CNRS and INRIA at the following URL
12# "http://www.cecill.info".
13#
14# As a counterpart to the access to the source code and  rights to copy,
15# modify and redistribute granted by the license, users are provided only
16# with a limited warranty  and the software's author,  the holder of the
17# economic rights,  and the successive licensors  have only  limited
18# liability.
19#
20# In this respect, the user's attention is drawn to the risks associated
21# with loading,  using,  modifying and/or developing or reproducing the
22# software by the user in light of its specific status of free software,
23# that may mean  that it is complicated to manipulate,  and  that  also
24# therefore means  that it is reserved for developers  and  experienced
25# professionals having in-depth computer knowledge. Users are therefore
26# encouraged to load and test the software's suitability as regards their
27# requirements in conditions enabling the security of their systems and/or
28# data to be ensured and,  more generally, to use and operate it in the
29# same conditions as regards security.
30#
31# The fact that you are presently reading this means that you have had
32# knowledge of the CeCILL license and that you accept its terms.
33#
34# Authors: Olivier Guilyardi <olivier@samalyse.com>
35#          David LIPSZYC <davidlipszyc@gmail.com>
36#          Guillaume Pellerin <yomguy@parisson.com>
37
38from django.conf import settings
39from django.db.models import Q, Max, Min
40from telemeta.models.core import *
41from telemeta.util.unaccent import unaccent, unaccent_icmp
42from telemeta.models.enum import EthnicGroup
43import re
44
45engine = settings.DATABASES['default']['ENGINE']
46
47class MediaItemQuerySet(CoreQuerySet):
48    "Base class for all media item query sets"
49
50    def quick_search(self, pattern):
51        "Perform a quick search on code, title and collector name"
52        pattern = pattern.strip()
53
54#        from telemeta.models.media import MediaItem
55#        mod = MediaItem()
56#        fields = mod.to_dict()
57#        keys =  fields.keys()
58#        q = self.by_fuzzy_collector_q(pattern)
59#        for field in keys:
60#            field_str = str(mod._meta.get_field(field))
61#            if 'CharField' in field_str:
62#                q = q | word_search_q(field)
63
64        q = ( Q(code__contains=pattern) |
65            Q(old_code__contains=pattern) |
66            word_search_q('title', pattern) |
67            word_search_q('comment', pattern) |
68            self.by_fuzzy_collector_q(pattern) )
69
70        return self.filter(q)
71
72    def without_collection(self):
73        "Find items which do not belong to any collection"
74        return self.extra(
75            where = ["collection_id NOT IN (SELECT id FROM media_collections)"]);
76
77    def by_public_id(self, public_id):
78        "Find items by public_id"
79        return self.filter(public_id=public_id)
80
81    def by_recording_date(self, from_date, to_date = None):
82        "Find items by recording date"
83        if to_date is None:
84            return (self.filter(recorded_from_date__lte=from_date, recorded_to_date__gte=from_date))
85        else :
86            return (self.filter(Q(recorded_from_date__range=(from_date, to_date))
87                                | Q(recorded_to_date__range=(from_date, to_date))))
88
89    def by_title(self, pattern):
90        "Find items by title"
91        # to (sort of) sync with models.media.MediaItem.get_title()
92        return self.filter(word_search_q("title", pattern) |
93                           (Q(title="") & word_search_q("collection__title", pattern)))
94
95    def by_publish_year(self, from_year, to_year = None):
96        "Find items by publishing year"
97        if to_year is None:
98            to_year = from_year
99        return self.filter(collection__year_published__range=(from_year, to_year))
100
101    def by_change_time(self, from_time = None, until_time = None):
102        "Find items by last change time"
103        return self._by_change_time('item', from_time, until_time)
104
105    def by_location(self, location):
106        "Find items by location"
107        return self.filter(location__in=location.apparented())
108
109    @staticmethod
110    def __name_cmp(obj1, obj2):
111        return unaccent_icmp(obj1.name, obj2.name)
112
113    def locations(self):
114        from telemeta.models import Location, LocationRelation
115        l = self.values('location')
116        c = self.values('location__current_location')
117        r = LocationRelation.objects.filter(location__in=l).values('ancestor_location')
118        return Location.objects.filter(Q(pk__in=l) | Q(pk__in=r) | Q(pk__in=c))
119
120    def countries(self, group_by_continent=False):
121        countries = []
122        from telemeta.models import Location
123        for id in self.filter(location__isnull=False).values_list('location', flat=True).distinct():
124            location = Location.objects.get(pk=id)
125            for l in location.countries():
126                c = l.current_location
127                if not c in countries:
128                    countries.append(c)
129
130        if group_by_continent:
131            grouped = {}
132
133            for country in countries:
134                for continent in country.continents():
135                    if not grouped.has_key(continent):
136                        grouped[continent] = []
137
138                    grouped[continent].append(country)
139
140            keys = grouped.keys()
141            keys.sort(self.__name_cmp)
142            ordered = []
143            for c in keys:
144                grouped[c].sort(self.__name_cmp)
145                ordered.append({'continent': c, 'countries': grouped[c]})
146
147            countries = ordered
148        else:
149            countries.sort(self.__name_cmp)
150
151        return countries
152
153    def virtual(self, *args):
154        qs = self
155        need_collection = False
156        related = []
157        from telemeta.models import Location
158        for f in args:
159            if f == 'apparent_collector':
160                if not 'sqlite3' in engine:
161                    related.append('collection')
162                    qs = qs.extra(select={f:
163                        'IF(collector_from_collection, '
164                            'IF(media_collections.collector_is_creator, '
165                               'media_collections.creator, '
166                               'media_collections.collector),'
167                            'media_items.collector)'})
168            elif f == 'country_or_continent':
169                related.append('location')
170                if not 'sqlite3' in engine:
171                    qs = qs.extra(select={f:
172                        'IF(locations.type = ' + str(Location.COUNTRY) + ' '
173                        'OR locations.type = ' + str(Location.CONTINENT) + ','
174                        'locations.name, '
175                        '(SELECT l2.name FROM location_relations AS r INNER JOIN locations AS l2 '
176                        'ON r.ancestor_location_id = l2.id '
177                        'WHERE r.location_id = media_items.location_id AND l2.type = ' + str(Location.COUNTRY) + ' LIMIT 1))'
178                    })
179            else:
180                raise Exception("Unsupported virtual field: %s" % f)
181
182        if related:
183            qs = qs.select_related(*related)
184
185        return qs
186
187    def ethnic_groups(self):
188        ids = self.filter(ethnic_group__isnull=False).values('ethnic_group');
189        return EthnicGroup.objects.filter(pk__in=ids)
190
191    @staticmethod
192    def by_fuzzy_collector_q(pattern):
193        return (word_search_q('collection__creator', pattern) |
194                word_search_q('collection__collector', pattern) |
195                word_search_q('collector', pattern))
196
197    def by_fuzzy_collector(self, pattern):
198        return self.filter(self.by_fuzzy_collector_q(pattern))
199
200    def sound(self):
201        return self.filter(file__contains='/')
202
203    def by_instrument(self, instrument):
204        "Find items by instrument"
205        return self.filter(instruments__in=instrument)
206
207
208class MediaItemManager(CoreManager):
209    "Manage media items queries"
210
211    def get_query_set(self):
212        "Return media query sets"
213        return MediaItemQuerySet(self.model)
214
215    def enriched(self):
216        "Query set with additional virtual fields such as apparent_collector and country_or_continent"
217        return self.get_query_set().virtual('apparent_collector', 'country_or_continent')
218
219    def quick_search(self, *args, **kwargs):
220        return self.get_query_set().quick_search(*args, **kwargs)
221    quick_search.__doc__ = MediaItemQuerySet.quick_search.__doc__
222
223    def without_collection(self, *args, **kwargs):
224        return self.get_query_set().without_collection(*args, **kwargs)
225    without_collection.__doc__ = MediaItemQuerySet.without_collection.__doc__
226
227    def by_recording_date(self, *args, **kwargs):
228        return self.get_query_set().by_recording_date(*args, **kwargs)
229    by_recording_date.__doc__ = MediaItemQuerySet.by_recording_date.__doc__
230
231    def by_title(self, *args, **kwargs):
232        return self.get_query_set().by_title(*args, **kwargs)
233    by_title.__doc__ = MediaItemQuerySet.by_title.__doc__
234
235    def by_publish_year(self, *args, **kwargs):
236        return self.get_query_set().by_publish_year(*args, **kwargs)
237    by_publish_year.__doc__ = MediaItemQuerySet.by_publish_year.__doc__
238
239    def by_change_time(self, *args, **kwargs):
240        return self.get_query_set().by_change_time(*args, **kwargs)
241    by_change_time.__doc__ = MediaItemQuerySet.by_change_time.__doc__
242
243    def by_location(self, *args, **kwargs):
244        return self.get_query_set().by_location(*args, **kwargs)
245    by_location.__doc__ = MediaItemQuerySet.by_location.__doc__
246
247    def sound(self, *args, **kwargs):
248        return self.get_query_set().sound(*args, **kwargs)
249    sound.__doc__ = MediaItemQuerySet.sound.__doc__
250
251    def by_instrument(self, *args, **kwargs):
252        return self.get_query_set().by_instrument(*args, **kwargs)
253    by_instrument.__doc__ = MediaItemQuerySet.by_instrument.__doc__
254
255
256class MediaCollectionQuerySet(CoreQuerySet):
257
258    def quick_search(self, pattern):
259        "Perform a quick search on code, title and collector name"
260        from telemeta.models.media import MediaCollection
261        pattern = pattern.strip()
262        mod = MediaCollection()
263        fields = mod.to_dict()
264        keys =  fields.keys()
265        q = self.by_fuzzy_collector_q(pattern)
266        for field in keys:
267            field_str = str(mod._meta.get_field(field))
268            if 'CharField' in field_str or 'TextField' in field_str:
269                q = q | word_search_q(field, pattern)
270        return self.filter(q)
271
272    def by_location(self, location):
273        "Find collections by location"
274        return self.filter(items__location__in=location.apparented()).distinct()
275
276    def by_recording_year(self, from_year, to_year=None):
277        "Find collections by recording year"
278        if to_year is None:
279            return (self.filter(recorded_from_year__lte=from_year, recorded_to_year__gte=from_year))
280        else:
281            return (self.filter(Q(recorded_from_year__range=(from_year, to_year)) |
282                    Q(recorded_to_year__range=(from_year, to_year))))
283
284    def by_publish_year(self, from_year, to_year=None):
285        "Find collections by publishing year"
286        if to_year is None:
287            to_year = from_year
288        return self.filter(year_published__range=(from_year, to_year))
289
290    def by_ethnic_group(self, group):
291        "Find collections by ethnic group"
292        return self.filter(items__ethnic_group=group).distinct()
293
294    def by_change_time(self, from_time=None, until_time=None):
295        "Find collections between two dates"
296        return self._by_change_time('collection', from_time, until_time)
297
298    def virtual(self, *args):
299        qs = self
300        for f in args:
301            if f == 'apparent_collector':
302                if not 'sqlite3' in engine:
303                    qs = qs.extra(select={f: 'IF(media_collections.collector_is_creator, '
304                                         'media_collections.creator, media_collections.collector)'})
305            else:
306                raise Exception("Unsupported virtual field: %s" % f)
307
308        return qs
309
310    def recording_year_range(self):
311        from_max = self.aggregate(Max('recorded_from_year'))['recorded_from_year__max']
312        to_max   = self.aggregate(Max('recorded_to_year'))['recorded_to_year__max']
313        year_max = max(from_max, to_max)
314
315        from_min = self.filter(recorded_from_year__gt=0).aggregate(Min('recorded_from_year'))['recorded_from_year__min']
316        to_min   = self.filter(recorded_to_year__gt=0).aggregate(Min('recorded_to_year'))['recorded_to_year__min']
317        year_min = min(from_min, to_min)
318
319        if not year_max:
320            year_max = year_min
321        elif not year_min:
322            year_min = year_max
323
324        return year_min, year_max
325
326    def publishing_year_range(self):
327        year_max = self.aggregate(Max('year_published'))['year_published__max']
328        year_min = self.filter(year_published__gt=0).aggregate(Min('year_published'))['year_published__min']
329
330        return year_min, year_max
331
332    @staticmethod
333    def by_fuzzy_collector_q(pattern):
334        return word_search_q('creator', pattern) | word_search_q('collector', pattern)
335
336    def by_fuzzy_collector(self, pattern):
337        return self.filter(self.by_fuzzy_collector_q(pattern))
338
339    def sound(self):
340        return self.filter(items__file__contains='/').distinct()
341
342    def by_instrument(self, instrument):
343        "Find collections by instrument"
344        return self.filter(items__instruments__in=instrument).distinct()
345
346
347class MediaCollectionManager(CoreManager):
348    "Manage collection queries"
349
350    def get_query_set(self):
351        "Return the collection query"
352        return MediaCollectionQuerySet(self.model)
353
354    def enriched(self):
355        "Query set with additional virtual fields such as apparent_collector"
356        return self.get_query_set().virtual('apparent_collector')
357
358    def quick_search(self, *args, **kwargs):
359        return self.get_query_set().quick_search(*args, **kwargs)
360    quick_search.__doc__ = MediaCollectionQuerySet.quick_search.__doc__
361
362    def by_location(self, *args, **kwargs):
363        return self.get_query_set().by_location(*args, **kwargs)
364    by_location.__doc__ = MediaCollectionQuerySet.by_location.__doc__
365
366    def by_recording_year(self, *args, **kwargs):
367        return self.get_query_set().by_recording_year(*args, **kwargs)
368    by_recording_year.__doc__ = MediaCollectionQuerySet.by_recording_year.__doc__
369
370    def by_publish_year(self, *args, **kwargs):
371        return self.get_query_set().by_publish_year(*args, **kwargs)
372    by_publish_year.__doc__ = MediaCollectionQuerySet.by_publish_year.__doc__
373
374    def by_ethnic_group(self, *args, **kwargs):
375        return self.get_query_set().by_ethnic_group(*args, **kwargs)
376    by_ethnic_group.__doc__ = MediaCollectionQuerySet.by_ethnic_group.__doc__
377
378    def by_change_time(self, *args, **kwargs):
379        return self.get_query_set().by_change_time(*args, **kwargs)
380    by_change_time.__doc__ = MediaCollectionQuerySet.by_change_time.__doc__
381
382    @staticmethod
383    def __name_cmp(obj1, obj2):
384        return unaccent_icmp(obj1.name, obj2.name)
385
386    def sound(self, *args, **kwargs):
387        return self.get_query_set().sound(*args, **kwargs)
388    sound.__doc__ = MediaCollectionQuerySet.sound.__doc__
389
390    def by_instrument(self, *args, **kwargs):
391        return self.get_query_set().by_instrument(*args, **kwargs)
392    by_instrument.__doc__ = MediaCollectionQuerySet.by_instrument.__doc__
393
394
395class LocationQuerySet(CoreQuerySet):
396    __flatname_map = None
397
398    def by_flatname(self, flatname):
399        map = self.flatname_map()
400        return self.filter(pk=map[flatname])
401
402    def flatname_map(self):
403        if self.__class__.__flatname_map:
404            return self.__class__.__flatname_map
405
406        map = {}
407        locations = self.filter(Q(type=self.model.COUNTRY) | Q(type=self.model.CONTINENT))
408        for l in locations:
409            flatname = unaccent(l.name).lower()
410            flatname = re.sub('[^a-z]', '_', flatname)
411            while map.has_key(flatname):
412                flatname = '_' + flatname
413            map[flatname] = l.id
414
415        self.__class__.__flatname_map = map
416        return map
417
418    def current(self):
419        return self.filter(id__in=self.values_list('current_location_id', flat=True)).distinct()
420
421class LocationManager(CoreManager):
422
423    def get_query_set(self):
424        "Return location query set"
425        return LocationQuerySet(self.model)
426
427    def by_flatname(self, *args, **kwargs):
428        return self.get_query_set().by_flatname(*args, **kwargs)
429    by_flatname.__doc__ = LocationQuerySet.by_flatname.__doc__
430
431    def flatname_map(self, *args, **kwargs):
432        return self.get_query_set().flatname_map(*args, **kwargs)
433    flatname_map.__doc__ = LocationQuerySet.flatname_map.__doc__
434
435
436class MediaCorpusQuerySet(CoreQuerySet):
437    "Base class for all media resource query sets"
438
439    def quick_search(self, pattern):
440        "Perform a quick search on text and char fields"
441        from telemeta.models.media import MediaCorpus
442        mod = MediaCorpus()
443        pattern = pattern.strip()
444        q = Q(code__contains=pattern)
445        fields = mod.to_dict()
446        keys =  fields.keys()
447
448        for field in keys:
449            field_str = str(mod._meta.get_field(field))
450            if 'CharField' in field_str or 'TextField' in field_str:
451                q = q | word_search_q(field, pattern)
452
453        return self.filter(q)
454
455
456class MediaCorpusManager(CoreManager):
457    "Manage media resource queries"
458
459    def get_query_set(self):
460        "Return resource query sets"
461        return MediaCorpusQuerySet(self.model)
462
463    def quick_search(self, *args, **kwargs):
464        return self.get_query_set().quick_search(*args, **kwargs)
465    quick_search.__doc__ = MediaCorpusQuerySet.quick_search.__doc__
466
467
468class MediaFondsQuerySet(CoreQuerySet):
469    "Base class for all media resource query sets"
470
471    def quick_search(self, pattern):
472        "Perform a quick search on text and char fields"
473        from telemeta.models.media import MediaFonds
474        mod = MediaFonds()
475        pattern = pattern.strip()
476        q = Q(code__contains=pattern)
477        fields = mod.to_dict()
478        keys =  fields.keys()
479        for field in keys:
480            field_str = str(mod._meta.get_field(field))
481            if 'CharField' in field_str or 'TextField' in field_str:
482                q = q | word_search_q(field, pattern)
483        return self.filter(q)
484
485
486class MediaFondsManager(CoreManager):
487    "Manage media resource queries"
488
489    def get_query_set(self):
490        "Return resource query sets"
491        return MediaFondsQuerySet(self.model)
492
493    def quick_search(self, *args, **kwargs):
494        return self.get_query_set().quick_search(*args, **kwargs)
495    quick_search.__doc__ = MediaFondsQuerySet.quick_search.__doc__
496
497
498class InstrumentQuerySet(CoreQuerySet):
499    "Base class for all media instrument query sets"
500
501    def quick_search(self, pattern):
502        "Perform a quick search on text and char fields"
503        from telemeta.models.instrument import Instrument
504        mod = Instrument()
505        pattern = pattern.strip()
506        q = Q(code__contains=pattern)
507        fields = mod.to_dict()
508        keys =  fields.keys()
509        for field in keys:
510            field_str = str(mod._meta.get_field(field))
511            if 'CharField' in field_str or 'TextField' in field_str:
512                q = q | word_search_q(field, pattern)
513        return self.filter(q)
514
515
516class InstrumentManager(CoreManager):
517    "Manage instrument queries"
518
519    def get_query_set(self):
520        "Return instrument query sets"
521        return InstrumentQuerySet(self.model)
522
523    def quick_search(self, *args, **kwargs):
524        return self.get_query_set().quick_search(*args, **kwargs)
525    quick_search.__doc__ = InstrumentQuerySet.quick_search.__doc__
Note: See TracBrowser for help on using the repository browser.