forked from rc/aircox
work on logs, timetable, stats
This commit is contained in:
@ -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
|
||||
|
@ -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):
|
||||
|
@ -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)
|
||||
|
||||
|
||||
|
@ -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']
|
||||
|
@ -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)
|
||||
|
||||
|
@ -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
67
aircox/views/mixins.py
Normal 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)
|
||||
|
||||
|
@ -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
|
||||
|
Reference in New Issue
Block a user