diff --git a/aircox/__init__.py b/aircox/__init__.py index e69de29..877e873 100755 --- a/aircox/__init__.py +++ b/aircox/__init__.py @@ -0,0 +1,3 @@ + +default_app_config = 'aircox.apps.AircoxConfig' + diff --git a/aircox/admin.py b/aircox/admin.py index e763b7a..7f99478 100755 --- a/aircox/admin.py +++ b/aircox/admin.py @@ -81,9 +81,8 @@ class ProgramAdmin(NameableAdmin): schedule.boolean = True schedule.short_description = _("Schedule") - list_display = ('id', 'name', 'active', 'schedule') - fields = NameableAdmin.fields + [ 'active', 'station' ] - # TODO list_display + list_display = ('id', 'name', 'active', 'schedule', 'sync') + fields = NameableAdmin.fields + [ 'active', 'station','sync' ] inlines = [ ScheduleInline, StreamInline ] # SO#8074161 diff --git a/aircox/apps.py b/aircox/apps.py new file mode 100644 index 0000000..f95d3cb --- /dev/null +++ b/aircox/apps.py @@ -0,0 +1,10 @@ +from django.apps import AppConfig + + +class AircoxConfig(AppConfig): + name = 'aircox' + verbose_name = 'Aircox' + + def ready(self): + import aircox.signals + diff --git a/aircox/management/commands/diffusions_monitor.py b/aircox/management/commands/diffusions_monitor.py index f3b70bd..9bf0a3b 100644 --- a/aircox/management/commands/diffusions_monitor.py +++ b/aircox/management/commands/diffusions_monitor.py @@ -128,16 +128,18 @@ class Command (BaseCommand): '--update', action='store_true', help='generate (unconfirmed) diffusions for the given month. ' 'These diffusions must be confirmed manually by changing ' - 'their type to "normal"') + 'their type to "normal"' + ) group.add_argument( '--clean', action='store_true', - help='remove unconfirmed diffusions older than the given month') - + help='remove unconfirmed diffusions older than the given month' + ) group.add_argument( '--check', action='store_true', - help='check future unconfirmed diffusions from the given date ' - 'agains\'t schedules and remove it if that do not match any ' - 'schedule') + help='check unconfirmed later diffusions from the given ' + 'date again'\'t schedule. If no schedule is found, remove ' + 'it.' + ) group = parser.add_argument_group('date') group.add_argument( @@ -161,7 +163,6 @@ class Command (BaseCommand): 'diffusions except those that conflicts with others' ) - def handle (self, *args, **options): date = tz.datetime(year = options.get('year'), month = options.get('month'), diff --git a/aircox/models.py b/aircox/models.py index 66cfc49..953b7c8 100755 --- a/aircox/models.py +++ b/aircox/models.py @@ -294,6 +294,11 @@ class Program(Nameable): default = True, help_text = _('if not checked this program is no longer active') ) + sync = models.BooleanField( + _('syncronise'), + default = True, + help_text = _('update later diffusions according to schedule changes') + ) @property def path(self): @@ -503,6 +508,24 @@ class Schedule(models.Model): help_text = 'this schedule is a rerun of this one', ) + # initial cached data + __initial = None + + def changed(self, fields = ['date','duration','frequency']): + initial = self._Schedule__initial + if not initial: + return + + before, now = self.__initial, self.__dict__ + before, now = { + f: getattr(before, f) for f in fields + if hasattr(before, f) + }, { + f: getattr(now, f) for f in fields + if hasattr(now, f) + } + return before == now + @property def end(self): return self.date + utils.to_timedelta(self.duration) @@ -627,6 +650,10 @@ class Schedule(models.Model): ] return diffusions + def __init__(self, *args, **kwargs): + super().__init__(*args, **kwargs) + self.__initial = self.__dict__.copy() + def __str__(self): return ' | '.join([ '#' + str(self.id), self.program.name, self.get_frequency_display(), diff --git a/aircox/signals.py b/aircox/signals.py new file mode 100644 index 0000000..a2def84 --- /dev/null +++ b/aircox/signals.py @@ -0,0 +1,63 @@ +from django.db.models.signals import post_save, pre_delete + +from django.dispatch import receiver +from django.utils.translation import ugettext as _, ugettext_lazy +from django.contrib.contenttypes.models import ContentType + +import aircox.models as models +import aircox.utils as utils + +# FIXME: avoid copy of the code in schedule_post_saved and +# schedule_pre_delete + +@receiver(post_save, sender=models.Schedule) +def schedule_post_saved(sender, instance, created, *args, **kwargs): + if not instance.program.sync: + return + + initial = instance._Schedule__initial + if not initial or not instance.changed(['date','duration', 'frequency']): + return + + # old schedule and timedelta + old_sched = models.Schedule( + date = initial['date'], + duration = initial['duration'], + frequency = initial['frequency'], + ) + delta = instance.date - old_sched.date + + # update diffusions... + qs = models.Diffusion.objects.get_after().filter( + program = instance.program + ) + for diff in qs: + if not old_sched.match(diff.date): + continue + diff.start += delta + diff.end = diff.start + utils.to_timedelta(instance.duration) + diff.save() + +@receiver(pre_delete, sender=models.Schedule) +def schedule_pre_delete(sender, instance, *args, **kwargs): + if not instance.program.sync: + return + + initial = instance._Schedule__initial + if not initial or not instance.changed(['date','duration', 'frequency']): + return + + old_sched = models.Schedule( + date = initial['date'], + duration = initial['duration'], + frequency = initial['frequency'], + ) + + qs = models.Diffusion.objects.get_after().filter( + program = instance.program + ) + for diff in qs: + if not old_sched.match(diff.date): + continue + diff.delete() + diff --git a/aircox_cms/__init__.py b/aircox_cms/__init__.py index e69de29..3ab7718 100644 --- a/aircox_cms/__init__.py +++ b/aircox_cms/__init__.py @@ -0,0 +1,3 @@ + +default_app_config = 'aircox_cms.apps.AircoxCMSConfig' + diff --git a/aircox_cms/apps.py b/aircox_cms/apps.py index 7ef3fea..03bc796 100644 --- a/aircox_cms/apps.py +++ b/aircox_cms/apps.py @@ -1,5 +1,9 @@ from django.apps import AppConfig +class AircoxCMSConfig(AppConfig): + name = 'aircox_cms' + verbose_name = 'Aircox CMS' + + def ready(self): + import aircox_cms.signals -class CmsConfig(AppConfig): - name = 'cms' diff --git a/aircox_cms/models.py b/aircox_cms/models.py index c5a1c55..4987f1e 100644 --- a/aircox_cms/models.py +++ b/aircox_cms/models.py @@ -33,8 +33,6 @@ import aircox_cms.settings as settings from aircox_cms.utils import image_url from aircox_cms.sections import * -import aircox_cms.signals - @register_setting class WebsiteSettings(BaseSetting): diff --git a/aircox_cms/signals.py b/aircox_cms/signals.py index d28c2a1..7d88d01 100644 --- a/aircox_cms/signals.py +++ b/aircox_cms/signals.py @@ -6,6 +6,8 @@ from django.contrib.contenttypes.models import ContentType from wagtail.wagtailcore.models import Page, Site import aircox.models as aircox +import aircox_cms.models as models +import aircox_cms.sections as sections import aircox_cms.utils as utils # on a new diffusion @@ -16,16 +18,12 @@ def station_post_saved(sender, instance, created, *args, **kwargs): Create the basis for the website: set up settings and pages that are common. """ - from aircox_cms.models import \ - WebsiteSettings, Publication, ProgramPage, \ - TimetablePage, DynamicListPage, LogsPage - import aircox_cms.sections as sections if not created: return root_page = Page.objects.get(id=1) - homepage = Publication( + homepage = models.Publication( title = instance.name, slug = instance.slug, body = _( @@ -46,7 +44,7 @@ def station_post_saved(sender, instance, created, *args, **kwargs): site.save() # settings - website_settings = WebsiteSettings( + website_settings = models.WebsiteSettings( site = site, station = instance, description = _("The website of the {name} radio").format( @@ -57,13 +55,13 @@ def station_post_saved(sender, instance, created, *args, **kwargs): ) # timetable - timetable = TimetablePage( + timetable = models.TimetablePage( title = _('Timetable'), ) homepage.add_child(instance = timetable) # list page (search, terms) - list_page = DynamicListPage( + list_page = models.DynamicListPage( # title is dynamic: no need to specify title = _('Search'), ) @@ -71,7 +69,7 @@ def station_post_saved(sender, instance, created, *args, **kwargs): website_settings.list_page = list_page # programs' page: list of programs in a section - programs = Publication( + programs = models.Publication( title = _('Programs'), ) homepage.add_child(instance = programs) @@ -93,7 +91,7 @@ def station_post_saved(sender, instance, created, *args, **kwargs): website_settings.sync = True # logs (because it is a cool feature) - logs = LogsPage( + logs = models.LogsPage( title = _('Previously on air'), station = instance, ) @@ -106,7 +104,6 @@ def station_post_saved(sender, instance, created, *args, **kwargs): @receiver(post_save, sender=aircox.Program) def program_post_saved(sender, instance, created, *args, **kwargs): - import aircox_cms.models as models if not created or instance.page.count(): return @@ -131,10 +128,11 @@ def program_post_saved(sender, instance, created, *args, **kwargs): ) parent.add_child(instance = page) + @receiver(pre_delete, sender=aircox.Program) def program_post_deleted(sender, instance, *args, **kwargs): - if not instance.page or ( - instance.page.first().body or + if not instance.page.count() or ( + instance.page.first().specific.body or Page.objects.descendant_of(instance).count() ): return @@ -143,9 +141,6 @@ def program_post_deleted(sender, instance, *args, **kwargs): @receiver(post_save, sender=aircox.Diffusion) def diffusion_post_saved(sender, instance, created, *args, **kwargs): - import aircox_cms.models as models - # TODO/FIXME: what about confirmed/unconfirmed; - # what about delete when not confirmed? if not created or instance.page.count(): return @@ -158,7 +153,7 @@ def diffusion_post_saved(sender, instance, created, *args, **kwargs): @receiver(pre_delete, sender=aircox.Program) def diffusion_post_deleted(sender, instance, *args, **kwargs): - if not instance.page or instance.page.first().body: + if not instance.page.count() or instance.page.first().specific.body: return instance.page.delete()