This commit is contained in:
bkfox
2019-08-07 01:45:27 +02:00
parent 324cf2ef0f
commit 248b77fca4
52 changed files with 794 additions and 384 deletions

7
aircox/views/__init__.py Normal file
View File

@ -0,0 +1,7 @@
from .article import ArticleListView
from .base import BaseView
from .episode import EpisodeDetailView, EpisodeListView, TimetableView
from .log import LogListView
from .page import PageDetailView, PageListView
from .program import ProgramDetailView

14
aircox/views/article.py Normal file
View File

@ -0,0 +1,14 @@
from ..models import Article
from .page import PageListView
__all__ = ['ArticleListView']
class ArticleListView(PageListView):
model = Article
is_static = False
def get_queryset(self):
return super().get_queryset(is_static=self.is_static)

32
aircox/views/base.py Normal file
View File

@ -0,0 +1,32 @@
from django.http import Http404
from django.views.generic import DetailView, ListView
from django.views.generic.base import TemplateResponseMixin, ContextMixin
from ..utils import Redirect
__all__ = ['BaseView', 'PageView']
class BaseView(TemplateResponseMixin, ContextMixin):
show_side_nav = False
""" Show side navigation """
title = None
""" Page title """
cover = None
""" Page cover """
@property
def station(self):
return self.request.station
def get_queryset(self):
return super().get_queryset().station(self.station)
def get_context_data(self, **kwargs):
kwargs.setdefault('station', self.station)
kwargs.setdefault('cover', self.cover)
kwargs.setdefault('show_side_nav', self.show_side_nav)
return super().get_context_data(**kwargs)

99
aircox/views/episode.py Normal file
View File

@ -0,0 +1,99 @@
from collections import OrderedDict
import datetime
from django.db.models import OuterRef, Subquery
from django.shortcuts import get_object_or_404
from django.views.generic import ListView
from ..models import Diffusion, Episode, Page, Program, Sound
from .base import BaseView
from .page import PageListView
from .program import ProgramPageDetailView
__all__ = ['EpisodeDetailView', 'DiffusionListView', 'TimetableView']
class EpisodeDetailView(ProgramPageDetailView):
model = Episode
def get_podcasts(self, diffusion):
return Sound.objects.diffusion(diffusion).podcasts()
def get_context_data(self, **kwargs):
kwargs.setdefault('program', self.object.program)
kwargs.setdefault('parent', kwargs['program'])
if not 'podcasts' in kwargs:
kwargs['podcasts'] = self.object.sound_set.podcasts()
return super().get_context_data(**kwargs)
# TODO: pagination: in template, only a limited number of pages displayed
class EpisodeListView(PageListView):
model = Episode
item_template_name = 'aircox/episode_item.html'
show_headline = True
template_name = 'aircox/diffusion_list.html'
program = None
def get(self, request, *args, **kwargs):
program_slug = kwargs.get('program_slug')
if program_slug:
self.program = get_object_or_404(Program, slug=program_slug)
return super().get(request, *args, **kwargs)
def get_queryset(self):
qs = super().get_queryset()
if self.program:
qs = qs.filter(program=self.program)
return qs
def get_context_data(self, **kwargs):
program = kwargs.setdefault('program', self.program)
if program is not None:
kwargs.setdefault('cover', program.cover)
kwargs.setdefault('parent', program)
return super().get_context_data(**kwargs)
class TimetableView(BaseView, ListView):
""" View for timetables """
template_name_suffix = '_timetable'
model = Diffusion
# ordering = ('start',)
date = None
start = None
end = None
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')
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
)

96
aircox/views/log.py Normal file
View File

@ -0,0 +1,96 @@
from collections import deque
import datetime
from django.views.generic import ListView
from ..models import Diffusion, Log
from .base import BaseView
__all__ = ['BaseLogView', 'LogListView']
class BaseLogView(ListView):
station = None
date = None
delta = None
def get_queryset(self):
# only get logs for tracks: log for diffusion will be retrieved
# by the diffusions' queryset.
return super().get_queryset().station(self.station).on_air() \
.at(self.date).filter(track__isnull=False)
def get_diffusions_queryset(self):
return Diffusion.objects.station(self.station).on_air() \
.today(self.date)
def get_object_list(self, queryset):
diffs = deque(self.get_diffusions_queryset().order_by('start'))
logs = list(queryset.order_by('date'))
if not len(diffs):
return logs
object_list = []
diff = None
last_collision = None
# TODO/FIXME: multiple diffs at once - recheck the whole algorithm in
# detail -- however I barely see cases except when there are diff
# collision or the streamer is not working
for index, log in enumerate(logs):
# get next diff
if diff is None or diff.end < log.date:
diff = diffs.popleft() if len(diffs) else None
# no more diff that can collide: return list
if diff is None:
if last_collision and not object_list or \
object_list[-1] is not last_collision:
object_list.append(last_collision)
return object_list + logs[index:]
# diff colliding with log
if diff.start <= log.date:
if not object_list or object_list[-1] is not diff:
object_list.append(diff)
if log.date <= diff.end:
last_collision = log
else:
# add last colliding log: track
if last_collision is not None:
object_list.append(last_collision)
object_list.append(log)
last_collision = None
return object_list
class LogListView(BaseView, BaseLogView):
model = Log
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_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=self.get_object_list(self.object_list),
**kwargs
)

84
aircox/views/page.py Normal file
View File

@ -0,0 +1,84 @@
from django.http import Http404
from django.views.generic import DetailView, ListView
from ..models import Category
from ..utils import Redirect
from .base import BaseView
__all__ = ['PageDetailView', 'PageListView']
class PageDetailView(BaseView, DetailView):
""" Base view class for pages. """
context_object_name = 'page'
def get_queryset(self):
return super().get_queryset().select_related('cover', 'category')
# This should not exists: it allows mapping not published pages
# or it should be only used for trashed pages.
def not_published_redirect(self, page):
"""
When a page is not published, redirect to the returned url instead
of an HTTP 404 code. """
return None
def get_object(self):
obj = super().get_object()
if not obj.is_published:
redirect_url = self.not_published_redirect(obj)
if redirect_url:
raise Redirect(redirect_url)
raise Http404('%s not found' % self.model._meta.verbose_name)
return obj
def get_context_data(self, **kwargs):
#if kwargs.get('regions') is None:
# contents = contents_for_item(
# page, page_renderer._renderers.keys())
# kwargs['regions'] = contents.render_regions(page_renderer)
page = kwargs.setdefault('page', self.object)
kwargs.setdefault('title', page.title)
kwargs.setdefault('cover', page.cover)
return super().get_context_data(**kwargs)
class PageListView(BaseView, ListView):
template_name = 'aircox/page_list.html'
item_template_name = 'aircox/page_item.html'
paginate_by = 10
show_headline = True
show_side_nav = True
categories = None
def get(self, *args, **kwargs):
self.categories = set(self.request.GET.getlist('categories'))
return super().get(*args, **kwargs)
def get_queryset(self):
qs = super().get_queryset().published() \
.select_related('cover', 'category')
# category can be filtered based on request.GET['categories']
# (by id)
if self.categories:
qs = qs.filter(category__slug__in=self.categories)
return qs.order_by('-date')
def get_categories_queryset(self):
# TODO: use generic reverse field lookup
categories = self.model.objects.published() \
.filter(category__isnull=False) \
.values_list('category', flat=True)
return Category.objects.filter(id__in=categories)
def get_context_data(self, **kwargs):
kwargs.setdefault('item_template_name', self.item_template_name)
kwargs.setdefault('filter_categories', self.get_categories_queryset())
kwargs.setdefault('categories', self.categories)
kwargs.setdefault('show_headline', self.show_headline)
return super().get_context_data(**kwargs)

29
aircox/views/program.py Normal file
View File

@ -0,0 +1,29 @@
from aircox.models import Episode, Program
from .page import PageDetailView
__all__ = ['ProgramPageDetailView', 'ProgramDetailView']
class ProgramPageDetailView(PageDetailView):
""" Base view class for rendering content of a specific programs. """
show_side_nav = True
list_count=5
def get_episodes_queryset(self, program):
return program.episode_set.published().order_by('-date')
def get_context_data(self, program, episodes=None, **kwargs):
if episodes is None:
episodes = self.get_episodes_queryset(program)
return super().get_context_data(
program=program, episodes=episodes[:self.list_count], **kwargs)
class ProgramDetailView(ProgramPageDetailView):
model = Program
def get_context_data(self, **kwargs):
kwargs.setdefault('program', self.object)
return super().get_context_data(**kwargs)