Some housekeeping
[redakcja.git] / src / catalogue / templatetags / book_list.py
1 # This file is part of FNP-Redakcja, licensed under GNU Affero GPLv3 or later.
2 # Copyright © Fundacja Nowoczesna Polska. See NOTICE for more information.
3 #
4 from re import split
5 from django.db.models import Q, Count
6 from django import template
7 from django.utils.translation import ugettext_lazy as _
8 from django.contrib.auth.models import User
9 from catalogue.models import Chunk, Image, Project
10
11 register = template.Library()
12
13
14 class ChunksList(object):
15     def __init__(self, chunk_qs):
16         self.chunk_qs = chunk_qs.select_related('book', 'book__project', 'stage', 'user')
17         self.book_qs = chunk_qs.values('book_id')
18
19     def __getitem__(self, key):
20         if isinstance(key, slice):
21             return self.get_slice(key)
22         elif isinstance(key, int):
23             return self.get_slice(slice(key, key+1))[0]
24         else:
25             raise TypeError('Unsupported list index. Must be a slice or an int.')
26
27     def __len__(self):
28         return self.book_qs.count()
29
30     def get_slice(self, slice_):
31         book_ids = [x['book_id'] for x in self.book_qs[slice_]]
32         chunk_qs = self.chunk_qs.filter(book__in=book_ids)
33
34         chunks_list = []
35         book = None
36         for chunk in chunk_qs:
37             if chunk.book != book:
38                 book = chunk.book
39                 chunks_list.append(ChoiceChunks(book, [chunk]))
40             else:
41                 chunks_list[-1].chunks.append(chunk)
42         return chunks_list
43
44
45 class ChoiceChunks(object):
46     """
47         Associates the given chunks iterable for a book.
48     """
49
50     chunks = None
51
52     def __init__(self, book, chunks):
53         self.book = book
54         self.chunks = chunks
55
56
57 def foreign_filter(qs, value, filter_field, model, model_field='slug', unset='-'):
58     if value == unset:
59         return qs.filter(**{filter_field: None})
60     if not value:
61         return qs
62     try:
63         obj = model._default_manager.get(**{model_field: value})
64     except model.DoesNotExist:
65         return qs.none()
66     else:
67         return qs.filter(**{filter_field: obj})
68
69
70 def search_filter(qs, value, filter_fields):
71     if not value:
72         return qs
73     q = Q(**{"%s__icontains" % filter_fields[0]: value})
74     for field in filter_fields[1:]:
75         q |= Q(**{"%s__icontains" % field: value})
76     return qs.filter(q)
77
78
79 _states = [
80         ('publishable', _('publishable'), Q(book___new_publishable=True)),
81         ('changed', _('changed'), Q(_changed=True)),
82         ('published', _('published'), Q(book___published=True)),
83         ('unpublished', _('unpublished'), Q(book___published=False)),
84         ('empty', _('empty'), Q(head=None)),
85     ]
86 _states_options = [s[:2] for s in _states]
87 _states_dict = dict([(s[0], s[2]) for s in _states])
88
89
90 def document_list_filter(request, **kwargs):
91
92     def arg_or_GET(field):
93         return kwargs.get(field, request.GET.get(field))
94
95     if arg_or_GET('all'):
96         chunks = Chunk.objects.all()
97     else:
98         chunks = Chunk.visible_objects.all()
99
100     chunks = chunks.order_by('book__title', 'book', 'number')
101
102     if not request.user.is_authenticated:
103         chunks = chunks.filter(book__public=True)
104
105     state = arg_or_GET('status')
106     if state in _states_dict:
107         chunks = chunks.filter(_states_dict[state])
108
109     chunks = foreign_filter(chunks, arg_or_GET('user'), 'user', User, 'username')
110     chunks = foreign_filter(chunks, arg_or_GET('stage'), 'stage', Chunk.tag_model, 'slug')
111     chunks = search_filter(chunks, arg_or_GET('title'), ['book__title', 'title'])
112     chunks = foreign_filter(chunks, arg_or_GET('project'), 'book__project', Project, 'pk')
113     return chunks
114
115
116 @register.inclusion_tag('catalogue/book_list/book_list.html', takes_context=True)
117 def book_list(context, user=None):
118     request = context['request']
119
120     if user:
121         filters = {"user": user}
122         new_context = {"viewed_user": user}
123     else:
124         filters = {}
125         new_context = {
126             "users": User.objects.annotate(
127                 count=Count('chunk')).filter(count__gt=0).order_by(
128                 '-count', 'last_name', 'first_name'),
129             "other_users": User.objects.annotate(
130                 count=Count('chunk')).filter(count=0).order_by(
131                 'last_name', 'first_name'),
132                 }
133
134     new_context.update({
135         "filters": True,
136         "request": request,
137         "books": ChunksList(document_list_filter(request, **filters)),
138         "stages": Chunk.tag_model.objects.all(),
139         "states": _states_options,
140         "projects": Project.objects.all(),
141     })
142
143     return new_context
144
145
146
147 _image_states = [
148         ('publishable', _('publishable'), Q(_new_publishable=True)),
149         ('changed', _('changed'), Q(_changed=True)),
150         ('published', _('published'), Q(_published=True)),
151         ('unpublished', _('unpublished'), Q(_published=False)),
152         ('empty', _('empty'), Q(head=None)),
153     ]
154 _image_states_options = [s[:2] for s in _image_states]
155 _image_states_dict = dict([(s[0], s[2]) for s in _image_states])
156
157 def image_list_filter(request, **kwargs):
158
159     def arg_or_GET(field):
160         return kwargs.get(field, request.GET.get(field))
161
162     images = Image.objects.all().select_related('user', 'stage', 'project')
163
164     if not request.user.is_authenticated:
165         images = images.filter(public=True)
166
167     state = arg_or_GET('status')
168     if state in _image_states_dict:
169         images = images.filter(_image_states_dict[state])
170
171     images = foreign_filter(images, arg_or_GET('user'), 'user', User, 'username')
172     images = foreign_filter(images, arg_or_GET('stage'), 'stage', Image.tag_model, 'slug')
173     images = search_filter(images, arg_or_GET('title'), ['title', 'title'])
174     images = foreign_filter(images, arg_or_GET('project'), 'project', Project, 'pk')
175     return images
176
177
178 @register.inclusion_tag('catalogue/image_table.html', takes_context=True)
179 def image_list(context, user=None):
180     request = context['request']
181
182     if user:
183         filters = {"user": user}
184         new_context = {"viewed_user": user}
185     else:
186         filters = {}
187         new_context = {
188             "users": User.objects.annotate(
189                 count=Count('image')).filter(count__gt=0).order_by(
190                 '-count', 'last_name', 'first_name'),
191             "other_users": User.objects.annotate(
192                 count=Count('image')).filter(count=0).order_by(
193                 'last_name', 'first_name'),
194                 }
195
196     new_context.update({
197         "filters": True,
198         "request": request,
199         "objects": image_list_filter(request, **filters),
200         "stages": Image.tag_model.objects.all(),
201         "states": _image_states_options,
202         "projects": Project.objects.all(),
203     })
204
205     return new_context