]> git.0d.be Git - django-panik-combo.git/blob - panikombo/models.py
trivial: apply new pre-commit configuration
[django-panik-combo.git] / panikombo / models.py
1 import os
2 from datetime import date
3
4 from ckeditor.fields import RichTextField
5 from combo.data.library import register_cell_class
6 from combo.data.models import CellBase
7 from django import template
8 from django.db import models
9 from django.db.models.functions import Lower
10 from django.utils.encoding import force_text, python_2_unicode_compatible
11 from django.utils.translation import ugettext_lazy as _
12 from emissions.models import Episode, NewsItem, SoundFile
13 from taggit.managers import TaggableManager
14 from taggit.models import Tag
15 from taggit.utils import parse_tags
16
17
18 @register_cell_class
19 class SoundCell(CellBase):
20     soundfile = models.ForeignKey('emissions.SoundFile', null=True, on_delete=models.CASCADE)
21
22     class Meta:
23         verbose_name = _('Sound')
24
25     def render(self, context):
26         tmpl = template.loader.get_template('panikombo/audio.html')
27         context['soundfile'] = self.soundfile
28         return tmpl.render(context)
29
30     def get_default_form_class(self):
31         from .forms import SoundCellForm
32
33         return SoundCellForm
34
35     def get_included_items(self):
36         if not self.soundfile:
37             return []
38         return [self.soundfile.episode]
39
40     def get_additional_label(self):
41         if self.soundfile:
42             if self.soundfile.fragment:
43                 return '%s - %s - %s' % (
44                     self.soundfile.episode.emission.title,
45                     self.soundfile.episode.title,
46                     self.soundfile.title,
47                 )
48             else:
49                 return '%s - %s' % (self.soundfile.episode.emission.title, self.soundfile.episode.title)
50
51         return ''
52
53
54 @register_cell_class
55 class EpisodeCell(CellBase):
56     episode = models.ForeignKey('emissions.Episode', null=True, on_delete=models.CASCADE)
57
58     class Meta:
59         verbose_name = _('Episode')
60
61     def render(self, context):
62         tmpl = template.loader.get_template('panikombo/episode.html')
63         context['episode'] = self.episode
64         if self.episode:
65             context['soundfile'] = self.episode.main_sound
66         return tmpl.render(context)
67
68     def get_included_items(self):
69         if not self.episode:
70             return []
71         return [self.episode]
72
73     def get_default_form_class(self):
74         from .forms import EpisodeCellForm
75
76         return EpisodeCellForm
77
78     def get_additional_label(self):
79         if self.episode:
80             return '%s - %s' % (self.episode.emission.title, self.episode.title)
81         return ''
82
83
84 def get_parsed_tags(tagstring):
85     tags = parse_tags(tagstring)
86     return [x for x in Tag.objects.filter(name__in=tags)]
87
88
89 @register_cell_class
90 class EpisodeAutoSelectionCell(CellBase):
91     title = models.CharField(_('Title'), max_length=50, blank=True)
92     tags = TaggableManager(_('Tags'), blank=True)
93     and_tags = models.CharField(_('And Tags'), max_length=100, blank=True)
94     category = models.ForeignKey('emissions.Category', null=True, blank=True, on_delete=models.CASCADE)
95     period = models.PositiveSmallIntegerField(
96         _('Period'), default=0, choices=((0, _('All')), (1, _('Future')), (2, _('Past')))
97     )
98
99     default_template_name = 'panikombo/episode_auto_selection.html'
100
101     class Meta:
102         verbose_name = _('Automatic Episode Selection')
103
104     def get_included_items(self):
105         episodes_queryset = Episode.objects.select_related()
106         if self.category:
107             episodes_queryset = episodes_queryset.filter(emission__categories__in=[self.category.id])
108         if self.tags.count():
109             episodes_queryset = episodes_queryset.filter(tags__in=self.tags.all())
110             if self.and_tags:
111                 and_tags = get_parsed_tags(self.and_tags)
112                 episodes_queryset = episodes_queryset.filter(tags__in=and_tags)
113
114         if self.period == 0:
115             episodes_queryset = episodes_queryset.extra(
116                 select={
117                     'first_diffusion': 'emissions_diffusion.datetime',
118                 },
119                 select_params=(False, True),
120                 where=[
121                     '''datetime = (SELECT MIN(datetime) FROM emissions_diffusion
122                                            WHERE episode_id = emissions_episode.id)'''
123                 ],
124                 tables=['emissions_diffusion'],
125             )
126             episodes_queryset = episodes_queryset.order_by('first_diffusion').distinct()
127         elif self.period == 1:
128             episodes_queryset = episodes_queryset.extra(
129                 select={
130                     'first_diffusion': 'emissions_diffusion.datetime',
131                 },
132                 select_params=(False, True),
133                 where=[
134                     '''datetime = (SELECT MIN(datetime) FROM emissions_diffusion
135                                            WHERE episode_id = emissions_episode.id) AND
136                                                  datetime >= CURRENT_TIMESTAMP'''
137                 ],
138                 tables=['emissions_diffusion'],
139             )
140             episodes_queryset = episodes_queryset.order_by('-first_diffusion').distinct()
141         elif self.period == 2:
142             episodes_queryset = episodes_queryset.extra(
143                 select={
144                     'first_diffusion': 'emissions_diffusion.datetime',
145                 },
146                 select_params=(False, True),
147                 where=[
148                     '''datetime = (SELECT MIN(datetime) FROM emissions_diffusion
149                                            WHERE episode_id = emissions_episode.id) AND
150                                                  datetime < CURRENT_TIMESTAMP'''
151                 ],
152                 tables=['emissions_diffusion'],
153             )
154             episodes_queryset = episodes_queryset.order_by('-first_diffusion').distinct()
155
156         return episodes_queryset
157
158     def get_cell_extra_context(self, context):
159         ctx = super().get_cell_extra_context(context)
160         ctx['title'] = self.title
161
162         if self.category or self.period or self.tags.count():
163             ctx['episodes'] = self.get_included_items()
164         else:
165             ctx['episodes'] = []
166
167         return ctx
168
169     def get_default_form_class(self):
170         from .forms import EpisodeAutoSelectionCellForm
171
172         return EpisodeAutoSelectionCellForm
173
174     def get_additional_label(self):
175         if self.title:
176             return self.title
177         return ''
178
179
180 @register_cell_class
181 class NewsItemAutoSelectionCell(CellBase):
182     title = models.CharField(_('Title'), max_length=50, blank=True)
183     tags = TaggableManager(_('Tags'), blank=True)
184     and_tags = models.CharField(_('And Tags'), max_length=100, blank=True)
185     future = models.BooleanField(_('Future Events Only'), default=True)
186     category = models.ForeignKey(
187         'emissions.NewsCategory', verbose_name=_('Category'), null=True, blank=True, on_delete=models.SET_NULL
188     )
189
190     default_template_name = 'panikombo/newsitem_auto_selection.html'
191
192     class Meta:
193         verbose_name = _('Automatic Newsitem Selection')
194
195     def get_included_items(self):
196         newsitems_queryset = NewsItem.objects.select_related()
197         if self.tags.count():
198             newsitems_queryset = newsitems_queryset.filter(tags__in=self.tags.all())
199             if self.and_tags:
200                 and_tags = get_parsed_tags(self.and_tags)
201                 newsitems_queryset = newsitems_queryset.filter(tags__in=and_tags)
202         if self.future:
203             newsitems_queryset = newsitems_queryset.filter(event_date__gte=date.today())
204         if self.category:
205             newsitems_queryset = newsitems_queryset.filter(category=self.category)
206         newsitems_queryset = newsitems_queryset.order_by('-event_date', '-creation_timestamp')
207         return newsitems_queryset
208
209     def get_cell_extra_context(self, context):
210         ctx = super().get_cell_extra_context(context)
211         ctx['title'] = self.title
212
213         if self.tags.count() or self.future or self.category:
214             ctx['newsitems'] = self.get_included_items()
215         else:
216             ctx['newsitems'] = []
217
218         return ctx
219
220     def get_default_form_class(self):
221         from .forms import NewsItemAutoSelectionCellForm
222
223         return NewsItemAutoSelectionCellForm
224
225     def get_additional_label(self):
226         if self.title:
227             return self.title
228         return ''
229
230
231 class ItemTopik(models.Model):
232     newsitem = models.ForeignKey(
233         'emissions.NewsItem', verbose_name=_('News Item'), null=True, blank=True, on_delete=models.SET_NULL
234     )
235     episode = models.ForeignKey(
236         'emissions.Episode', verbose_name=_('Episode'), null=True, blank=True, on_delete=models.SET_NULL
237     )
238     page = models.ForeignKey('data.Page', null=True, blank=True, on_delete=models.SET_NULL)
239
240
241 @register_cell_class
242 class SoundsCell(CellBase):
243     title = models.CharField(_('Title'), max_length=150, blank=True)
244     include_search_input = models.BooleanField(_('Include search input'), default=True)
245     include_fragments = models.BooleanField(_('Include fragments'), default=True)
246     limit_to_focus = models.BooleanField(_('Limit to focused elements'), default=False)
247     sound_format = models.ForeignKey(
248         'emissions.Format', verbose_name=_('Limit to format'), null=True, blank=True, on_delete=models.CASCADE
249     )
250     tags = TaggableManager(_('Tags'), blank=True)
251     minimal_duration = models.PositiveIntegerField(
252         _('Minimal duration (in minutes)'), default=None, blank=True, null=True
253     )
254     maximal_duration = models.PositiveIntegerField(
255         _('Maximal duration (in minutes)'), default=None, blank=True, null=True
256     )
257     count = models.PositiveSmallIntegerField(_('Count'), default=20)
258     sort_order = models.CharField(
259         _('Sort order'),
260         default='-creation_timestamp',
261         max_length=30,
262         choices=[
263             ('-creation_timestamp', _('Reverse chronological (creation)')),
264             ('-first_diffusion', _('Reverse chronological (diffusion)')),
265             ('creation_timestamp', _('Chronological (creation)')),
266             ('first_diffusion', _('Chronological (diffusion)')),
267             ('?', _('Random')),
268         ],
269     )
270
271     class Meta:
272         verbose_name = _('Sounds')
273
274     def get_default_form_fields(self):
275         fields = super().get_default_form_fields()
276         fields.insert(fields.index('minimal_duration'), 'tags')
277         return fields
278
279     def get_cell_extra_context(self, context):
280         soundfiles = SoundFile.objects.prefetch_related('episode__emission__categories')
281         soundfiles = soundfiles.filter(podcastable=True)
282         if not self.include_fragments:
283             soundfiles = soundfiles.filter(fragment=False)
284         if self.limit_to_focus:
285             soundfiles = soundfiles.filter(got_focus__isnull=False)
286         if self.sound_format:
287             soundfiles = soundfiles.filter(format_id=self.sound_format_id)
288         if self.minimal_duration:
289             soundfiles = soundfiles.filter(duration__gte=self.minimal_duration * 60)
290         if self.maximal_duration:
291             soundfiles = soundfiles.filter(duration__lte=self.maximal_duration * 60)
292         if self.tags.exists():
293             soundfiles = soundfiles.filter(episode__tags__in=self.tags.all())
294         soundfiles = (
295             soundfiles.select_related()
296             .extra(
297                 select={
298                     'first_diffusion': 'emissions_diffusion.datetime',
299                 },
300                 select_params=(False, True),
301                 where=[
302                     '''datetime = (SELECT MIN(datetime)
303                                             FROM emissions_diffusion
304                                         WHERE episode_id = emissions_episode.id)'''
305                 ],
306                 tables=['emissions_diffusion'],
307             )
308             .order_by(self.sort_order)
309             .distinct()
310         )
311         return {
312             'include_search_input': self.include_search_input,
313             'soundfiles': soundfiles[: self.count],
314         }