]> git.0d.be Git - django-panik-combo.git/blobdiff - panikombo/models.py
add introduction to fullwidth page placeholders
[django-panik-combo.git] / panikombo / models.py
index fd717090120082638d05a1ac878690a9272ea7d2..de99b6732726e6eb5535a8811d0d8b73c0a74e12 100644 (file)
@@ -1,19 +1,20 @@
-from datetime import date
+from datetime import date, datetime, timedelta
 
+from combo.data.library import register_cell_class
+from combo.data.models import CellBase
 from django import template
 from django.db import models
-from django.utils.translation import ugettext_lazy as _
-
+from django.utils.translation import gettext_lazy as _
+from emissions.models import Emission, Episode, Focus, NewsItem, SoundFile
+from emissions.utils import period_program
 from taggit.managers import TaggableManager
+from taggit.models import Tag
+from taggit.utils import parse_tags
 
-from combo.data.models import CellBase
-from combo.data.library import register_cell_class
-
-from emissions.models import Episode, NewsItem
 
 @register_cell_class
 class SoundCell(CellBase):
-    soundfile = models.ForeignKey('emissions.SoundFile', null=True)
+    soundfile = models.ForeignKey('emissions.SoundFile', null=True, on_delete=models.CASCADE)
 
     class Meta:
         verbose_name = _('Sound')
@@ -25,26 +26,31 @@ class SoundCell(CellBase):
 
     def get_default_form_class(self):
         from .forms import SoundCellForm
+
         return SoundCellForm
 
+    def get_included_items(self):
+        if not self.soundfile:
+            return []
+        return [self.soundfile.episode]
+
     def get_additional_label(self):
         if self.soundfile:
             if self.soundfile.fragment:
-                return u'%s - %s - %s' % (
-                        self.soundfile.episode.emission.title,
-                        self.soundfile.episode.title,
-                        self.soundfile.title)
+                return '%s - %s - %s' % (
+                    self.soundfile.episode.emission.title,
+                    self.soundfile.episode.title,
+                    self.soundfile.title,
+                )
             else:
-                return u'%s - %s' % (
-                        self.soundfile.episode.emission.title,
-                        self.soundfile.episode.title)
+                return '%s - %s' % (self.soundfile.episode.emission.title, self.soundfile.episode.title)
 
         return ''
 
 
 @register_cell_class
 class EpisodeCell(CellBase):
-    episode = models.ForeignKey('emissions.Episode', null=True)
+    episode = models.ForeignKey('emissions.Episode', null=True, on_delete=models.CASCADE)
 
     class Meta:
         verbose_name = _('Episode')
@@ -52,79 +58,369 @@ class EpisodeCell(CellBase):
     def render(self, context):
         tmpl = template.loader.get_template('panikombo/episode.html')
         context['episode'] = self.episode
-        context['soundfile'] = self.episode.main_sound
+        if self.episode:
+            context['soundfile'] = self.episode.main_sound
         return tmpl.render(context)
 
+    def get_included_items(self):
+        if not self.episode:
+            return []
+        return [self.episode]
+
     def get_default_form_class(self):
         from .forms import EpisodeCellForm
+
         return EpisodeCellForm
 
     def get_additional_label(self):
         if self.episode:
-            return u'%s - %s' % (
-                        self.episode.emission.title,
-                        self.episode.title)
+            return '%s - %s' % (self.episode.emission.title, self.episode.title)
         return ''
 
 
+def get_parsed_tags(tagstring):
+    tags = parse_tags(tagstring)
+    return [x for x in Tag.objects.filter(name__in=tags)]
+
+
 @register_cell_class
 class EpisodeAutoSelectionCell(CellBase):
     title = models.CharField(_('Title'), max_length=50, blank=True)
     tags = TaggableManager(_('Tags'), blank=True)
-    category = models.ForeignKey('emissions.Category', null=True, blank=True)
+    and_tags = models.CharField(_('And Tags'), max_length=100, blank=True)
+    category = models.ForeignKey('emissions.Category', null=True, blank=True, on_delete=models.CASCADE)
+    period = models.PositiveSmallIntegerField(
+        _('Period'), default=0, choices=((0, _('All')), (1, _('Future')), (2, _('Past')))
+    )
 
-    class Meta:
-        verbose_name = _('Automatic Episode Selection')
+    default_template_name = 'panikombo/episode_auto_selection.html'
 
-    def render(self, context):
-        tmpl = template.loader.get_template('panikombo/episode_auto_selection.html')
-        context['title'] = self.title
+    class Meta:
+        verbose_name = _('Episodes')
 
+    def get_included_items(self):
         episodes_queryset = Episode.objects.select_related()
         if self.category:
             episodes_queryset = episodes_queryset.filter(emission__categories__in=[self.category.id])
         if self.tags.count():
             episodes_queryset = episodes_queryset.filter(tags__in=self.tags.all())
+            if self.and_tags:
+                and_tags = get_parsed_tags(self.and_tags)
+                episodes_queryset = episodes_queryset.filter(tags__in=and_tags)
 
-        episodes_queryset = episodes_queryset.extra(select={
-                        'first_diffusion': 'emissions_diffusion.datetime',
-                        },
-                        select_params=(False, True),
-                        where=['''datetime = (SELECT MIN(datetime)
-                                                FROM emissions_diffusion
-                                               WHERE episode_id = emissions_episode.id)'''],
-                        tables=['emissions_diffusion'],
-                    ).order_by('-first_diffusion').distinct()
-        context['episodes'] = episodes_queryset
-        return tmpl.render(context)
+        if self.period == 0:
+            episodes_queryset = episodes_queryset.extra(
+                select={
+                    'first_diffusion': 'emissions_diffusion.datetime',
+                },
+                select_params=(False, True),
+                where=[
+                    '''datetime = (SELECT MIN(datetime) FROM emissions_diffusion
+                                           WHERE episode_id = emissions_episode.id)'''
+                ],
+                tables=['emissions_diffusion'],
+            )
+            episodes_queryset = episodes_queryset.order_by('first_diffusion').distinct()
+        elif self.period == 1:
+            episodes_queryset = episodes_queryset.extra(
+                select={
+                    'first_diffusion': 'emissions_diffusion.datetime',
+                },
+                select_params=(False, True),
+                where=[
+                    '''datetime = (SELECT MIN(datetime) FROM emissions_diffusion
+                                           WHERE episode_id = emissions_episode.id) AND
+                                                 datetime >= CURRENT_TIMESTAMP'''
+                ],
+                tables=['emissions_diffusion'],
+            )
+            episodes_queryset = episodes_queryset.order_by('-first_diffusion').distinct()
+        elif self.period == 2:
+            episodes_queryset = episodes_queryset.extra(
+                select={
+                    'first_diffusion': 'emissions_diffusion.datetime',
+                },
+                select_params=(False, True),
+                where=[
+                    '''datetime = (SELECT MIN(datetime) FROM emissions_diffusion
+                                           WHERE episode_id = emissions_episode.id) AND
+                                                 datetime < CURRENT_TIMESTAMP'''
+                ],
+                tables=['emissions_diffusion'],
+            )
+            episodes_queryset = episodes_queryset.order_by('-first_diffusion').distinct()
+
+        return episodes_queryset
+
+    def get_cell_extra_context(self, context):
+        ctx = super().get_cell_extra_context(context)
+        ctx['title'] = self.title
+
+        if self.category or self.period or self.tags.count():
+            ctx['episodes'] = self.get_included_items()
+        else:
+            ctx['episodes'] = []
+
+        return ctx
 
     def get_default_form_class(self):
         from .forms import EpisodeAutoSelectionCellForm
+
         return EpisodeAutoSelectionCellForm
 
+    def get_additional_label(self):
+        if self.title:
+            return self.title
+        return ''
+
 
 @register_cell_class
 class NewsItemAutoSelectionCell(CellBase):
     title = models.CharField(_('Title'), max_length=50, blank=True)
     tags = TaggableManager(_('Tags'), blank=True)
+    and_tags = models.CharField(_('And Tags'), max_length=100, blank=True)
     future = models.BooleanField(_('Future Events Only'), default=True)
+    category = models.ForeignKey(
+        'emissions.NewsCategory', verbose_name=_('Category'), null=True, blank=True, on_delete=models.SET_NULL
+    )
+    count = models.PositiveSmallIntegerField(_('Count'), default=20)
 
-    class Meta:
-        verbose_name = _('Automatic Newsitem Selection')
+    default_template_name = 'panikombo/newsitem_auto_selection.html'
 
-    def render(self, context):
-        tmpl = template.loader.get_template('panikombo/newsitem_auto_selection.html')
-        context['title'] = self.title
+    class Meta:
+        verbose_name = _('Newsitems')
 
+    def get_included_items(self):
         newsitems_queryset = NewsItem.objects.select_related()
         if self.tags.count():
             newsitems_queryset = newsitems_queryset.filter(tags__in=self.tags.all())
+            if self.and_tags:
+                and_tags = get_parsed_tags(self.and_tags)
+                newsitems_queryset = newsitems_queryset.filter(tags__in=and_tags)
         if self.future:
             newsitems_queryset = newsitems_queryset.filter(event_date__gte=date.today())
+        if self.category:
+            newsitems_queryset = newsitems_queryset.filter(category=self.category)
+        newsitems_queryset = newsitems_queryset.order_by('-event_date', '-creation_timestamp')
+        return newsitems_queryset[: self.count]
 
-        context['newsitems'] = newsitems_queryset
-        return tmpl.render(context)
+    def get_cell_extra_context(self, context):
+        ctx = super().get_cell_extra_context(context)
+        ctx['title'] = self.title
+        ctx['newsitems'] = self.get_included_items()
+        return ctx
 
     def get_default_form_class(self):
         from .forms import NewsItemAutoSelectionCellForm
+
         return NewsItemAutoSelectionCellForm
+
+    def get_additional_label(self):
+        if self.title:
+            return self.title
+        return ''
+
+
+class ItemTopik(models.Model):
+    newsitem = models.ForeignKey(
+        'emissions.NewsItem', verbose_name=_('News Item'), null=True, blank=True, on_delete=models.SET_NULL
+    )
+    episode = models.ForeignKey(
+        'emissions.Episode', verbose_name=_('Episode'), null=True, blank=True, on_delete=models.SET_NULL
+    )
+    page = models.ForeignKey('data.Page', null=True, blank=True, on_delete=models.SET_NULL)
+
+
+@register_cell_class
+class SoundsCell(CellBase):
+    title = models.CharField(_('Title'), max_length=150, blank=True)
+    include_search_input = models.BooleanField(_('Include search input'), default=True)
+    include_fragments = models.BooleanField(_('Include fragments'), default=True)
+    limit_to_focus = models.BooleanField(_('Limit to focused elements'), default=False)
+    sound_format = models.ForeignKey(
+        'emissions.Format', verbose_name=_('Limit to format'), null=True, blank=True, on_delete=models.CASCADE
+    )
+    tags = TaggableManager(_('Tags'), blank=True)
+    minimal_duration = models.PositiveIntegerField(
+        _('Minimal duration (in minutes)'), default=None, blank=True, null=True
+    )
+    maximal_duration = models.PositiveIntegerField(
+        _('Maximal duration (in minutes)'), default=None, blank=True, null=True
+    )
+    count = models.PositiveSmallIntegerField(_('Count'), default=20)
+    sort_order = models.CharField(
+        _('Sort order'),
+        default='-creation_timestamp',
+        max_length=30,
+        choices=[
+            ('-creation_timestamp', _('Reverse chronological (creation)')),
+            ('-first_diffusion', _('Reverse chronological (diffusion)')),
+            ('creation_timestamp', _('Chronological (creation)')),
+            ('first_diffusion', _('Chronological (diffusion)')),
+            ('?', _('Random')),
+        ],
+    )
+
+    class Meta:
+        verbose_name = _('Sounds')
+
+    def get_default_form_fields(self):
+        fields = super().get_default_form_fields()
+        fields.insert(fields.index('minimal_duration'), 'tags')
+        return fields
+
+    def get_cell_extra_context(self, context):
+        soundfiles = SoundFile.objects.prefetch_related('episode__emission__categories')
+        soundfiles = soundfiles.filter(podcastable=True)
+        soundfiles = soundfiles.exclude(creation_timestamp__isnull=True)
+        if not self.include_fragments:
+            soundfiles = soundfiles.filter(fragment=False)
+        if self.limit_to_focus:
+            soundfiles = soundfiles.filter(got_focus__isnull=False)
+        if self.sound_format:
+            soundfiles = soundfiles.filter(format_id=self.sound_format_id)
+        if self.minimal_duration:
+            soundfiles = soundfiles.filter(duration__gte=self.minimal_duration * 60)
+        if self.maximal_duration:
+            soundfiles = soundfiles.filter(duration__lte=self.maximal_duration * 60)
+        if self.tags.exists():
+            soundfiles = soundfiles.filter(episode__tags__in=self.tags.all())
+        soundfiles = (
+            soundfiles.select_related()
+            .extra(
+                select={
+                    'first_diffusion': 'emissions_diffusion.datetime',
+                },
+                select_params=(False, True),
+                where=[
+                    '''datetime = (SELECT MIN(datetime)
+                                            FROM emissions_diffusion
+                                        WHERE episode_id = emissions_episode.id)'''
+                ],
+                tables=['emissions_diffusion'],
+            )
+            .order_by(self.sort_order)
+            .distinct()
+        )
+        return {
+            'include_search_input': self.include_search_input,
+            'soundfiles': soundfiles[: self.count],
+        }
+
+
+@register_cell_class
+class WeekProgramCell(CellBase):
+    include_nonstop = models.BooleanField(_('Include nonstop'), default=True)
+
+    default_template_name = 'panikombo/week_program.html'
+
+    class Meta:
+        verbose_name = _('Week Program')
+
+    @staticmethod
+    def tofirstdayinisoweek(year, week):
+        # from http://stackoverflow.com/questions/5882405/get-date-from-iso-week-number-in-python
+        ret = datetime.strptime('%04d-%02d-1' % (year, week), '%Y-%W-%w')
+        if datetime(year, 1, 4).isoweekday() > 4:
+            ret -= timedelta(days=7)
+        return ret
+
+    def get_cell_extra_context(self, context):
+        ctx = super().get_cell_extra_context(context)
+        year = context['request'].GET.get('year') if 'request' in context else None
+        week = context['request'].GET.get('week') if 'request' in context else None
+        weekday = context['request'].GET.get('weekday') if 'request' in context else None
+        year = int(year if year else datetime.today().isocalendar()[0])
+        week = int(week if week else datetime.today().isocalendar()[1])
+        weekday = int(weekday if weekday is not None else datetime.today().weekday())
+
+        date = WeekProgramCell.tofirstdayinisoweek(year, week)
+        date = datetime(*date.timetuple()[:3])
+
+        previous_week = date - timedelta(days=7)
+        previous_week_year, previous_week_no = previous_week.isocalendar()[:2]
+
+        next_week = date + timedelta(days=7)
+        next_week_year, next_week_no = next_week.isocalendar()[:2]
+
+        program = period_program(date, date + timedelta(days=7), include_nonstop=self.include_nonstop)
+        days = []
+        for day in range(7):
+            days.append(
+                {
+                    'cells': [x for x in program if x.is_on_weekday(day + 1)],
+                    'datetime': date + timedelta(days=day),
+                }
+            )
+
+        ctx.update(
+            {
+                'days': days,
+                'weekday': weekday,
+                'week': week,
+                'year': year,
+                'previous_week_year': previous_week_year,
+                'previous_week_no': previous_week_no,
+                'next_week_year': next_week_year,
+                'next_week_no': next_week_no,
+            }
+        )
+
+        return ctx
+
+
+@register_cell_class
+class FocusCarrouselCell(CellBase):
+    count = models.PositiveSmallIntegerField(_('Count'), default=3)
+
+    default_template_name = 'panikombo/focus_carrousel.html'
+
+    class Meta:
+        verbose_name = _('Focus carrousel')
+
+    def get_cell_extra_context(self, context):
+        ctx = super().get_cell_extra_context(context)
+        ctx['news'] = (
+            Focus.objects.filter(current=True)
+            .select_related('emission', 'newsitem', 'soundfile', 'episode', 'newsitem__category')
+            .order_by('?')[: self.count]
+        )
+        return ctx
+
+
+@register_cell_class
+class EmissionsCell(CellBase):
+    title = models.CharField(_('Title'), max_length=50, blank=True)
+    count = models.PositiveSmallIntegerField(_('Count'), default=3)
+    include_active = models.BooleanField(_('Include active'), default=True)
+    include_archived = models.BooleanField(_('Include archived'), default=False)
+    sort_order = models.CharField(
+        _('Sort order'),
+        default='-creation_timestamp',
+        max_length=30,
+        choices=[
+            ('title', _('Alphabetical')),
+            ('-creation_timestamp', _('Reverse chronological (latest emissions)')),
+            ('?', _('Random')),
+        ],
+    )
+
+    default_template_name = 'panikombo/emissions.html'
+
+    class Meta:
+        verbose_name = _('Emissions')
+
+    def get_cell_extra_context(self, context):
+        ctx = super().get_cell_extra_context(context)
+
+        qs = Emission.objects.all()
+        if not self.include_active:
+            qs = qs.exclude(archived=False)
+        if not self.include_archived:
+            qs = qs.exclude(archived=True)
+
+        qs = qs.order_by(self.sort_order)
+
+        ctx['emissions'] = qs[: self.count]
+
+        return ctx