work on logs, timetable, stats

This commit is contained in:
bkfox
2019-09-08 01:39:40 +02:00
parent 2d21ab2434
commit c68e21ee57
71 changed files with 19683 additions and 710 deletions

View File

@ -2,7 +2,7 @@ from . import api, admin
from .article import ArticleDetailView, ArticleListView
from .base import BaseView
from .episode import EpisodeDetailView, EpisodeListView, TimetableView
from .episode import EpisodeDetailView, EpisodeListView, DiffusionListView
from .log import LogListView
from .page import PageDetailView, PageListView
from .program import ProgramDetailView

View File

@ -10,7 +10,7 @@ from django.utils.translation import ugettext_lazy as _
from django.views.generic import ListView
from ..models import Program
from .log import BaseLogListView
from .log import LogListView
class BaseAdminView(LoginRequiredMixin, UserPassesTestMixin):
@ -25,20 +25,13 @@ class BaseAdminView(LoginRequiredMixin, UserPassesTestMixin):
return super().get_context_data(**kwargs)
class StatisticsView(BaseAdminView, BaseLogListView, ListView):
class StatisticsView(BaseAdminView, LogListView, ListView):
template_name = 'admin/aircox/statistics.html'
title = _('Statistics')
date = None
def get_queryset(self):
return super().get_queryset().today(self.date)
def get_diffusions_queryset(self):
return super().get_diffusions_queryset().today(self.date)
def get(self, request, *args, date=None, **kwargs):
self.date = datetime.date.today() if date is None else date
return super().get(request, *args, date=date, **kwargs)
def get_object_list(self, logs, *_):
return super().get_object_list(logs, True)
class AdminSite(admin.AdminSite):

View File

@ -4,9 +4,10 @@ from django.utils import timezone as tz
from rest_framework.generics import ListAPIView
from ..utils import str_to_date
from ..models import Log
from ..serializers import LogInfo, LogInfoSerializer
from .log import BaseLogListView
from .log import LogListMixin
class BaseAPIView:
@ -18,28 +19,30 @@ class BaseAPIView:
return super().get_queryset().station(self.station)
class LiveAPIView(BaseLogListView, BaseAPIView, ListAPIView):
class LogListAPIView(LogListMixin, BaseAPIView, ListAPIView):
"""
Return logs list, including diffusions. By default return logs of
the last 30 minutes.
Available GET parameters:
- "date": return logs for a specified date (
- "full": (staff user only) don't merge diffusion and logs
"""
serializer_class = LogInfoSerializer
min_date = None
queryset = Log.objects.all()
def get(self, *args, **kwargs):
self.min_date = tz.now() - tz.timedelta(minutes=20)
self.date = self.get_date()
if self.date is None:
self.min_date = tz.now() - tz.timedelta(minutes=30)
return super().get(*args, **kwargs)
def get_object_list(self, logs, full):
return [LogInfo(obj) for obj in super().get_object_list(logs, full)]
def get_serializer(self, queryset, *args, **kwargs):
queryset = Log.merge_diffusions(self.get_queryset(),
self.get_diffusions_queryset())
queryset = [LogInfo(obj) for obj in queryset]
return super().get_serializer(queryset, *args, **kwargs)
def get_queryset(self):
return super().get_queryset().after(self.min_date)
def get_diffusions_queryset(self):
return super().get_diffusions_queryset().after(self.min_date)
# Monitoring
full = bool(self.request.GET.get('full'))
return super().get_serializer(self.get_object_list(queryset, full),
*args, **kwargs)

View File

@ -1,5 +1,6 @@
from ..models import Article, Program
from .page import ParentMixin, PageDetailView, PageListView
from .mixins import ParentMixin
from .page import PageDetailView, PageListView
__all__ = ['ArticleDetailView', 'ArticleListView']

View File

@ -2,15 +2,16 @@ from collections import OrderedDict
import datetime
from django.views.generic import ListView
from django.utils.translation import ugettext as _
from ..converters import WeekConverter
from ..models import Diffusion, Episode, Program, Sound
from .base import BaseView
from .program import ProgramPageDetailView
from .page import ParentMixin, PageListView
from .page import PageListView
from .mixins import GetDateMixin, ParentMixin
__all__ = ['EpisodeDetailView', 'EpisodeListView', 'TimetableView']
__all__ = ['EpisodeDetailView', 'EpisodeListView', 'DiffusionListView']
class EpisodeDetailView(ProgramPageDetailView):
@ -32,7 +33,6 @@ class EpisodeDetailView(ProgramPageDetailView):
class EpisodeListView(ParentMixin, PageListView):
model = Episode
template_name = 'aircox/diffusion_list.html'
item_template_name = 'aircox/episode_item.html'
show_headline = True
@ -40,44 +40,35 @@ class EpisodeListView(ParentMixin, PageListView):
fk_parent = 'program'
class TimetableView(BaseView, ListView):
class DiffusionListView(GetDateMixin, BaseView, ListView):
""" View for timetables """
template_name_suffix = '_timetable'
model = Diffusion
# ordering = ('start',)
date = None
start = None
end = None
def get_date(self):
date = super().get_date()
return date if date is not None else datetime.date.today()
def get_queryset(self):
self.date = self.kwargs.get('date') or datetime.date.today()
self.start = self.date - datetime.timedelta(days=self.date.weekday())
self.end = self.start + datetime.timedelta(days=7)
return super().get_queryset().range(self.start, self.end) \
.order_by('start')
return super().get_queryset().today(self.date).order_by('start')
def get_context_data(self, **kwargs):
# regoup by dates
by_date = OrderedDict()
date = self.start
while date < self.end:
by_date[date] = []
date += datetime.timedelta(days=1)
for diffusion in self.object_list:
if diffusion.date not in by_date:
continue
by_date[diffusion.date].append(diffusion)
return super().get_context_data(
by_date=by_date,
date=self.date,
start=self.start,
end=self.end - datetime.timedelta(days=1),
prev_date=self.start - datetime.timedelta(days=1),
next_date=self.end + datetime.timedelta(days=1),
**kwargs
)
today = datetime.date.today()
start = self.date - datetime.timedelta(days=self.date.weekday())
dates = [
(today, None),
(today - datetime.timedelta(days=1), None),
(today + datetime.timedelta(days=1), None),
(today - datetime.timedelta(days=7), _('next week')),
(today + datetime.timedelta(days=7), _('last week')),
(None, None)
] + [
(date, date.strftime('%A %d'))
for date in (start + datetime.timedelta(days=i)
for i in range(0, 7)) if date != today
]
return super().get_context_data(date=self.date, dates=dates, **kwargs)

View File

@ -5,56 +5,61 @@ from django.views.generic import ListView
from ..models import Diffusion, Log
from .base import BaseView
from .mixins import GetDateMixin
__all__ = ['BaseLogListView', 'LogListView']
__all__ = ['LogListMixin', 'LogListView']
class BaseLogListView:
class LogListMixin(GetDateMixin):
model = Log
date = None
def get_date(self):
date, today = super().get_date(), datetime.date.today()
if date is not None and not self.request.user.is_staff:
return min(date, today)
return date
def get_queryset(self):
# only get logs for tracks: log for diffusion will be retrieved
# by the diffusions' queryset.
return super().get_queryset().on_air().filter(track__isnull=False)
qs = super().get_queryset().on_air().filter(track__isnull=False)
return qs.today(self.date) if self.date is not None else \
qs.after(self.min_date) if self.min_date is not None else qs
def get_diffusions_queryset(self):
return Diffusion.objects.station(self.station).on_air()
qs = Diffusion.objects.station(self.station).on_air()
return qs.today(self.date) if self.date is not None else \
qs.after(self.min_date) if self.min_date is not None else qs
def get_object_list(self, logs, full=False):
"""
Return diffusions merged to the provided logs queryset. If
`full`, sort items by date without merging.
"""
diffs = self.get_diffusions_queryset()
if self.request.user.is_staff and full:
return sorted(list(logs) + list(diffs), key=lambda obj: obj.start)
return Log.merge_diffusions(logs, diffs)
class LogListView(BaseView, BaseLogListView, ListView):
date = None
max_age = 10
min_date = None
def get(self, request, *args, **kwargs):
today = datetime.date.today()
self.min_date = today - datetime.timedelta(days=self.max_age)
self.date = min(max(self.min_date, self.kwargs['date']), today) \
if 'date' in self.kwargs else today
return super().get(request, *args, **kwargs)
def get_queryset(self):
return super().get_queryset().today(self.date)
def get_diffusions_queryset(self):
return super().get_diffusions_queryset().today(self.date)
class LogListView(BaseView, LogListMixin, ListView):
"""
Return list of logs for the provided date (from `kwargs` or
`request.GET`, defaults to today).
"""
def get_date(self):
date, today = super().get_date(), datetime.date.today()
return today if date is None else min(date, today)
def get_context_data(self, **kwargs):
today = datetime.date.today()
max_date = min(max(self.date + datetime.timedelta(days=3),
self.min_date + datetime.timedelta(days=6)), today)
return super().get_context_data(
date=self.date,
min_date=self.min_date,
dates=(date for date in (
max_date - datetime.timedelta(days=i)
for i in range(0, 7)) if date >= self.min_date),
object_list=Log.merge_diffusions(self.object_list,
self.get_diffusions_queryset()),
**kwargs
)
kwargs.update({
'date': self.date,
'dates': ((today - datetime.timedelta(days=i), None)
for i in range(0, 7)),
'object_list': self.get_object_list(self.object_list),
})
return super().get_context_data(**kwargs)

67
aircox/views/mixins.py Normal file
View File

@ -0,0 +1,67 @@
from django.shortcuts import get_object_or_404
from ..utils import str_to_date
__all__ = ['GetDateMixin', 'ParentMixin']
class GetDateMixin:
"""
Mixin offering utils to get date by `request.GET` or
`kwargs['date']`
"""
date = None
def get_date(self):
if 'date' in self.request.GET:
return str_to_date(self.request.GET['date'], '-')
return self.kwargs['date'] if 'date' in self.kwargs else None
def get(self, *args, **kwargs):
self.date = self.get_date()
return super().get(*args, **kwargs)
class ParentMixin:
"""
Optional parent page for a list view. Parent is fetched and passed to the
template context when `parent_model` is provided (queryset is filtered by
parent page in such case).
"""
parent_model = None
""" Parent model """
parent_url_kwarg = 'parent_slug'
""" Url lookup argument """
parent_field = 'slug'
""" Parent field for url lookup """
fk_parent = 'page'
""" Page foreign key to the parent """
parent = None
""" Parent page object """
def get_parent(self, request, *args, **kwargs):
if self.parent_model is None or self.parent_url_kwarg not in kwargs:
return
lookup = {self.parent_field: kwargs[self.parent_url_kwarg]}
return get_object_or_404(
self.parent_model.objects.select_related('cover'), **lookup)
def get(self, request, *args, **kwargs):
self.parent = self.get_parent(request, *args, **kwargs)
return super().get(request, *args, **kwargs)
def get_queryset(self):
if self.parent is not None:
lookup = {self.fk_parent: self.parent}
return super().get_queryset().filter(**lookup)
return super().get_queryset()
def get_context_data(self, **kwargs):
parent = kwargs.setdefault('parent', self.parent)
if parent is not None:
kwargs.setdefault('cover', parent.cover)
return super().get_context_data(**kwargs)

View File

@ -12,49 +12,7 @@ from ..utils import Redirect
from .base import BaseView
__all__ = ['ParentMixin', 'PageDetailView', 'PageListView']
class ParentMixin:
"""
Optional parent page for a list view. Parent is fetched and passed to the
template context when `parent_model` is provided (queryset is filtered by
parent page in such case).
"""
parent_model = None
""" Parent model """
parent_url_kwarg = 'parent_slug'
""" Url lookup argument """
parent_field = 'slug'
""" Parent field for url lookup """
fk_parent = 'page'
""" Page foreign key to the parent """
parent = None
""" Parent page object """
def get_parent(self, request, *args, **kwargs):
if self.parent_model is None or self.parent_url_kwarg not in kwargs:
return
lookup = {self.parent_field: kwargs[self.parent_url_kwarg]}
return get_object_or_404(
self.parent_model.objects.select_related('cover'), **lookup)
def get(self, request, *args, **kwargs):
self.parent = self.get_parent(request, *args, **kwargs)
return super().get(request, *args, **kwargs)
def get_queryset(self):
if self.parent is not None:
lookup = {self.fk_parent: self.parent}
return super().get_queryset().filter(**lookup)
return super().get_queryset()
def get_context_data(self, **kwargs):
parent = kwargs.setdefault('parent', self.parent)
if parent is not None:
kwargs.setdefault('cover', parent.cover)
return super().get_context_data(**kwargs)
__all__ = ['PageDetailView', 'PageListView']
# TODO: pagination: in template, only a limited number of pages displayed