forked from rc/aircox
		
	issue #1: synchronise programs' schedules and later diffusions (update, delete); fix signal import in apps
This commit is contained in:
		@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					default_app_config = 'aircox.apps.AircoxConfig'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -81,9 +81,8 @@ class ProgramAdmin(NameableAdmin):
 | 
				
			|||||||
    schedule.boolean = True
 | 
					    schedule.boolean = True
 | 
				
			||||||
    schedule.short_description = _("Schedule")
 | 
					    schedule.short_description = _("Schedule")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    list_display = ('id', 'name', 'active', 'schedule')
 | 
					    list_display = ('id', 'name', 'active', 'schedule', 'sync')
 | 
				
			||||||
    fields = NameableAdmin.fields + [ 'active', 'station' ]
 | 
					    fields = NameableAdmin.fields + [ 'active', 'station','sync' ]
 | 
				
			||||||
    # TODO list_display
 | 
					 | 
				
			||||||
    inlines = [ ScheduleInline, StreamInline ]
 | 
					    inlines = [ ScheduleInline, StreamInline ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # SO#8074161
 | 
					    # SO#8074161
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										10
									
								
								aircox/apps.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										10
									
								
								aircox/apps.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,10 @@
 | 
				
			|||||||
 | 
					from django.apps import AppConfig
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class AircoxConfig(AppConfig):
 | 
				
			||||||
 | 
					    name = 'aircox'
 | 
				
			||||||
 | 
					    verbose_name = 'Aircox'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def ready(self):
 | 
				
			||||||
 | 
					        import aircox.signals
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -128,16 +128,18 @@ class Command (BaseCommand):
 | 
				
			|||||||
            '--update', action='store_true',
 | 
					            '--update', action='store_true',
 | 
				
			||||||
            help='generate (unconfirmed) diffusions for the given month. '
 | 
					            help='generate (unconfirmed) diffusions for the given month. '
 | 
				
			||||||
                 'These diffusions must be confirmed manually by changing '
 | 
					                 'These diffusions must be confirmed manually by changing '
 | 
				
			||||||
                 'their type to "normal"')
 | 
					                 'their type to "normal"'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
        group.add_argument(
 | 
					        group.add_argument(
 | 
				
			||||||
            '--clean', action='store_true',
 | 
					            '--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(
 | 
					        group.add_argument(
 | 
				
			||||||
            '--check', action='store_true',
 | 
					            '--check', action='store_true',
 | 
				
			||||||
            help='check future unconfirmed diffusions from the given date '
 | 
					            help='check unconfirmed later diffusions from the given '
 | 
				
			||||||
                 'agains\'t schedules and remove it if that do not match any '
 | 
					                 'date again'\'t schedule. If no schedule is found, remove '
 | 
				
			||||||
                 'schedule')
 | 
					                 'it.'
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        group = parser.add_argument_group('date')
 | 
					        group = parser.add_argument_group('date')
 | 
				
			||||||
        group.add_argument(
 | 
					        group.add_argument(
 | 
				
			||||||
@ -161,7 +163,6 @@ class Command (BaseCommand):
 | 
				
			|||||||
                 'diffusions except those that conflicts with others'
 | 
					                 'diffusions except those that conflicts with others'
 | 
				
			||||||
        )
 | 
					        )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    def handle (self, *args, **options):
 | 
					    def handle (self, *args, **options):
 | 
				
			||||||
        date = tz.datetime(year = options.get('year'),
 | 
					        date = tz.datetime(year = options.get('year'),
 | 
				
			||||||
                                 month = options.get('month'),
 | 
					                                 month = options.get('month'),
 | 
				
			||||||
 | 
				
			|||||||
@ -294,6 +294,11 @@ class Program(Nameable):
 | 
				
			|||||||
        default = True,
 | 
					        default = True,
 | 
				
			||||||
        help_text = _('if not checked this program is no longer active')
 | 
					        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
 | 
					    @property
 | 
				
			||||||
    def path(self):
 | 
					    def path(self):
 | 
				
			||||||
@ -503,6 +508,24 @@ class Schedule(models.Model):
 | 
				
			|||||||
        help_text = 'this schedule is a rerun of this one',
 | 
					        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
 | 
					    @property
 | 
				
			||||||
    def end(self):
 | 
					    def end(self):
 | 
				
			||||||
        return self.date + utils.to_timedelta(self.duration)
 | 
					        return self.date + utils.to_timedelta(self.duration)
 | 
				
			||||||
@ -627,6 +650,10 @@ class Schedule(models.Model):
 | 
				
			|||||||
        ]
 | 
					        ]
 | 
				
			||||||
        return diffusions
 | 
					        return diffusions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__(self, *args, **kwargs):
 | 
				
			||||||
 | 
					        super().__init__(*args, **kwargs)
 | 
				
			||||||
 | 
					        self.__initial = self.__dict__.copy()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__(self):
 | 
					    def __str__(self):
 | 
				
			||||||
        return ' | '.join([ '#' + str(self.id), self.program.name,
 | 
					        return ' | '.join([ '#' + str(self.id), self.program.name,
 | 
				
			||||||
                            self.get_frequency_display(),
 | 
					                            self.get_frequency_display(),
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										63
									
								
								aircox/signals.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										63
									
								
								aircox/signals.py
									
									
									
									
									
										Normal file
									
								
							@ -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()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
 | 
					default_app_config = 'aircox_cms.apps.AircoxCMSConfig'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,5 +1,9 @@
 | 
				
			|||||||
from django.apps import AppConfig
 | 
					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'
 | 
					 | 
				
			||||||
 | 
				
			|||||||
@ -33,8 +33,6 @@ import aircox_cms.settings as settings
 | 
				
			|||||||
from aircox_cms.utils import image_url
 | 
					from aircox_cms.utils import image_url
 | 
				
			||||||
from aircox_cms.sections import *
 | 
					from aircox_cms.sections import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import aircox_cms.signals
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
@register_setting
 | 
					@register_setting
 | 
				
			||||||
class WebsiteSettings(BaseSetting):
 | 
					class WebsiteSettings(BaseSetting):
 | 
				
			||||||
 | 
				
			|||||||
@ -6,6 +6,8 @@ from django.contrib.contenttypes.models import ContentType
 | 
				
			|||||||
from wagtail.wagtailcore.models import Page, Site
 | 
					from wagtail.wagtailcore.models import Page, Site
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import aircox.models as aircox
 | 
					import aircox.models as aircox
 | 
				
			||||||
 | 
					import aircox_cms.models as models
 | 
				
			||||||
 | 
					import aircox_cms.sections as sections
 | 
				
			||||||
import aircox_cms.utils as utils
 | 
					import aircox_cms.utils as utils
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# on a new diffusion
 | 
					# 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
 | 
					    Create the basis for the website: set up settings and pages
 | 
				
			||||||
    that are common.
 | 
					    that are common.
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    from aircox_cms.models import \
 | 
					 | 
				
			||||||
            WebsiteSettings, Publication, ProgramPage, \
 | 
					 | 
				
			||||||
            TimetablePage, DynamicListPage, LogsPage
 | 
					 | 
				
			||||||
    import aircox_cms.sections as sections
 | 
					 | 
				
			||||||
    if not created:
 | 
					    if not created:
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    root_page = Page.objects.get(id=1)
 | 
					    root_page = Page.objects.get(id=1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    homepage = Publication(
 | 
					    homepage = models.Publication(
 | 
				
			||||||
        title = instance.name,
 | 
					        title = instance.name,
 | 
				
			||||||
        slug = instance.slug,
 | 
					        slug = instance.slug,
 | 
				
			||||||
        body = _(
 | 
					        body = _(
 | 
				
			||||||
@ -46,7 +44,7 @@ def station_post_saved(sender, instance, created, *args, **kwargs):
 | 
				
			|||||||
    site.save()
 | 
					    site.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # settings
 | 
					    # settings
 | 
				
			||||||
    website_settings = WebsiteSettings(
 | 
					    website_settings = models.WebsiteSettings(
 | 
				
			||||||
        site = site,
 | 
					        site = site,
 | 
				
			||||||
        station = instance,
 | 
					        station = instance,
 | 
				
			||||||
        description = _("The website of the {name} radio").format(
 | 
					        description = _("The website of the {name} radio").format(
 | 
				
			||||||
@ -57,13 +55,13 @@ def station_post_saved(sender, instance, created, *args, **kwargs):
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # timetable
 | 
					    # timetable
 | 
				
			||||||
    timetable = TimetablePage(
 | 
					    timetable = models.TimetablePage(
 | 
				
			||||||
        title = _('Timetable'),
 | 
					        title = _('Timetable'),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    homepage.add_child(instance = timetable)
 | 
					    homepage.add_child(instance = timetable)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # list page (search, terms)
 | 
					    # list page (search, terms)
 | 
				
			||||||
    list_page = DynamicListPage(
 | 
					    list_page = models.DynamicListPage(
 | 
				
			||||||
        # title is dynamic: no need to specify
 | 
					        # title is dynamic: no need to specify
 | 
				
			||||||
        title = _('Search'),
 | 
					        title = _('Search'),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
@ -71,7 +69,7 @@ def station_post_saved(sender, instance, created, *args, **kwargs):
 | 
				
			|||||||
    website_settings.list_page = list_page
 | 
					    website_settings.list_page = list_page
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # programs' page: list of programs in a section
 | 
					    # programs' page: list of programs in a section
 | 
				
			||||||
    programs = Publication(
 | 
					    programs = models.Publication(
 | 
				
			||||||
        title = _('Programs'),
 | 
					        title = _('Programs'),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    homepage.add_child(instance = programs)
 | 
					    homepage.add_child(instance = programs)
 | 
				
			||||||
@ -93,7 +91,7 @@ def station_post_saved(sender, instance, created, *args, **kwargs):
 | 
				
			|||||||
    website_settings.sync = True
 | 
					    website_settings.sync = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # logs (because it is a cool feature)
 | 
					    # logs (because it is a cool feature)
 | 
				
			||||||
    logs = LogsPage(
 | 
					    logs = models.LogsPage(
 | 
				
			||||||
        title = _('Previously on air'),
 | 
					        title = _('Previously on air'),
 | 
				
			||||||
        station = instance,
 | 
					        station = instance,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
@ -106,7 +104,6 @@ def station_post_saved(sender, instance, created, *args, **kwargs):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@receiver(post_save, sender=aircox.Program)
 | 
					@receiver(post_save, sender=aircox.Program)
 | 
				
			||||||
def program_post_saved(sender, instance, created, *args, **kwargs):
 | 
					def program_post_saved(sender, instance, created, *args, **kwargs):
 | 
				
			||||||
    import aircox_cms.models as models
 | 
					 | 
				
			||||||
    if not created or instance.page.count():
 | 
					    if not created or instance.page.count():
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -131,10 +128,11 @@ def program_post_saved(sender, instance, created, *args, **kwargs):
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
    parent.add_child(instance = page)
 | 
					    parent.add_child(instance = page)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@receiver(pre_delete, sender=aircox.Program)
 | 
					@receiver(pre_delete, sender=aircox.Program)
 | 
				
			||||||
def program_post_deleted(sender, instance, *args, **kwargs):
 | 
					def program_post_deleted(sender, instance, *args, **kwargs):
 | 
				
			||||||
    if not instance.page or (
 | 
					    if not instance.page.count() or (
 | 
				
			||||||
            instance.page.first().body or
 | 
					            instance.page.first().specific.body or
 | 
				
			||||||
            Page.objects.descendant_of(instance).count()
 | 
					            Page.objects.descendant_of(instance).count()
 | 
				
			||||||
            ):
 | 
					            ):
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
@ -143,9 +141,6 @@ def program_post_deleted(sender, instance, *args, **kwargs):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@receiver(post_save, sender=aircox.Diffusion)
 | 
					@receiver(post_save, sender=aircox.Diffusion)
 | 
				
			||||||
def diffusion_post_saved(sender, instance, created, *args, **kwargs):
 | 
					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():
 | 
					    if not created or instance.page.count():
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -158,7 +153,7 @@ def diffusion_post_saved(sender, instance, created, *args, **kwargs):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@receiver(pre_delete, sender=aircox.Program)
 | 
					@receiver(pre_delete, sender=aircox.Program)
 | 
				
			||||||
def diffusion_post_deleted(sender, instance, *args, **kwargs):
 | 
					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
 | 
					        return
 | 
				
			||||||
    instance.page.delete()
 | 
					    instance.page.delete()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user