From fff4801ac7df0f7e5532aba0f5341991c7eb8342 Mon Sep 17 00:00:00 2001 From: bkfox Date: Sun, 28 Jul 2019 18:40:59 +0200 Subject: [PATCH] work on it --- aircox/admin/sound.py | 8 +- aircox/management/commands/sounds_monitor.py | 9 +- aircox/models.py | 85 +++++++++---------- aircox_web/assets/styles.scss | 7 +- aircox_web/models.py | 4 + aircox_web/static/aircox_web/assets/main.css | 6 +- aircox_web/templates/aircox_web/base.html | 16 +++- .../templates/aircox_web/diffusion_item.html | 32 +++---- .../templates/aircox_web/diffusion_page.html | 14 +++ .../templates/aircox_web/diffusions.html | 12 +-- aircox_web/templates/aircox_web/page.html | 6 -- .../templates/aircox_web/podcast_item.html | 8 ++ .../templates/aircox_web/program_base.html | 29 +++++++ .../templates/aircox_web/program_page.html | 32 +------ aircox_web/views.py | 68 ++++++++++----- 15 files changed, 190 insertions(+), 146 deletions(-) create mode 100644 aircox_web/templates/aircox_web/diffusion_page.html create mode 100644 aircox_web/templates/aircox_web/podcast_item.html create mode 100644 aircox_web/templates/aircox_web/program_base.html diff --git a/aircox/admin/sound.py b/aircox/admin/sound.py index 2cdca5c..3203e16 100644 --- a/aircox/admin/sound.py +++ b/aircox/admin/sound.py @@ -9,12 +9,12 @@ from .playlist import TracksInline class SoundAdmin(admin.ModelAdmin): fields = None list_display = ['id', 'name', 'program', 'type', 'duration', 'mtime', - 'public', 'good_quality', 'path'] - list_filter = ('program', 'type', 'good_quality', 'public') + 'is_public', 'is_good_quality', 'path'] + list_filter = ('program', 'type', 'is_good_quality', 'is_public') fieldsets = [ (None, {'fields': ['name', 'path', 'type', 'program', 'diffusion']}), - (None, {'fields': ['embed', 'duration', 'public', 'mtime']}), - (None, {'fields': ['good_quality']}) + (None, {'fields': ['embed', 'duration', 'is_public', 'mtime']}), + (None, {'fields': ['is_good_quality']}) ] readonly_fields = ('path', 'duration',) inlines = [TracksInline] diff --git a/aircox/management/commands/sounds_monitor.py b/aircox/management/commands/sounds_monitor.py index 6f4704e..c034470 100755 --- a/aircox/management/commands/sounds_monitor.py +++ b/aircox/management/commands/sounds_monitor.py @@ -327,18 +327,17 @@ class Command(BaseCommand): """ Check all files where quality has been set to bad """ - import aircox.management.commands.sounds_quality_check \ - as quality_check + import aircox.management.commands.sounds_quality_check as quality_check # get available sound files - sounds = Sound.objects.filter(good_quality=False) \ + sounds = Sound.objects.filter(is_good_quality=False) \ .exclude(type=Sound.Type.removed) if check: self.check_sounds(sounds) files = [ sound.path for sound in sounds - if os.path.exists(sound.path) and sound.good_quality is None + if os.path.exists(sound.path) and sound.is_good_quality is None ] # check quality @@ -358,7 +357,7 @@ class Command(BaseCommand): for sound_info in cmd.good: sound = Sound.objects.get(path=sound_info.path) - sound.good_quality = True + sound.is_good_quality = True update_stats(sound_info, sound) sound.save(check=False) diff --git a/aircox/models.py b/aircox/models.py index 7961fc0..da689fb 100755 --- a/aircox/models.py +++ b/aircox/models.py @@ -326,7 +326,6 @@ class Program(models.Model): .update(path=Concat('path', Substr(F('path'), len(path_)))) - class Stream(models.Model): """ When there are no program scheduled, it is possible to play sounds @@ -522,74 +521,54 @@ class Schedule(models.Model): Return a new datetime with schedule time. Timezone is handled using `schedule.timezone`. """ - time = self.time or self.date - date = tz.datetime(date.year, date.month, date.day, - time.hour, time.minute, 0, 0) - date = self.tz.localize(date) - date = self.tz.normalize(date) + date = tz.datetime.combine(date, self.time) + return self.tz.normalize(self.tz.localize(date)) - return date - - def dates_of_month(self, date=None): + def dates_of_month(self, date): """ Return a list with all matching dates of date.month (=today) Ensure timezone awareness. + + :param datetime.date date: month and year """ if self.frequency == Schedule.Frequency.ponctual: return [] - # first day of month - date = utils.date_or_default(date, datetime.date).replace(day=1) - freq = self.frequency + sched_wday, freq = self.date.weekday(), self.frequency + date = date.replace(day=1) # last of the month - if freq == Schedule.Frequency.last: date = date.replace( day=calendar.monthrange(date.year, date.month)[1]) + date_wday = date.weekday() # end of month before the wanted weekday: move one week back - if date.weekday() < self.date.weekday(): + if date_wday < sched_wday: date -= tz.timedelta(days=7) - - delta = self.date.weekday() - date.weekday() - date += tz.timedelta(days=delta) + date += tz.timedelta(days=sched_wday - date_wday) return [self.normalize(date)] # move to the first day of the month that matches the schedule's weekday # check on SO#3284452 for the formula - first_weekday = date.weekday() - sched_weekday = self.date.weekday() - date += tz.timedelta(days=(7 if first_weekday > sched_weekday else 0) - - first_weekday + sched_weekday) - month = date.month - - dates = [] + date_wday, month = date.weekday(), date.month + date += tz.timedelta(days=(7 if date_wday > sched_wday else 0) - + date_wday + sched_wday) if freq == Schedule.Frequency.one_on_two: - # check date base on a diff of dates base on a 14 days delta - diff = utils.cast_date(date, datetime.date) - \ - utils.cast_date(self.date, datetime.date) - - if diff.days % 14: + # - adjust date with modulo 14 (= 2 weeks in days) + # - there are max 3 "weeks on two" per month + if (date - self.date).days % 14: date += tz.timedelta(days=7) - - while date.month == month: - dates.append(date) - date += tz.timedelta(days=14) + dates = (date + tz.timedelta(days=14*i) for i in range(0, 3)) else: - week = 0 + dates = (date + tz.timedelta(days=7*week) for week in range(0, 5) + if freq & (0b1 << week)) - while week < 5 and date.month == month: - if freq & (0b1 << week): - dates.append(date) - date += tz.timedelta(days=7) - week += 1 - - return [self.normalize(date) for date in dates] + return [self.normalize(date) for date in dates if date.month == month] def diffusions_of_month(self, date=None, exclude_saved=False): """ @@ -625,6 +604,7 @@ class Schedule(models.Model): # FIXME: daylight saving bug: delta misses an hour when diffusion and # rerun are not on the same daylight-saving timezone + # -> solution: add rerun=True param, and gen reruns from initial for each diffusions += [ Diffusion( program=self.program, @@ -922,6 +902,15 @@ class Diffusion(models.Model): ) +class SoundQuerySet(models.QuerySet): + def podcasts(self): + """ Return sound available as podcasts """ + return self.filter(Q(embed__isnull=False) | Q(is_public=True)) + + def diffusion(self, diffusion): + return self.filter(diffusion=diffusion) + + class Sound(models.Model): """ A Sound is the representation of a sound file that can be either an excerpt @@ -942,10 +931,10 @@ class Sound(models.Model): help_text=_('program related to it'), ) diffusion = models.ForeignKey( - 'Diffusion', + Diffusion, models.SET_NULL, verbose_name=_('diffusion'), blank=True, null=True, - on_delete=models.SET_NULL, + limit_choices_to={'initial__isnull': True}, help_text=_('initial diffusion related it') ) type = models.SmallIntegerField( @@ -980,17 +969,19 @@ class Sound(models.Model): blank=True, null=True, help_text=_('last modification date and time'), ) - good_quality = models.NullBooleanField( + is_good_quality = models.BooleanField( _('good quality'), - help_text=_('sound\'s quality is okay'), + help_text=_('sound meets quality requirements for diffusion'), blank=True, null=True ) - public = models.BooleanField( + is_public = models.BooleanField( _('public'), default=False, help_text=_('the sound is accessible to the public') ) + objects = SoundQuerySet.as_manager() + def get_mtime(self): """ Get the last modification date from file @@ -1080,7 +1071,7 @@ class Sound(models.Model): if self.mtime != mtime: self.mtime = mtime - self.good_quality = None + self.is_good_quality = None logger.info('sound %s: m_time has changed. Reset quality info', self.path) diff --git a/aircox_web/assets/styles.scss b/aircox_web/assets/styles.scss index 9b0875b..5f55c82 100644 --- a/aircox_web/assets/styles.scss +++ b/aircox_web/assets/styles.scss @@ -28,7 +28,12 @@ $body-background-color: $light; /** page **/ .page { - .header { + & > .cover { + float: right; + max-width: 45%; + } + + & > .header { margin-bottom: 1.5em; } diff --git a/aircox_web/models.py b/aircox_web/models.py index 4fa8ec8..6b2957a 100644 --- a/aircox_web/models.py +++ b/aircox_web/models.py @@ -132,6 +132,10 @@ class Page(StatusModel): objects = PageQueryset.as_manager() + @property + def is_published(self): + return self.status == self.STATUS.published + @property def path(self): return reverse(self.detail_url_name, kwargs={'slug': self.slug}) diff --git a/aircox_web/static/aircox_web/assets/main.css b/aircox_web/static/aircox_web/assets/main.css index 1d02f69..b988d70 100644 --- a/aircox_web/static/aircox_web/assets/main.css +++ b/aircox_web/static/aircox_web/assets/main.css @@ -7165,7 +7165,11 @@ label.panel-block { } */ /** page **/ -.page .header { +.page > .cover { + float: right; + max-width: 45%; } + +.page > .header { margin-bottom: 1.5em; } .page .headline { diff --git a/aircox_web/templates/aircox_web/base.html b/aircox_web/templates/aircox_web/base.html index 820872f..796790b 100644 --- a/aircox_web/templates/aircox_web/base.html +++ b/aircox_web/templates/aircox_web/base.html @@ -41,25 +41,35 @@ Context:
-
+
{% block header %}

{% block title %}{% endblock %}

+ + {% if parent %} +

+ + ❬ {{ parent.title }} +

+ {% endif %} {% endblock %}
{% block main %}{% endblock main %}
-
diff --git a/aircox_web/templates/aircox_web/diffusion_item.html b/aircox_web/templates/aircox_web/diffusion_item.html index 3f10603..c4bd8c5 100644 --- a/aircox_web/templates/aircox_web/diffusion_item.html +++ b/aircox_web/templates/aircox_web/diffusion_item.html @@ -18,27 +18,27 @@ Context variables: class="small-cover">
-
-
- {% if d_page %} - {{ d_page.title }} - {% endif %} +
+ {% if d_page %} + {{ d_page.title }} + {% endif %} +
- {% if not page or p_page != page %} - {% if d_page %} — {% endif %} - {% if p_page %} - - {{ p_page.title }} - {% else %} - {{ program.name }} - {% endif %} - {% endif %} - +
+ {% if not page or p_page != page %} + {% if p_page %} + + {{ p_page.title }} + {% else %} + {{ program.name }} + {% endif %} + {% if not hide_schedule %} — {% endif %} + {% endif %} {% if not hide_schedule %} {% endif %} diff --git a/aircox_web/templates/aircox_web/diffusion_page.html b/aircox_web/templates/aircox_web/diffusion_page.html new file mode 100644 index 0000000..63fcd13 --- /dev/null +++ b/aircox_web/templates/aircox_web/diffusion_page.html @@ -0,0 +1,14 @@ +{% extends "aircox_web/program_base.html" %} +{% load i18n %} + +{% block main %} +{{ block.super }} + +{% if podcasts %} +{% for object in podcasts %} +{% include "aircox_web/podcast_item.html" %} +{% endfor %} +{% endif %} + +{% endwith %} + diff --git a/aircox_web/templates/aircox_web/diffusions.html b/aircox_web/templates/aircox_web/diffusions.html index 43e0e7d..c7ab8f3 100644 --- a/aircox_web/templates/aircox_web/diffusions.html +++ b/aircox_web/templates/aircox_web/diffusions.html @@ -12,18 +12,8 @@ {% endblock %} -{% block header %} -{{ block.super }} -{% if program %} -

- ❬ {{ program.name }} -

-{% endif %} -{% endblock %} - - {% block content %} -
+
{% for object in object_list %} {% include "aircox_web/diffusion_item.html" %} {% endfor %} diff --git a/aircox_web/templates/aircox_web/page.html b/aircox_web/templates/aircox_web/page.html index 6923f29..152d2cd 100644 --- a/aircox_web/templates/aircox_web/page.html +++ b/aircox_web/templates/aircox_web/page.html @@ -27,10 +27,4 @@ Context: {% endblock %} -{% block side_nav %} -{% if cover is not None %} - -{% endif %} -{% endblock %} - diff --git a/aircox_web/templates/aircox_web/podcast_item.html b/aircox_web/templates/aircox_web/podcast_item.html new file mode 100644 index 0000000..0b2d0a4 --- /dev/null +++ b/aircox_web/templates/aircox_web/podcast_item.html @@ -0,0 +1,8 @@ +
+ {% if object.embed %} + {{ object.embed }} + {% else %} +
+ diff --git a/aircox_web/templates/aircox_web/program_base.html b/aircox_web/templates/aircox_web/program_base.html new file mode 100644 index 0000000..5995e12 --- /dev/null +++ b/aircox_web/templates/aircox_web/program_base.html @@ -0,0 +1,29 @@ +{% extends "aircox_web/page.html" %} +{% load i18n %} + +{% block side_nav %} +{{ block.super }} + +{% if diffusions %} +
+

{% trans "Last shows" %}

+ + {% for object in diffusions %} + {% include "aircox_web/diffusion_item.html" %} + {% endfor %} + +
+
+{% endif %} +{% endblock %} + diff --git a/aircox_web/templates/aircox_web/program_page.html b/aircox_web/templates/aircox_web/program_page.html index f11d15e..f7dd92c 100644 --- a/aircox_web/templates/aircox_web/program_page.html +++ b/aircox_web/templates/aircox_web/program_page.html @@ -1,38 +1,8 @@ -{% extends "aircox_web/page.html" %} +{% extends "aircox_web/program_base.html" %} {% load i18n %} -{% with page.program as program %} {% block header %} {{ block.super }} {% include "aircox_web/program_header.html" %} {% endblock %} -{% block side_nav %} -{{ block.super }} - -{% if diffusions %} -
-

{% trans "Last shows" %}

- - {% for object in diffusions %} - {% include "aircox_web/diffusion_item.html" %} - {% endfor %} - -
-
-{% endif %} -{% endblock %} - - -{% endwith %} - diff --git a/aircox_web/views.py b/aircox_web/views.py index 262bc35..fc85db3 100644 --- a/aircox_web/views.py +++ b/aircox_web/views.py @@ -1,11 +1,10 @@ from collections import OrderedDict, deque import datetime -from django.db.models import Q -from django.core.paginator import Paginator +from django.http import Http404 from django.shortcuts import get_object_or_404 from django.utils.translation import ugettext_lazy as _ -from django.views.generic import TemplateView, DetailView, ListView +from django.views.generic import DetailView, ListView from django.views.generic.base import TemplateResponseMixin, ContextMixin from content_editor.contents import contents_for_item @@ -36,9 +35,14 @@ def route_page(request, path=None, *args, model=None, site=None, **kwargs): class BaseView(TemplateResponseMixin, ContextMixin): - title = None - cover = None site = None + """ Current website """ + nav_side = False + """ Show side navigation """ + title = None + """ Page title """ + cover = None + """ Page cover """ def dispatch(self, request, *args, site=None, **kwargs): self.site = site if site is not None else \ @@ -53,6 +57,7 @@ class BaseView(TemplateResponseMixin, ContextMixin): kwargs.setdefault('site', self.site) kwargs.setdefault('cover', self.cover) + kwargs.setdefault('nav_side', self.nav_side) return super().get_context_data(**kwargs) @@ -79,31 +84,52 @@ class PageView(BaseView, DetailView): return super().get_context_data(**kwargs) -class ProgramPageView(PageView): - """ Base view class for pages. """ +class BaseProgramView(PageView): + """ Base view class for programs and their sub-pages. """ + nav_side = True + list_count=5 + + def get_diffusions_queryset(self, program, queryset=None): + qs = get_diffusions_with_page() if queryset is None else queryset + return qs.before().filter(program=program).order_by('-start') + + def get_context_data(self, program, **kwargs): + if not hasattr(program, 'page') or not program.page.is_published: + raise Http404 + + if 'diffusions' not in kwargs: + diffs = self.get_diffusions_queryset(program)[:self.list_count] + kwargs['diffusions'] = diffs + return super().get_context_data(program=program, **kwargs) + + +class ProgramPageView(BaseProgramView): template_name = 'aircox_web/program_page.html' model = ProgramPage - list_count=10 - def get_queryset(self): return super().get_queryset().select_related('program') - def get_context_data(self, program=None, diffusions=None, **kwargs): - program = program or self.object.program - diffusions = diffusions or \ - get_diffusions_with_page().filter(program=program) - return super().get_context_data( - program=program, - diffusions=diffusions.order_by('-start')[:self.list_count], - **kwargs - ) + def get_context_data(self, **kwargs): + kwargs.setdefault('program', self.object.program) + return super().get_context_data(**kwargs) -class DiffusionPageView(PageView): - # template_name = 'aircox_web/diffusion.html' +class DiffusionPageView(BaseProgramView): + template_name = 'aircox_web/program_base.html' model = DiffusionPage + def get_podcasts(self, diffusion): + return aircox.Sound.objects.diffusion(diffusion).podcasts() + + def get_context_data(self, **kwargs): + diffusion = self.object.diffusion + kwargs.setdefault('program', diffusion.program) + kwargs.setdefault('parent', getattr(kwargs['program'], 'page', None)) + if not 'podcasts' in kwargs: + kwargs['podcasts'] = self.get_podcasts(diffusion) + print('get prodcasts...', kwargs['podcasts'], diffusion) + return super().get_context_data(**kwargs) # TODO: pagination: in template, only a limited number of pages displayed @@ -133,7 +159,7 @@ class DiffusionsView(BaseView, ListView): program = kwargs.setdefault('program', self.program) if program is not None and hasattr(program, 'page'): kwargs.setdefault('cover', program.page.cover) - kwargs.setdefault('page', program.page) + kwargs.setdefault('parent', program.page) return super().get_context_data(**kwargs)