add date_list; LogsPage; ListPage
This commit is contained in:
		
							
								
								
									
										403
									
								
								cms/models.py
									
									
									
									
									
								
							
							
						
						
									
										403
									
								
								cms/models.py
									
									
									
									
									
								
							@ -1,3 +1,6 @@
 | 
			
		||||
import datetime
 | 
			
		||||
import re
 | 
			
		||||
from enum import Enum, IntEnum
 | 
			
		||||
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.contrib.contenttypes.models import ContentType
 | 
			
		||||
@ -30,16 +33,32 @@ from taggit.models import TaggedItemBase
 | 
			
		||||
import bleach
 | 
			
		||||
 | 
			
		||||
import aircox.programs.models as programs
 | 
			
		||||
import aircox.controllers.models as controllers
 | 
			
		||||
import aircox.cms.settings as settings
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ListItem:
 | 
			
		||||
    """
 | 
			
		||||
    Generic normalized element to add item in lists that are not based
 | 
			
		||||
    on Publication.
 | 
			
		||||
    """
 | 
			
		||||
    title = ''
 | 
			
		||||
    summary = ''
 | 
			
		||||
    url = ''
 | 
			
		||||
    cover = None
 | 
			
		||||
    date = None
 | 
			
		||||
 | 
			
		||||
    def __init__(self, **kwargs):
 | 
			
		||||
        self.__dict__.update(kwargs)
 | 
			
		||||
        self.specific = self
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@register_setting
 | 
			
		||||
class WebsiteSettings(BaseSetting):
 | 
			
		||||
    logo = models.ForeignKey(
 | 
			
		||||
        'wagtailimages.Image',
 | 
			
		||||
        verbose_name = _('logo'),
 | 
			
		||||
        null=True, blank=True,
 | 
			
		||||
        on_delete=models.SET_NULL,
 | 
			
		||||
        null=True, blank=True, on_delete=models.SET_NULL,
 | 
			
		||||
        related_name='+',
 | 
			
		||||
        help_text = _('logo of the website'),
 | 
			
		||||
    )
 | 
			
		||||
@ -91,8 +110,10 @@ class WebsiteSettings(BaseSetting):
 | 
			
		||||
        verbose_name = _('website settings')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RelatedLink(Orderable):
 | 
			
		||||
    parent = ParentalKey('Publication', related_name='related_links')
 | 
			
		||||
#
 | 
			
		||||
#   Base
 | 
			
		||||
#
 | 
			
		||||
class BaseRelatedLink(Orderable):
 | 
			
		||||
    url = models.URLField(
 | 
			
		||||
        _('url'),
 | 
			
		||||
        help_text = _('URL of the link'),
 | 
			
		||||
@ -112,6 +133,9 @@ class RelatedLink(Orderable):
 | 
			
		||||
        help_text = _('text to display of the link'),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        abstract = True
 | 
			
		||||
 | 
			
		||||
    panels = [
 | 
			
		||||
        FieldPanel('url'),
 | 
			
		||||
        FieldRowPanel([
 | 
			
		||||
@ -121,6 +145,9 @@ class RelatedLink(Orderable):
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Publications
 | 
			
		||||
#
 | 
			
		||||
@register_snippet
 | 
			
		||||
class Comment(models.Model):
 | 
			
		||||
    publication = models.ForeignKey(
 | 
			
		||||
@ -170,6 +197,9 @@ class Comment(models.Model):
 | 
			
		||||
        return super().save(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class RelatedLink(BaseRelatedLink):
 | 
			
		||||
    parent = ParentalKey('Publication', related_name='related_links')
 | 
			
		||||
 | 
			
		||||
class PublicationTag(TaggedItemBase):
 | 
			
		||||
    content_object = ParentalKey('Publication', related_name='tagged_items')
 | 
			
		||||
 | 
			
		||||
@ -248,80 +278,6 @@ class Publication(Page):
 | 
			
		||||
            published = True,
 | 
			
		||||
        ).order_by('-date')
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_queryset(cl, request, *args,
 | 
			
		||||
                     thread = None, context = {},
 | 
			
		||||
                     **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Return a queryset from the request's GET parameters. Context
 | 
			
		||||
        can be used to update relative informations.
 | 
			
		||||
 | 
			
		||||
        Parameters:
 | 
			
		||||
        * type:     ['program','diffusion','event'] type of the publication
 | 
			
		||||
        * tag:      tag to search for
 | 
			
		||||
        * search:   query to search in the publications
 | 
			
		||||
        * thread:   children of the thread passed in arguments only
 | 
			
		||||
        * order:    ['asc','desc'] sort ordering
 | 
			
		||||
        * page:     page number
 | 
			
		||||
 | 
			
		||||
        Context's fields:
 | 
			
		||||
        * list_type:        type of the items in the list (as Page subclass)
 | 
			
		||||
        * tag_query:        tag searched for
 | 
			
		||||
        * search_query:     search terms
 | 
			
		||||
        * thread_query:     thread
 | 
			
		||||
        * paginator:        paginator object
 | 
			
		||||
        """
 | 
			
		||||
        if 'thread' in request.GET and thread:
 | 
			
		||||
            qs = self.get_children()
 | 
			
		||||
            context['thread_query'] = thread
 | 
			
		||||
        else:
 | 
			
		||||
            qs = cl.objects.all()
 | 
			
		||||
        qs = qs.not_in_menu().live()
 | 
			
		||||
 | 
			
		||||
        # type
 | 
			
		||||
        type = request.GET.get('type')
 | 
			
		||||
        if type == 'program':
 | 
			
		||||
            qs = qs.type(ProgramPage)
 | 
			
		||||
            context['list_type'] = ProgramPage
 | 
			
		||||
        elif type == 'diffusion':
 | 
			
		||||
            qs = qs.type(DiffusionPage)
 | 
			
		||||
            context['list_type'] = DiffusionPage
 | 
			
		||||
        elif type == 'event':
 | 
			
		||||
            qs = qs.type(EventPage)
 | 
			
		||||
            context['list_type'] = EventPage
 | 
			
		||||
 | 
			
		||||
        # filter by tag
 | 
			
		||||
        tag = request.GET.get('tag')
 | 
			
		||||
        if tag:
 | 
			
		||||
            context['tag_query'] = tag
 | 
			
		||||
            qs = qs.filter(tags__name = tag)
 | 
			
		||||
 | 
			
		||||
        # search
 | 
			
		||||
        search = request.GET.get('search')
 | 
			
		||||
        if search:
 | 
			
		||||
            context['search_query'] = search
 | 
			
		||||
            qs = qs.search(search)
 | 
			
		||||
 | 
			
		||||
        # ordering
 | 
			
		||||
        order = request.GET.get('order')
 | 
			
		||||
        if order not in ('asc','desc'):
 | 
			
		||||
            order = 'desc'
 | 
			
		||||
        qs = qs.order_by(
 | 
			
		||||
            ('-' if order == 'desc' else '') + 'first_published_at'
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        qs = self.get_queryset(request, *args, context, **kwargs)
 | 
			
		||||
        if qs:
 | 
			
		||||
            paginator = Paginator(qs, 30)
 | 
			
		||||
            try:
 | 
			
		||||
                qs = paginator.page('page')
 | 
			
		||||
            except PageNotAnInteger:
 | 
			
		||||
                qs = paginator.page(1)
 | 
			
		||||
            except EmptyPage:
 | 
			
		||||
                qs = parginator.page(paginator.num_pages)
 | 
			
		||||
            context['paginator'] = paginator
 | 
			
		||||
        return qs
 | 
			
		||||
 | 
			
		||||
    def get_context(self, request, *args, **kwargs):
 | 
			
		||||
        from aircox.cms.forms import CommentForm
 | 
			
		||||
        context = super().get_context(request, *args, **kwargs)
 | 
			
		||||
@ -333,7 +289,7 @@ class Publication(Page):
 | 
			
		||||
            context['comment_form'] = CommentForm()
 | 
			
		||||
 | 
			
		||||
        if view == 'list':
 | 
			
		||||
            context['object_list'] = self.get_queryset(
 | 
			
		||||
            context['object_list'] = ListPage.get_queryset(
 | 
			
		||||
                request, *args, context = context, thread = self, **kwargs
 | 
			
		||||
            )
 | 
			
		||||
        return context
 | 
			
		||||
@ -366,6 +322,7 @@ class ProgramPage(Publication):
 | 
			
		||||
    program = models.ForeignKey(
 | 
			
		||||
        programs.Program,
 | 
			
		||||
        verbose_name = _('program'),
 | 
			
		||||
        related_name = 'page',
 | 
			
		||||
        on_delete=models.SET_NULL,
 | 
			
		||||
        blank=True, null=True,
 | 
			
		||||
    )
 | 
			
		||||
@ -403,20 +360,22 @@ class ProgramPage(Publication):
 | 
			
		||||
                    ),
 | 
			
		||||
                    cover = self.cover,
 | 
			
		||||
                    live = True,
 | 
			
		||||
                    date = diff.start,
 | 
			
		||||
                )
 | 
			
		||||
            diff.page_.date = diff.start
 | 
			
		||||
        return [
 | 
			
		||||
            diff.page_ for diff in diffs if diff.page_.live
 | 
			
		||||
        ]
 | 
			
		||||
 | 
			
		||||
    def next_diffs(self):
 | 
			
		||||
    @property
 | 
			
		||||
    def next(self):
 | 
			
		||||
        now = tz.now()
 | 
			
		||||
        diffs = programs.Diffusion.objects \
 | 
			
		||||
                    .filter(end__gte = now, program = self.program) \
 | 
			
		||||
                    .order_by('start').prefetch_related('page')
 | 
			
		||||
        return self.diffs_to_page(diffs)
 | 
			
		||||
 | 
			
		||||
    def prev_diffs(self):
 | 
			
		||||
    @property
 | 
			
		||||
    def prev(self):
 | 
			
		||||
        now = tz.now()
 | 
			
		||||
        diffs = programs.Diffusion.objects \
 | 
			
		||||
                    .filter(end__lte = now, program = self.program) \
 | 
			
		||||
@ -471,6 +430,32 @@ class DiffusionPage(Publication):
 | 
			
		||||
        InlinePanel('tracks', label=_('Tracks'))
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def as_item(cl, diff):
 | 
			
		||||
        """
 | 
			
		||||
        Return a DiffusionPage or ListItem from a Diffusion
 | 
			
		||||
        """
 | 
			
		||||
        if diff.page.all().count():
 | 
			
		||||
            item = diff.page.live().first()
 | 
			
		||||
        else:
 | 
			
		||||
            item = ListItem(
 | 
			
		||||
                title = '{}, {}'.format(
 | 
			
		||||
                    diff.program.name, diff.date.strftime('%d %B %Y')
 | 
			
		||||
                ),
 | 
			
		||||
                cover = (diff.program.page.count() and \
 | 
			
		||||
                            diff.program.page.first().cover) or '',
 | 
			
		||||
                live = True,
 | 
			
		||||
                date = diff.start,
 | 
			
		||||
            )
 | 
			
		||||
 | 
			
		||||
        if diff.initial:
 | 
			
		||||
            item.info = _('Rerun of %(date)s') % {
 | 
			
		||||
                'date': diff.initial.start.strftime('%A %d')
 | 
			
		||||
            }
 | 
			
		||||
        diff.css_class = 'diffusion'
 | 
			
		||||
 | 
			
		||||
        return item
 | 
			
		||||
 | 
			
		||||
    def save(self, *args, **kwargs):
 | 
			
		||||
        if self.diffusion:
 | 
			
		||||
            self.first_published_at = self.diffusion.start
 | 
			
		||||
@ -528,6 +513,260 @@ class EventPage(Publication):
 | 
			
		||||
        super().save(*args, **kwargs)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Indexes
 | 
			
		||||
#
 | 
			
		||||
class BaseDateList(models.Model):
 | 
			
		||||
    nav_days = models.SmallIntegerField(
 | 
			
		||||
        _('navigation days count'),
 | 
			
		||||
        default = 7,
 | 
			
		||||
        help_text = _('number of days to display in the navigation header '
 | 
			
		||||
                      'when we use dates')
 | 
			
		||||
    )
 | 
			
		||||
    nav_per_week = models.BooleanField(
 | 
			
		||||
        _('navigation per week'),
 | 
			
		||||
        default = False,
 | 
			
		||||
        help_text = _('if selected, show dates navigation per weeks instead '
 | 
			
		||||
                      'of show days equally around the current date')
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @staticmethod
 | 
			
		||||
    def str_to_date(date):
 | 
			
		||||
        """
 | 
			
		||||
        Parse a string and return a regular date or None.
 | 
			
		||||
        Format is either "YYYY/MM/DD" or "YYYY-MM-DD" or "YYYYMMDD"
 | 
			
		||||
        """
 | 
			
		||||
        try:
 | 
			
		||||
            exp = r'(?P<year>[0-9]{4})(-|\/)?(?P<month>[0-9]{1,2})(-|\/)?' \
 | 
			
		||||
                  r'(?P<day>[0-9]{1,2})'
 | 
			
		||||
            date = re.match(exp, date).groupdict()
 | 
			
		||||
            return datetime.date(
 | 
			
		||||
                year = int(date['year']), month = int(date['month']),
 | 
			
		||||
                day = int(date['day'])
 | 
			
		||||
            )
 | 
			
		||||
        except:
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
    def get_date_context(self, date, date_max = None):
 | 
			
		||||
        """
 | 
			
		||||
        Return a dict that can be added to the context to be used by
 | 
			
		||||
        a date_list.
 | 
			
		||||
        """
 | 
			
		||||
        if not date:
 | 
			
		||||
            date = tz.now().today()
 | 
			
		||||
 | 
			
		||||
        if date_max:
 | 
			
		||||
            date = min(date, date_max)
 | 
			
		||||
 | 
			
		||||
        # dates
 | 
			
		||||
        if date_max == date:
 | 
			
		||||
            first = self.nav_days - 1
 | 
			
		||||
        elif self.nav_per_week:
 | 
			
		||||
            first = date.weekday()
 | 
			
		||||
        else:
 | 
			
		||||
            first = int((self.nav_days - 1) / 2)
 | 
			
		||||
        first = date - tz.timedelta(days = first)
 | 
			
		||||
        dates = [ first + tz.timedelta(days=i)
 | 
			
		||||
                    for i in range(0, self.nav_days) ]
 | 
			
		||||
 | 
			
		||||
        # next/prev weeks/date bunch
 | 
			
		||||
        next = date + tz.timedelta(days=self.nav_days)
 | 
			
		||||
        prev = date - tz.timedelta(days=self.nav_days)
 | 
			
		||||
 | 
			
		||||
        if date_max:
 | 
			
		||||
            dates = [ date for date in dates if date <= date_max ]
 | 
			
		||||
            next = min(next, date_max)
 | 
			
		||||
            if next in dates:
 | 
			
		||||
                next = None
 | 
			
		||||
            prev = min(prev, date_max)
 | 
			
		||||
 | 
			
		||||
        # context dict
 | 
			
		||||
        return {
 | 
			
		||||
            'nav_dates': {
 | 
			
		||||
                'date': date,
 | 
			
		||||
                'next': next,
 | 
			
		||||
                'prev': prev,
 | 
			
		||||
                'dates': dates,
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        abstract = True
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class ListPage(Page):
 | 
			
		||||
    """
 | 
			
		||||
    Page for simple lists, query is done though request' GET fields.
 | 
			
		||||
    Look at get_queryset for more information.
 | 
			
		||||
    """
 | 
			
		||||
    summary = models.TextField(
 | 
			
		||||
        _('summary'),
 | 
			
		||||
        blank = True, null = True,
 | 
			
		||||
        help_text = _('some short description if you want to, just for fun'),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def get_queryset(cl, request, *args,
 | 
			
		||||
                     thread = None, context = {},
 | 
			
		||||
                     **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        Return a queryset from the request's GET parameters. Context
 | 
			
		||||
        can be used to update relative informations.
 | 
			
		||||
 | 
			
		||||
        This function can be used by other views if needed
 | 
			
		||||
 | 
			
		||||
        Parameters:
 | 
			
		||||
        * type:     ['program','diffusion','event'] type of the publication
 | 
			
		||||
        * tag:      tag to search for
 | 
			
		||||
        * search:   query to search in the publications
 | 
			
		||||
        * thread:   children of the thread passed in arguments only
 | 
			
		||||
        * order:    ['asc','desc'] sort ordering
 | 
			
		||||
        * page:     page number
 | 
			
		||||
 | 
			
		||||
        Context's fields:
 | 
			
		||||
        * object_list:      the final queryset
 | 
			
		||||
        * list_type:        type of the items in the list (as Page subclass)
 | 
			
		||||
        * tag_query:        tag searched for
 | 
			
		||||
        * search_query:     search terms
 | 
			
		||||
        * thread_query:     thread
 | 
			
		||||
        * paginator:        paginator object
 | 
			
		||||
        """
 | 
			
		||||
        if 'thread' in request.GET and thread:
 | 
			
		||||
            qs = thread.get_children().not_in_menu()
 | 
			
		||||
            context['thread_query'] = thread
 | 
			
		||||
        else:
 | 
			
		||||
            qs = Publication.objects.all()
 | 
			
		||||
        qs = qs.live()
 | 
			
		||||
 | 
			
		||||
        # ordering
 | 
			
		||||
        order = request.GET.get('order')
 | 
			
		||||
        if order not in ('asc','desc'):
 | 
			
		||||
            order = 'desc'
 | 
			
		||||
        qs = qs.order_by(
 | 
			
		||||
            ('-' if order == 'desc' else '') + 'first_published_at'
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
        # type
 | 
			
		||||
        type = request.GET.get('type')
 | 
			
		||||
        if type == 'program':
 | 
			
		||||
            qs = qs.type(ProgramPage)
 | 
			
		||||
            context['list_type'] = ProgramPage
 | 
			
		||||
        elif type == 'diffusion':
 | 
			
		||||
            qs = qs.type(DiffusionPage)
 | 
			
		||||
            context['list_type'] = DiffusionPage
 | 
			
		||||
        elif type == 'event':
 | 
			
		||||
            qs = qs.type(EventPage)
 | 
			
		||||
            context['list_type'] = EventPage
 | 
			
		||||
 | 
			
		||||
        # filter by tag
 | 
			
		||||
        tag = request.GET.get('tag')
 | 
			
		||||
        if tag:
 | 
			
		||||
            context['tag_query'] = tag
 | 
			
		||||
            qs = qs.filter(tags__name = tag)
 | 
			
		||||
 | 
			
		||||
        # search
 | 
			
		||||
        search = request.GET.get('search')
 | 
			
		||||
        if search:
 | 
			
		||||
            context['search_query'] = search
 | 
			
		||||
            qs = qs.search(search)
 | 
			
		||||
 | 
			
		||||
        # paginator
 | 
			
		||||
        if qs:
 | 
			
		||||
            paginator = Paginator(qs, 30)
 | 
			
		||||
            try:
 | 
			
		||||
                qs = paginator.page('page')
 | 
			
		||||
            except PageNotAnInteger:
 | 
			
		||||
                qs = paginator.page(1)
 | 
			
		||||
            except EmptyPage:
 | 
			
		||||
                qs = parginator.page(paginator.num_pages)
 | 
			
		||||
            context['paginator'] = paginator
 | 
			
		||||
        context['object_list'] = qs
 | 
			
		||||
        return qs
 | 
			
		||||
 | 
			
		||||
    def get_context(self, request, *args, **kwargs):
 | 
			
		||||
        context = super().get_context(request, *args, **kwargs)
 | 
			
		||||
        qs = self.get_queryset(request, context=context)
 | 
			
		||||
        context['object_list'] = qs
 | 
			
		||||
        return context
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class LogsPage(BaseDateList,Page):
 | 
			
		||||
    summary = models.TextField(
 | 
			
		||||
        _('summary'),
 | 
			
		||||
        blank = True, null = True,
 | 
			
		||||
        help_text = _('some short description if you want to, just for fun'),
 | 
			
		||||
    )
 | 
			
		||||
    station = models.ForeignKey(
 | 
			
		||||
        controllers.Station,
 | 
			
		||||
        verbose_name = _('station'),
 | 
			
		||||
        null = True,
 | 
			
		||||
        on_delete=models.SET_NULL,
 | 
			
		||||
        help_text = _('(required for logs) the station on which the logs '
 | 
			
		||||
                      'happened')
 | 
			
		||||
    )
 | 
			
		||||
    max_days = models.IntegerField(
 | 
			
		||||
        _('maximum days'),
 | 
			
		||||
        default=15,
 | 
			
		||||
        help_text = _('maximum days in the past allowed to be shown. '
 | 
			
		||||
                      '0 means no limit')
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    content_panels = [
 | 
			
		||||
        FieldPanel('title'),
 | 
			
		||||
        MultiFieldPanel([
 | 
			
		||||
            FieldPanel('station'),
 | 
			
		||||
            FieldPanel('max_days'),
 | 
			
		||||
        ], heading=_('Configuration')),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def as_item(cl, log):
 | 
			
		||||
        """
 | 
			
		||||
        Return a log object as a DiffusionPage or ListItem.
 | 
			
		||||
        Supports: Log/Track, Diffusion
 | 
			
		||||
        """
 | 
			
		||||
        if type(log) == programs.Diffusion:
 | 
			
		||||
            return DiffusionPage.as_item(log)
 | 
			
		||||
        return ListItem(
 | 
			
		||||
            title = '{artist} -- {title}'.format(
 | 
			
		||||
                artist = log.related.artist,
 | 
			
		||||
                title = log.related.title,
 | 
			
		||||
            ),
 | 
			
		||||
            summary = log.related.info,
 | 
			
		||||
            date = log.date,
 | 
			
		||||
            info = '♫',
 | 
			
		||||
            css_class = 'track'
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
    def get_queryset(self, request, context):
 | 
			
		||||
        if 'date' in request.GET:
 | 
			
		||||
            date = request.GET.get('date')
 | 
			
		||||
            date = self.str_to_date(date)
 | 
			
		||||
 | 
			
		||||
            if date and self.max_days:
 | 
			
		||||
                date = max(
 | 
			
		||||
                    tz.now().date() - tz.timedelta(days=self.max_days),
 | 
			
		||||
                    date
 | 
			
		||||
                )
 | 
			
		||||
        else:
 | 
			
		||||
            date = tz.now().date()
 | 
			
		||||
        context.update(self.get_date_context(date, date_max=tz.now().date()))
 | 
			
		||||
 | 
			
		||||
        r = []
 | 
			
		||||
        for date in context['nav_dates']['dates']:
 | 
			
		||||
            logs = self.station.get_on_air(date)
 | 
			
		||||
            logs = [ self.as_item(log) for log in logs ]
 | 
			
		||||
            r.append((date, logs))
 | 
			
		||||
        return r
 | 
			
		||||
 | 
			
		||||
    def get_context(self, request, *args, **kwargs):
 | 
			
		||||
        context = super().get_context(request, *args, **kwargs)
 | 
			
		||||
        qs = self.get_queryset(request, context)
 | 
			
		||||
        context['object_list'] = qs
 | 
			
		||||
        return context
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Menus and Sections
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
@ -34,7 +34,7 @@
 | 
			
		||||
{% endif %}
 | 
			
		||||
 | 
			
		||||
{% with list_paginator=paginator %}
 | 
			
		||||
{% include "cms/list.html" %}
 | 
			
		||||
{% include "cms/snippets/list.html" %}
 | 
			
		||||
{% endwith %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										7
									
								
								cms/templates/cms/logs_page.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										7
									
								
								cms/templates/cms/logs_page.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,7 @@
 | 
			
		||||
{% extends "cms/base_site.html" %}
 | 
			
		||||
{# generic page to display list of articles #}
 | 
			
		||||
 | 
			
		||||
{% block content %}
 | 
			
		||||
{% include "cms/snippets/date_list.html" %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
@ -34,21 +34,21 @@
 | 
			
		||||
 | 
			
		||||
{% block page_nav_extras %}
 | 
			
		||||
{% if page.program.active %}
 | 
			
		||||
{% with object_list=page.next_diffs %}
 | 
			
		||||
{% with object_list=page.next %}
 | 
			
		||||
{% if object_list %}
 | 
			
		||||
<div>
 | 
			
		||||
    <h2>{% trans "Next Diffusions" %}</h2>
 | 
			
		||||
    {% include "cms/list.html" %}
 | 
			
		||||
    {% include "cms/snippets/list.html" %}
 | 
			
		||||
</div>
 | 
			
		||||
{% endif %}
 | 
			
		||||
{% endwith %}
 | 
			
		||||
{% endif %}{# program.active #}
 | 
			
		||||
 | 
			
		||||
{% with object_list=page.prev_diffs %}
 | 
			
		||||
{% with object_list=page.prev %}
 | 
			
		||||
{% if object_list %}
 | 
			
		||||
<div>
 | 
			
		||||
    <h2>{% trans "Previous Diffusions" %}</h2>
 | 
			
		||||
    {% include "cms/list.html" %}
 | 
			
		||||
    {% include "cms/snippets/list.html" %}
 | 
			
		||||
</div>
 | 
			
		||||
{% endif %}
 | 
			
		||||
{% endwith %}
 | 
			
		||||
 | 
			
		||||
@ -1,10 +0,0 @@
 | 
			
		||||
{% extends "cms/base_site.html" %}
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
{% block title %}
 | 
			
		||||
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										37
									
								
								cms/templates/cms/snippets/date_list.html
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										37
									
								
								cms/templates/cms/snippets/date_list.html
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,37 @@
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
 | 
			
		||||
{# FIXME: get current complete URL #}
 | 
			
		||||
<div class="list dated_list">
 | 
			
		||||
{% if nav_dates %}
 | 
			
		||||
<nav class="nav_dates">
 | 
			
		||||
    {% if nav_dates.prev %}
 | 
			
		||||
    <a href="?date={{ nav_dates.prev|date:"Y-m-d" }}" title="{% trans "previous days" %}"><</a>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
 | 
			
		||||
    {% for day in nav_dates.dates %}
 | 
			
		||||
    <a href="#day_{{day|date:"Y-m-d"}}"
 | 
			
		||||
        {% if day == nav_dates.date %}class="today"{% endif %}>
 | 
			
		||||
        {{ day|date:'D. d' }}
 | 
			
		||||
    </a>
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
 | 
			
		||||
    {% if nav_dates.next %}
 | 
			
		||||
    <a href="?date={{ nav_dates.next|date:"Y-m-d" }}" title="{% trans "next days" %}">></a>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
</nav>
 | 
			
		||||
{% endif %}
 | 
			
		||||
 | 
			
		||||
{% for day, list in object_list %}
 | 
			
		||||
<ul id="day_{{day|date:"Y-m-d"}}"
 | 
			
		||||
    {% if day == nav_dates.date %}class="today"{% endif %}>
 | 
			
		||||
    {# you might like to hide it by default -- this more for sections #}
 | 
			
		||||
    <h2>{{ day|date:'l d F' }}</h2>
 | 
			
		||||
    {% with object_list=list list_date_format="H:i" %}
 | 
			
		||||
    {% for item in list %}
 | 
			
		||||
    {% include "cms/snippets/list_item.html" %}
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
    {% endwith %}
 | 
			
		||||
</ul>
 | 
			
		||||
{% endfor %}
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,59 @@
 | 
			
		||||
{% load i18n %}
 | 
			
		||||
{% load aircox_cms %}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
<div class="list">
 | 
			
		||||
<ul>
 | 
			
		||||
{% for page in object_list %}
 | 
			
		||||
{% with item=page.specific %}
 | 
			
		||||
{% include "cms/snippets/list_item.html" %}
 | 
			
		||||
{% endwith %}
 | 
			
		||||
{% endfor %}
 | 
			
		||||
</ul>
 | 
			
		||||
 | 
			
		||||
{# we use list_paginator to avoid conflicts when there are multiple lists #}
 | 
			
		||||
{% if list_paginator and list_paginator.num_pages > 1 %}
 | 
			
		||||
<nav>
 | 
			
		||||
{% with list_paginator.num_pages as num_pages %}
 | 
			
		||||
    {% if object_list.has_previous %}
 | 
			
		||||
        <a href="?page={{ object_list.previous_page_number }}">
 | 
			
		||||
            {% trans "previous page" %}
 | 
			
		||||
        </a>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
 | 
			
		||||
    {% if object_list.number > 3 %}
 | 
			
		||||
        <a href="?page=1">1</a>
 | 
			
		||||
        {% if object_list.number > 4 %}
 | 
			
		||||
        …
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    {% endif %}
 | 
			
		||||
 | 
			
		||||
    {% for i in object_list.number|around:2 %}
 | 
			
		||||
        {% if i == object_list.number %}
 | 
			
		||||
            {{ object_list.number }}
 | 
			
		||||
        {% elif i > 0 and i <= num_pages %}
 | 
			
		||||
            <a href="?page={{ i }}">{{ i }}</a>
 | 
			
		||||
        {% endif %}
 | 
			
		||||
    {% endfor %}
 | 
			
		||||
 | 
			
		||||
    {% with object_list.number|add:"2" as max %}
 | 
			
		||||
    {% if max < num_pages %}
 | 
			
		||||
        {% if max|add:"1" < num_pages %}
 | 
			
		||||
        …
 | 
			
		||||
        {% endif %}
 | 
			
		||||
        <a href="?page={{ num_pages }}">{{ num_pages }}</a>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    {% endwith %}
 | 
			
		||||
 | 
			
		||||
    {% if object_list.has_next %}
 | 
			
		||||
        <a href="?page={{ object_list.next_page_number }}">
 | 
			
		||||
            {% trans "next page" %}
 | 
			
		||||
        </a>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
{% endwith %}
 | 
			
		||||
</nav>
 | 
			
		||||
{% endif %}
 | 
			
		||||
 | 
			
		||||
</div>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -1,15 +1,28 @@
 | 
			
		||||
{% comment %}
 | 
			
		||||
Configurable item to be put in a list. Support standard Publication or
 | 
			
		||||
ListItem instance.
 | 
			
		||||
 | 
			
		||||
Options:
 | 
			
		||||
* item: item to render. Fields: title, summary, cover, url, date, info, css_class
 | 
			
		||||
* list_date_format: format passed to the date filter instead of default one
 | 
			
		||||
{% endcomment %}
 | 
			
		||||
 | 
			
		||||
{% load wagtailimages_tags %}
 | 
			
		||||
 | 
			
		||||
<a {% if item.url %}href="{{ item.url }}" {% endif %}class="item page_item">
 | 
			
		||||
<a {% if item.url %}href="{{ item.url }}" {% endif %}
 | 
			
		||||
    class="item page_item{% if item.css_class %}{{ item.css_class }}{% endif %}">
 | 
			
		||||
    {% image item.cover fill-64x64 class="cover item_cover" %}
 | 
			
		||||
    <h3>{{ item.title }}</h3>
 | 
			
		||||
    <div class="summary">{{ item.summary }}</div>
 | 
			
		||||
    {% if not item.show_in_menus %}
 | 
			
		||||
    {% if item.date %}
 | 
			
		||||
    {% if item.summary %}<div class="summary">{{ item.summary }}</div>{% endif %}
 | 
			
		||||
    {% if not item.show_in_menus and item.date %}
 | 
			
		||||
    {% with date_format=list_date_format|default:'l d F, H:i' %}
 | 
			
		||||
    <time datetime="{{ item.date }}">
 | 
			
		||||
        {{ item.date|date:'l d F, H:i' }}
 | 
			
		||||
        {{ item.date|date:date_format }}
 | 
			
		||||
    </time>
 | 
			
		||||
    {% endwith %}
 | 
			
		||||
    {% endif %}
 | 
			
		||||
    {% if item.info %}
 | 
			
		||||
    <span class="info">{{ item.info|safe }}</span>
 | 
			
		||||
    {% endif %}
 | 
			
		||||
</a>
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										11
									
								
								cms/templatetags/aircox_cms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										11
									
								
								cms/templatetags/aircox_cms.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,11 @@
 | 
			
		||||
from django import template
 | 
			
		||||
register = template.Library()
 | 
			
		||||
 | 
			
		||||
@register.filter(name='around')
 | 
			
		||||
def around(page_num, n):
 | 
			
		||||
    """
 | 
			
		||||
    Return a range of value around a given number.
 | 
			
		||||
    """
 | 
			
		||||
    return range(page_num-n, page_num+n+1)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										10
									
								
								cms/views.py
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								cms/views.py
									
									
									
									
									
								
							@ -1,12 +1,2 @@
 | 
			
		||||
from django.shortcuts import render
 | 
			
		||||
from django.core.paginator import Paginator, EmptyPage, PageNotAnInteger
 | 
			
		||||
from aircox.cms.models import *
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def index_page(request):
 | 
			
		||||
    context = {}
 | 
			
		||||
    if ('tag' or 'search') in request.GET:
 | 
			
		||||
        qs = Publication.get_queryset(request, context = context)
 | 
			
		||||
 | 
			
		||||
    return render(request, 'index_page.html', context)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -12,6 +12,7 @@ sources that are used to generate the audio stream:
 | 
			
		||||
- **master**: main output
 | 
			
		||||
"""
 | 
			
		||||
import os
 | 
			
		||||
import datetime
 | 
			
		||||
import logging
 | 
			
		||||
from enum import Enum, IntEnum
 | 
			
		||||
 | 
			
		||||
@ -160,6 +161,47 @@ class Station(programs.Nameable):
 | 
			
		||||
            )
 | 
			
		||||
        return qs.order_by('date')
 | 
			
		||||
 | 
			
		||||
    def get_on_air(self, date = None):
 | 
			
		||||
        """
 | 
			
		||||
        Return a list of what should have normally been on air at the
 | 
			
		||||
        given date, ordered descending on the diffusion time
 | 
			
		||||
 | 
			
		||||
        The list contains:
 | 
			
		||||
        - track logs: for the streamed programs;
 | 
			
		||||
        - diffusion: for the scheduled diffusions;
 | 
			
		||||
        """
 | 
			
		||||
        # TODO: argument to get sound instead of tracks
 | 
			
		||||
        date = date or tz.now().date()
 | 
			
		||||
        if date > datetime.date.today():
 | 
			
		||||
            return []
 | 
			
		||||
 | 
			
		||||
        logs = Log.get_for(model = programs.Track) \
 | 
			
		||||
                    .filter(date__contains = date) \
 | 
			
		||||
                    .order_by('date')
 | 
			
		||||
        diffs = programs.Diffusion.objects.get_at(date) \
 | 
			
		||||
                    .filter(type = programs.Diffusion.Type.normal) \
 | 
			
		||||
                    .order_by('start')
 | 
			
		||||
 | 
			
		||||
        # mix up
 | 
			
		||||
        items = []
 | 
			
		||||
        prev_diff = None
 | 
			
		||||
        for diff in diffs:
 | 
			
		||||
            logs_ = logs.filter(date__gt = prev_diff.end,
 | 
			
		||||
                                date__lt = diff.start) \
 | 
			
		||||
                    if prev_diff else \
 | 
			
		||||
                    logs.filter(date__lt = diff.start)
 | 
			
		||||
            prev_diff = diff
 | 
			
		||||
            items.extend(logs_)
 | 
			
		||||
            items.append(diff)
 | 
			
		||||
 | 
			
		||||
        # last logs
 | 
			
		||||
        if prev_diff:
 | 
			
		||||
            logs_ = logs.filter(date__gt = prev_diff.end)
 | 
			
		||||
            items.extend(logs_)
 | 
			
		||||
        return reversed(items)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    def save(self, make_sources = True, *args, **kwargs):
 | 
			
		||||
        """
 | 
			
		||||
        * make_sources: if the model has not been yet saved, generate
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user