diff --git a/cms/routes.py b/cms/routes.py index 74d79fd..de2dfa5 100644 --- a/cms/routes.py +++ b/cms/routes.py @@ -1,3 +1,5 @@ +import datetime + from django.db import models from django.utils import timezone as tz from django.conf.urls import url @@ -176,17 +178,13 @@ class DateRoute(Route): ] @classmethod - def get_queryset(cl, model, request, year, month, day, - attr='date', **kwargs): + def get_queryset(cl, model, request, year, month, day, **kwargs): """ * request: optional * attr: name of the attribute to check the date against """ - return model.objects.filter(**{ - attr + '__year': int(year), - attr + '__month': int(month), - attr + '__day': int(day) - }) + date = datetime.date(int(year), int(month), int(day)) + return model.objects.filter(date__contains = date) @classmethod def get_title(cl, model, request, year, month, day, **kwargs): diff --git a/controllers/monitor.py b/controllers/monitor.py index 65f8588..bccf4cb 100644 --- a/controllers/monitor.py +++ b/controllers/monitor.py @@ -108,7 +108,7 @@ class Monitor: .order_by('date').last() if not diff_log or \ - not diff_log.related.is_date_in_my_range(now): + not diff_log.related.is_date_in_range(now): return None, [] # sound has switched? assume it has been (forced to) stopped @@ -138,10 +138,8 @@ class Monitor: now = tz.make_aware(tz.datetime.now()) args = {'start__gt': diff.start } if diff else {} - diff = programs.Diffusion.get( - now, now = True, + diff = programs.Diffusion.objects.get_at(now).filter( type = programs.Diffusion.Type.normal, - sound__type = programs.Sound.Type.archive, sound__removed = False, **args diff --git a/notes.md b/notes.md index 97861c0..4af0778 100644 --- a/notes.md +++ b/notes.md @@ -27,7 +27,6 @@ - admin cms -> sections/actions and django decorator? -> enhance calendar with possible actions? - - sections id generator - website: - diffusions: @@ -39,7 +38,6 @@ - remove from playing playlist -> stop - date_by_list: - sections' url - - list of played diffusions and tracks when non-stop; # Long term TODO - automatic cancel of passed diffusion based on logs? diff --git a/programs/models.py b/programs/models.py index 3809958..019e1ec 100755 --- a/programs/models.py +++ b/programs/models.py @@ -1,3 +1,4 @@ +import datetime import os import shutil import logging @@ -36,7 +37,7 @@ def date_or_default(date, no_time = False): Return date or default value (now) if not defined, and remove time info if date_only is True """ - date = date or tz.datetime.today() + date = date or tz.now() if not tz.is_aware(date): date = tz.make_aware(date) if no_time: @@ -576,6 +577,49 @@ class Program(Nameable): return qs[0] if qs else None +class DiffusionManager(models.Manager): + def get_at(self, date = None): + """ + Return a queryset of diffusions that have the given date + in their range. + + If date is a datetime.date object, check only against the + date. + """ + date = date or tz.now() + if issubclass(type(date), datetime.date): + return self.filter( + models.Q(start__contains = date) | \ + models.Q(end__contains = date) + ) + + return self.filter( + models.Q(start__lte = date, end__gte = date) | + # FIXME: should not be here? + models.Q(start__gte = date), + ).order_by('start') + + def get_after(self, date = None): + """ + Return a queryset of diffusions that happen after the given + date. + """ + date = date_or_default(date) + return self.filter( + start__gte = date, + ).order_by('start') + + def get_before(self, date): + """ + Return a queryset of diffusions that finish before the given + date. + """ + date = date_or_default(date) + return self.filter( + end__lte = date, + ).order_by('start') + + class Diffusion(models.Model): """ A Diffusion is an occurrence of a Program that is scheduled on the @@ -594,6 +638,8 @@ class Diffusion(models.Model): - cancel: the diffusion has been canceled - stop: the diffusion has been manually stopped """ + objects = DiffusionManager() + class Type(IntEnum): normal = 0x00 unconfirmed = 0x01 @@ -642,52 +688,13 @@ class Diffusion(models.Model): return sounds.filter(type = Sound.Type.archive, removed = False). \ order_by('path') - @classmethod - def get(cl, date = None, - now = False, next = False, prev = False, - queryset = None, - **filter_args): - """ - Return a queryset of diffusions, depending on value of now/next/prev - - now: that have date in their start-end range or start after - - next: that start after date - - prev: that end before date - - If queryset is not given, use self.objects.all - - Diffusions are ordered by +start for now and next; -start for prev - """ - #FIXME: conflicts? ( + calling functions) - date = date_or_default(date) - if queryset is None: - queryset = cl.objects - - if now: - return queryset.filter( - models.Q(start__lte = date, - end__gte = date) | - models.Q(start__gte = date), - **filter_args - ).order_by('start') - - if next: - return queryset.filter( - start__gte = date, - **filter_args - ).order_by('start') - - if prev: - return queryset.filter( - end__lte = date, - **filter_args - ).order_by('-start') - - def is_date_in_my_range(self, date = None): + def is_date_in_range(self, date = None): """ Return true if the given date is in the diffusion's start-end range. """ - return self.start < date_or_default(date) < self.end + date = date or tz.now() + return self.start < date < self.end def get_conflicts(self): """ diff --git a/website/sections.py b/website/sections.py index 66fdd77..af0c022 100644 --- a/website/sections.py +++ b/website/sections.py @@ -1,9 +1,11 @@ import json +import datetime from django.utils import timezone as tz from django.utils.translation import ugettext as _, ugettext_lazy import aircox.programs.models as programs +import aircox.controllers.models as controllers import aircox.cms.models as cms import aircox.cms.routes as routes import aircox.cms.sections as sections @@ -30,12 +32,12 @@ class Player(sections.Section): @expose def on_air(cl, request): - qs = programs.Diffusion.get( - now = True, + now = tz.now() + qs = programs.Diffusion.objects.get_at(now).filter( type = programs.Diffusion.Type.normal ) - if not qs or not qs[0].is_date_in_my_range(): + if not qs or not qs[0].is_date_in_range(): return {} qs = qs[0] @@ -214,7 +216,8 @@ class Sounds(sections.List): class ListByDate(sections.List): """ - List that add a navigation by date in its header. + List that add a navigation by date in its header. It aims to be + used with DateRoute. """ template_name = 'aircox/website/list_by_date.html' message_empty = '' @@ -253,15 +256,19 @@ class ListByDate(sections.List): return [ first + tz.timedelta(days=i) for i in range(0, self.nav_days) ] def date_or_default(self): + """ + Return self.date or create a date if needed, using kwargs' + year, month, day attributes if exists (otherwise, use today) + """ if self.date: - return self.date + return datetime.date(self.date) elif self.kwargs and 'year' in self.kwargs: - return tz.datetime(year = int(self.kwargs['year']), - month = int(self.kwargs['month']), - day = int(self.kwargs['day']), - hour = 0, minute = 0, second = 0, - microsecond = 0) - return tz.now() + return datetime.date( + year = int(self.kwargs['year']), + month = int(self.kwargs['month']), + day = int(self.kwargs['day']) + ) + return datetime.date.today() def get_context_data(self, *args, **kwargs): context = super().get_context_data(*args, **kwargs) @@ -270,6 +277,7 @@ class ListByDate(sections.List): dates = [ (date, self.get_date_url(date)) for date in self.nav_dates(date) ] + # FIXME next_week = dates[-1][0] + tz.timedelta(days=1) next_week = self.get_date_url(next_week) @@ -289,18 +297,22 @@ class ListByDate(sections.List): @staticmethod def get_date_url(date): """ - return a url to the list for the given date + return an url for the given date """ + return self.view.website.reverse( + model = self.model, route = routes.DateRoute, + year = date.year, month = date.month, day = date.day, + ) @property def url(self): return None - class Schedule(Diffusions,ListByDate): """ Render a list of diffusions in the form of a schedule """ + model = models.Diffusion fields = [ 'time', 'image', 'title', 'content', 'info', 'actions' ] truncate = 30 @@ -310,10 +322,7 @@ class Schedule(Diffusions,ListByDate): def get_object_list(self): date = self.date_or_default() - diffs = routes.DateRoute.get_queryset( - programs.Diffusion, None, date.year, date.month, date.day, - attr = 'start' - ).order_by('start') + diffs = programs.Diffusion.objects.get_at(date).order_by('start') return models.Diffusion.objects.get_for(diffs, create = True) @staticmethod @@ -329,54 +338,58 @@ class Schedule(Diffusions,ListByDate): class Logs(ListByDate): """ - Return a list of played stream sounds and diffusions. + Print a list of played stream tracks and diffusions. + Note that for the moment we don't print if the track has been + partially hidden by a scheduled diffusion """ + model = controllers.Log + @staticmethod - def make_item(log): + def make_item(item): """ Return a list of items to add to the playlist. + Only support Log related to a Track and programs.Diffusion """ - if issubclass(type(log.related), programs.Diffusion): - diff = log.related - post = models.Diffusion.objects.filter(related = diff).first() \ - or models.Program.objects.filter(related = diff.program).first() \ - or ListItem(title = diff.program.name) - post.date = diff.start - return post - - if issubclass(type(log.related), programs.Track): - track = log.related - post = ListItem( - title = '{artist} — {name}'.format( - artist = track.artist, - name = track.name, - ), - date = log.date, - content = track.info, - info = '♫', + if issubclass(type(item), programs.Diffusion): + return models.Diffusion.objects.get_for( + item, create = True, save = False ) + + track = log.related + post = ListItem( + title = '{artist} — {name}'.format( + artist = track.artist, + name = track.name, + ), + date = log.date, + content = track.info, + info = '♫', + ) return post - @staticmethod - def make_diff(diff): - pass - def get_object_list(self): - return [] - station = self.view.website.station - qs = station.get_played( - models = [ programs.Diffusion, programs.Track ], - ).filter( - date__year = int(year), date__month = int(month), - date__day = int(day) - ) - # TODO for each, exclude if there is a corresponding diffusion - # (that has not been logged) - # if diff and diff != last_diff: - # r.append(cl.make_item - # return [ cl.make_item(log) for log in qs ] + date = self.date_or_default() + if date > datetime.date.today(): + return [] + + logs = controllers.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) + + 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) + + return list(map(self.make_item, items)) - @staticmethod - def get_date_url(date): - return 'TODO'