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.
5 from django.db.models import Q, Count, F, Max
6 from django import template
7 from django.utils.translation import gettext_lazy as _
8 from django.contrib.auth.models import User
9 from documents.models import Book, Chunk, Image, Project
11 register = template.Library()
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')
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]
25 raise TypeError('Unsupported list index. Must be a slice or an int.')
28 return self.book_qs.count()
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)
36 for chunk in chunk_qs:
37 if chunk.book != book:
39 chunks_list.append(ChoiceChunks(book, [chunk]))
41 chunks_list[-1].chunks.append(chunk)
45 class ChoiceChunks(object):
47 Associates the given chunks iterable for a book.
52 def __init__(self, book, chunks):
57 def foreign_filter(qs, value, filter_field, model, model_field='slug', unset='-'):
59 return qs.filter(**{filter_field: None})
63 obj = model._default_manager.get(**{model_field: value})
64 except model.DoesNotExist:
67 return qs.filter(**{filter_field: obj})
70 def search_filter(qs, value, filter_fields):
74 for word in value.split():
75 m = re.match(r'(.+):(.+)', word)
79 q = Book.q_dc(field, field + 's', value, 'book__')
81 q = Q(**{"%s__icontains" % filter_fields[0]: value})
82 for field in filter_fields[1:]:
83 q |= Q(**{"%s__icontains" % field: value})
90 ('publishable', _('publishable'), Q(book___new_publishable=True)),
91 ('changed', _('changed'), Q(_changed=True)),
92 ('published', _('published'), Q(book___published=True)),
93 ('unpublished', _('unpublished'), Q(book___published=False)),
94 ('empty', _('empty'), Q(head=None)),
96 _states_options = [s[:2] for s in _states]
97 _states_dict = dict([(s[0], s[2]) for s in _states])
100 def document_list_filter(request, **kwargs):
102 def arg_or_GET(field):
103 return kwargs.get(field, request.GET.get(field))
105 if arg_or_GET('all'):
106 chunks = Chunk.objects.all()
108 chunks = Chunk.visible_objects.all()
110 chunks = chunks.order_by('book__title', 'book', 'number')
112 if not request.user.is_authenticated:
113 chunks = chunks.filter(book__public=True)
115 state = arg_or_GET('status')
116 if state in _states_dict:
117 chunks = chunks.filter(_states_dict[state])
119 chunks = foreign_filter(chunks, arg_or_GET('user'), 'user', User, 'username')
120 chunks = foreign_filter(chunks, arg_or_GET('stage'), 'stage', Chunk.tag_model, 'slug')
121 chunks = search_filter(chunks, arg_or_GET('title'), ['book__title', 'title'])
122 chunks = foreign_filter(chunks, arg_or_GET('project'), 'book__project', Project, 'pk')
126 @register.inclusion_tag('documents/book_list/book_list.html', takes_context=True)
127 def book_list(context, user=None):
128 request = context['request']
131 filters = {"user": user}
132 new_context = {"viewed_user": user}
136 "users": User.objects.annotate(
137 count=Count('chunk')).filter(count__gt=0).order_by(
138 '-count', 'last_name', 'first_name'),
139 "other_users": User.objects.annotate(
140 count=Count('chunk')).filter(count=0).annotate(m=Max('chunkchange__created_at')).order_by(F('m').desc(nulls_last=True), 'last_name', 'first_name'),
141 "active_users": User.objects.annotate(m=Max('chunkchange__created_at')).order_by(F('m').desc(nulls_last=True), 'last_name', 'first_name'),
147 "books": ChunksList(document_list_filter(request, **filters)),
148 "stages": Chunk.tag_model.objects.all(),
149 "states": _states_options,
150 "projects": Project.objects.all(),
158 ('publishable', _('publishable'), Q(_new_publishable=True)),
159 ('changed', _('changed'), Q(_changed=True)),
160 ('published', _('published'), Q(_published=True)),
161 ('unpublished', _('unpublished'), Q(_published=False)),
162 ('empty', _('empty'), Q(head=None)),
164 _image_states_options = [s[:2] for s in _image_states]
165 _image_states_dict = dict([(s[0], s[2]) for s in _image_states])
167 def image_list_filter(request, **kwargs):
169 def arg_or_GET(field):
170 return kwargs.get(field, request.GET.get(field))
172 images = Image.objects.all().select_related('user', 'stage', 'project')
174 if not request.user.is_authenticated:
175 images = images.filter(public=True)
177 state = arg_or_GET('status')
178 if state in _image_states_dict:
179 images = images.filter(_image_states_dict[state])
181 images = foreign_filter(images, arg_or_GET('user'), 'user', User, 'username')
182 images = foreign_filter(images, arg_or_GET('stage'), 'stage', Image.tag_model, 'slug')
183 images = search_filter(images, arg_or_GET('title'), ['title', 'title'])
184 images = foreign_filter(images, arg_or_GET('project'), 'project', Project, 'pk')
188 @register.inclusion_tag('documents/image_table.html', takes_context=True)
189 def image_list(context, user=None):
190 request = context['request']
193 filters = {"user": user}
194 new_context = {"viewed_user": user}
198 "users": User.objects.annotate(
199 count=Count('image')).filter(count__gt=0).order_by(
200 '-count', 'last_name', 'first_name'),
201 "other_users": User.objects.annotate(
202 count=Count('image')).filter(count=0).order_by(
203 'last_name', 'first_name'),
209 "objects": image_list_filter(request, **filters),
210 "stages": Image.tag_model.objects.all(),
211 "states": _image_states_options,
212 "projects": Project.objects.all(),