From 2188eba06a452bedd67a70046f50b08ee7d2cd09 Mon Sep 17 00:00:00 2001 From: Emilie Date: Fri, 30 Sep 2016 19:08:25 +0200 Subject: [PATCH] Media : ordering by date --- .../core/templatetags/organization_tags.py | 4 + app/organization/core/utils.py | 256 ------------------ .../migrations/0005_auto_20160930_1849.py | 23 ++ app/organization/media/models.py | 2 + app/organization/media/views.py | 11 +- app/templates/media/media_list.html | 2 +- 6 files changed, 35 insertions(+), 263 deletions(-) delete mode 100644 app/organization/core/utils.py create mode 100644 app/organization/media/migrations/0005_auto_20160930_1849.py diff --git a/app/organization/core/templatetags/organization_tags.py b/app/organization/core/templatetags/organization_tags.py index fc8ba6b4..b75bf00a 100644 --- a/app/organization/core/templatetags/organization_tags.py +++ b/app/organization/core/templatetags/organization_tags.py @@ -113,3 +113,7 @@ def in_category(objects, category): @register.filter def sub_topics(topic): return ProjectTopic.objects.filter(parent=topic) + +@register.filter +def classname(obj): + return obj.__class__.__name__ diff --git a/app/organization/core/utils.py b/app/organization/core/utils.py deleted file mode 100644 index e13a9fe8..00000000 --- a/app/organization/core/utils.py +++ /dev/null @@ -1,256 +0,0 @@ -from itertools import chain, dropwhile -from operator import mul, attrgetter, __not__ - -from django.db.models.query import REPR_OUTPUT_SIZE, EmptyQuerySet - -# Django Snippets -# https://djangosnippets.org/snippets/1933/ - -def mul_it(it1, it2): - ''' - Element-wise iterables multiplications. - ''' - assert len(it1) == len(it2),\ - "Can not element-wise multiply iterables of different length." - return map(mul, it1, it2) - - -def chain_sing(*iterables_or_items): - ''' - As itertools.chain except that if an argument is not iterable then chain it - as a singleton. - ''' - for iter_or_item in iterables_or_items: - if hasattr(iter_or_item, '__iter__'): - for item in iter_or_item: - yield item - else: - yield iter_or_item - - -class IableSequence(object): - ''' - Wrapper for sequence of iterable and indexable by non-negative integers - objects. That is a sequence of objects which implement __iter__, __len__ and - __getitem__ for slices, ints and longs. - - Note: not a Django-specific class. - ''' - def __init__(self, *args, **kwargs): - self.iables = args # wrapped sequence - self._len = None # length cache - self._collapsed = [] # collapsed elements cache - - def __len__(self): - if not self._len: - self._len = sum(len(iable) for iable in self.iables) - return self._len - - - def __iter__(self): - return chain(*self.iables) - - def __nonzero__(self): - try: - iter(self).__next__() - except StopIteration: - return False - return True - - - def _collect(self, start=0, stop=None, step=1): - if not stop: - stop = len(self) - sub_iables = [] - # collect sub sets - it = self.iables.__iter__() - try: - while stop>start: - i = it.__next__() - i_len = len(i) - if i_len > start: - # no problem with 'stop' being too big - sub_iables.append(i[start:stop:step]) - start = max(0, start-i_len) - stop -= i_len - except StopIteration: - pass - return sub_iables - - def __getitem__(self, key): - ''' - Preserves wrapped indexable sequences. - Does not support negative indices. - ''' - # params validation - if not isinstance(key, (slice, int, long)): - raise TypeError - assert ((not isinstance(key, slice) and (key >= 0)) - or (isinstance(key, slice) and (key.start is None or key.start >= 0) - and (key.stop is None or key.stop >= 0))), \ - "Negative indexing is not supported." - # initialization - if isinstance(key, slice): - start, stop, step = key.indices(len(self)) - ret_item=False - else: # isinstance(key, (int,long)) - start, stop, step = key, key+1, 1 - ret_item=True - # collect sub sets - ret_iables = self._collect(start, stop, step) - # return the simplest possible answer - if not len(ret_iables): - if ret_item: - raise IndexError("'%s' index out of range" % self.__class__.__name__) - return () - if ret_item: - # we have exactly one query set with exactly one item - assert len(ret_iables) == 1 and len(ret_iables[0]) == 1 - return ret_iables[0][0] - # otherwise we have more then one item in at least one query set - if len(ret_iables) == 1: - return ret_iables[0] - # Note: this can't be self.__class__ instead of IableSequence; exemplary - # cause is that indexing over query sets returns lists so we can not - # return QuerySetSequence by default. Some type checking enhancement can - # be implemented in subclasses. - return IableSequence(*ret_iables) - - - def collapse(self, stop=None): - ''' - Collapses sequence into a list. - - Try to do it effectively with caching. - ''' - if not stop: - stop = len(self) - # if we already calculated sufficient collapse then return it - if len(self._collapsed) >= stop: - return self._collapsed[:stop] - # otherwise collapse only the missing part - items = self._collapsed - sub_iables = self._collect(len(self._collapsed), stop) - for sub_iable in sub_iables: - items+=sub_iable - # cache new collapsed items - self._collapsed = items - return self._collapsed - - def __repr__(self): - # get +1 element for the truncation msg if applicable - items = self.collapse(stop=REPR_OUTPUT_SIZE+1) - if len(items) > REPR_OUTPUT_SIZE: - items[-1] = "...(remaining elements truncated)..." - return repr(items) - -# http://stackoverflow.com/questions/1516249/python-list-sorting-with-multiple-attributes-and-mixed-order -class QuerySetSequence(IableSequence): - ''' - Wrapper for the query sets sequence without the restriction on the identity - of the base models. - ''' - def count(self): - if not self._len: - self._len = sum(qs.count() for qs in self.iables) - return self._len - - def __len__(self): - # override: use DB effective count's instead of len() - return self.count() - - def order_by(self, *field_names): - ''' - Returns a list of the QuerySetSequence items with the ordering changed. - ''' - # construct a comparator function based on the field names prefixes - reverses = [1] * len(field_names) - field_names = list(field_names) - for i in range(len(field_names)): - field_name = field_names[i] - if field_name[0] == '-': - reverses[i] = -1 - field_names[i] = field_name[1:] - # wanna iterable and attrgetter returns single item if 1 arg supplied - fields_getter = lambda i: chain_sing(attrgetter(*field_names)(i)) - print("************************") - print(fields_getter) - print("************************") - # comparator gets the first non-zero value of the field comparison - # results taking into account reverse order for fields prefixed with '-' - comparator = lambda i1, i2:\ - dropwhile(__not__, - mul_it(map(key, fields_getter(i1), fields_getter(i2)), reverses) - ).__next__() - - # return new sorted list - return sorted(self.collapse(), key=fields_getter) - - def filter(self, *args, **kwargs): - """ - Returns a new QuerySetSequence or instance with the args ANDed to the - existing set. - - QuerySetSequence is simplified thus result actually can be one of: - QuerySetSequence, QuerySet, EmptyQuerySet. - """ - return self._filter_or_exclude(False, *args, **kwargs) - - def exclude(self, *args, **kwargs): - """ - Returns a new QuerySetSequence instance with NOT (args) ANDed to the - existing set. - - QuerySetSequence is simplified thus result actually can be one of: - QuerySetSequence, QuerySet, EmptyQuerySet. - """ - return self._filter_or_exclude(True, *args, **kwargs) - - def _simplify(self, qss=None): - ''' - Returns QuerySetSequence, QuerySet or EmptyQuerySet depending on the - contents of items, i.e. at least two non empty QuerySets, exactly one - non empty QuerySet and all empty QuerySets respectively. - - Does not modify original QuerySetSequence. - ''' - not_empty_qss = filter(None, qss if qss else self.iables) - if not len(not_empty_qss): - return EmptyQuerySet() - if len(not_empty_qss) == 1: - return not_empty_qss[0] - return QuerySetSequence(*not_empty_qss) - - def _filter_or_exclude(self, negate, *args, **kwargs): - ''' - Maps _filter_or_exclude over QuerySet items and simplifies the result. - ''' - # each Query set is cloned separately - return self._simplify(*map(lambda qs: - qs._filter_or_exclude(negate, *args, **kwargs), self.iables)) - - def exists(self): - for qs in self.iables: - if qs.exists(): - return True - return False - - -def cmp_to_key(mycmp): - 'Convert a cmp= function into a key= function' - class K(object): - def __init__(self, obj, *args): - self.obj = obj - def __lt__(self, other): - return mycmp(self.obj, other.obj) < 0 - def __gt__(self, other): - return mycmp(self.obj, other.obj) > 0 - def __eq__(self, other): - return mycmp(self.obj, other.obj) == 0 - def __le__(self, other): - return mycmp(self.obj, other.obj) <= 0 - def __ge__(self, other): - return mycmp(self.obj, other.obj) >= 0 - def __ne__(self, other): - return mycmp(self.obj, other.obj) != 0 - return K diff --git a/app/organization/media/migrations/0005_auto_20160930_1849.py b/app/organization/media/migrations/0005_auto_20160930_1849.py new file mode 100644 index 00000000..2aa0ec0c --- /dev/null +++ b/app/organization/media/migrations/0005_auto_20160930_1849.py @@ -0,0 +1,23 @@ +# -*- coding: utf-8 -*- +# Generated by Django 1.9.7 on 2016-09-30 16:49 +from __future__ import unicode_literals + +from django.db import migrations + + +class Migration(migrations.Migration): + + dependencies = [ + ('organization-media', '0004_auto_20160930_1039'), + ] + + operations = [ + migrations.AlterModelOptions( + name='audio', + options={'ordering': ('-created_at',), 'verbose_name': 'audio'}, + ), + migrations.AlterModelOptions( + name='video', + options={'ordering': ('-created_at',), 'verbose_name': 'video'}, + ), + ] diff --git a/app/organization/media/models.py b/app/organization/media/models.py index cfa70c4f..54f32841 100644 --- a/app/organization/media/models.py +++ b/app/organization/media/models.py @@ -62,6 +62,7 @@ class Audio(Media): class Meta: verbose_name = _('audio') + ordering = ('-created_at',) def get_absolute_url(self): return reverse("festival-audio-detail", kwargs={"slug": self.slug}) @@ -76,6 +77,7 @@ class Video(Media): class Meta: verbose_name = _('video') + ordering = ('-created_at',) def get_absolute_url(self): return reverse("festival-video-detail", kwargs={"slug": self.slug}) diff --git a/app/organization/media/views.py b/app/organization/media/views.py index 525895f8..044c90d5 100644 --- a/app/organization/media/views.py +++ b/app/organization/media/views.py @@ -51,11 +51,10 @@ class MediaListView(ListView): context_object_name = 'media' def get_queryset(self): - audios = Audio.objects.all() videos = Video.objects.all() - qsseq = QuerySetSequence(audios, videos) - qsseq.order_by('created_at',) - print("----------------------------------") - print(len(qsseq)) - return qsseq + media_list = [video for video in videos] + media_list += [audio for audio in audios] + media_list.sort(key=lambda x: x.created_at, reverse=True) + + return media_list diff --git a/app/templates/media/media_list.html b/app/templates/media/media_list.html index dfa4eff1..41526d7f 100644 --- a/app/templates/media/media_list.html +++ b/app/templates/media/media_list.html @@ -29,7 +29,7 @@ {% block page_content %} {% for m in media %} - {{ m.get_content_model }} : {{ m.created_at }} : {{ m.title }} + {{ m|classname }} : {{ m.created_at }} : {{ m.title }}
{% endfor %} -- 2.39.5