add programs_to_cms commands to make syncs between programs and cms apps
This commit is contained in:
		
							
								
								
									
										87
									
								
								cms/management/commands/programs_to_cms.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								cms/management/commands/programs_to_cms.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,87 @@
 | 
			
		||||
"""
 | 
			
		||||
Create missing publications for diffusions and programs already existing.
 | 
			
		||||
 | 
			
		||||
We limit the creation of diffusion to the elements to those that start at least
 | 
			
		||||
in the last 15 days, and to the future ones.
 | 
			
		||||
 | 
			
		||||
The new publications are not published automatically.
 | 
			
		||||
"""
 | 
			
		||||
import logging
 | 
			
		||||
from argparse import RawTextHelpFormatter
 | 
			
		||||
 | 
			
		||||
from django.core.management.base import BaseCommand, CommandError
 | 
			
		||||
from django.contrib.contenttypes.models import ContentType
 | 
			
		||||
from django.utils import timezone as tz
 | 
			
		||||
 | 
			
		||||
from aircox.programs.models import Program, Diffusion
 | 
			
		||||
from aircox.cms.models import WebsiteSettings, ProgramPage, DiffusionPage
 | 
			
		||||
 | 
			
		||||
logger = logging.getLogger('aircox.tools')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Command (BaseCommand):
 | 
			
		||||
    help= __doc__
 | 
			
		||||
 | 
			
		||||
    def add_arguments (self, parser):
 | 
			
		||||
        parser.formatter_class=RawTextHelpFormatter
 | 
			
		||||
 | 
			
		||||
    def handle (self, *args, **options):
 | 
			
		||||
        for settings in WebsiteSettings.objects.all():
 | 
			
		||||
            logger.info('start sync for website {}'.format(
 | 
			
		||||
                str(settings.site)
 | 
			
		||||
            ))
 | 
			
		||||
 | 
			
		||||
            if not settings.auto_create:
 | 
			
		||||
                logger.warning('auto_create disabled: skip')
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            if not settings.default_program_parent_page:
 | 
			
		||||
                logger.warning('no default program page for this website: skip')
 | 
			
		||||
                continue
 | 
			
		||||
 | 
			
		||||
            logger.info('start syncing programs...')
 | 
			
		||||
 | 
			
		||||
            parent = settings.default_program_parent_page
 | 
			
		||||
            for program in Program.objects.all():
 | 
			
		||||
                if program.page.count():
 | 
			
		||||
                    continue
 | 
			
		||||
 | 
			
		||||
                logger.info('- ' + program.name)
 | 
			
		||||
                page = ProgramPage(
 | 
			
		||||
                    program = program,
 | 
			
		||||
                    title = program.name,
 | 
			
		||||
                    live = False
 | 
			
		||||
                )
 | 
			
		||||
                parent.add_child(instance = page)
 | 
			
		||||
 | 
			
		||||
            logger.info('start syncing diffusions...')
 | 
			
		||||
 | 
			
		||||
            min_date = tz.now().date() - tz.timedelta(days = 20)
 | 
			
		||||
            for diffusion in Diffusion.objects.filter(start__gt = min_date):
 | 
			
		||||
                if diffusion.page.count() or diffusion.initial:
 | 
			
		||||
                    continue
 | 
			
		||||
 | 
			
		||||
                if not diffusion.program.page.count():
 | 
			
		||||
                    if not hasattr(diffusion.program, '__logged_diff_error'):
 | 
			
		||||
                        logger.warning(
 | 
			
		||||
                            'the program {} has no page; skip the creation of '
 | 
			
		||||
                            'page for its diffusions'.format(
 | 
			
		||||
                                diffusion.program.name
 | 
			
		||||
                            )
 | 
			
		||||
                        )
 | 
			
		||||
                        diffusion.program.__logged_diff_error = True
 | 
			
		||||
                    continue
 | 
			
		||||
 | 
			
		||||
                logger.info('- ' + str(diffusion))
 | 
			
		||||
                try:
 | 
			
		||||
                    page = DiffusionPage.from_diffusion(diffusion)
 | 
			
		||||
                    diffusion.program.page.first().add_child(instance = page)
 | 
			
		||||
                except:
 | 
			
		||||
                    import sys
 | 
			
		||||
                    e = sys.exc_info()[0]
 | 
			
		||||
                    logger.error('Error saving', str(diffusion) + ':', e)
 | 
			
		||||
 | 
			
		||||
            logger.info('done')
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -36,6 +36,10 @@ from aircox.cms.sections import *
 | 
			
		||||
 | 
			
		||||
@register_setting
 | 
			
		||||
class WebsiteSettings(BaseSetting):
 | 
			
		||||
    # TODO: #Station assign a website to a programs.model.station when it will
 | 
			
		||||
    #   exist. Update all dependent code such as signal handling
 | 
			
		||||
 | 
			
		||||
    # general website information
 | 
			
		||||
    logo = models.ForeignKey(
 | 
			
		||||
        'wagtailimages.Image',
 | 
			
		||||
        verbose_name = _('logo'),
 | 
			
		||||
@ -51,6 +55,8 @@ class WebsiteSettings(BaseSetting):
 | 
			
		||||
        related_name='+',
 | 
			
		||||
        help_text = _('favicon for the website'),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # comments
 | 
			
		||||
    accept_comments = models.BooleanField(
 | 
			
		||||
        default = True,
 | 
			
		||||
        help_text = _('publish comments automatically without verifying'),
 | 
			
		||||
@ -75,6 +81,31 @@ class WebsiteSettings(BaseSetting):
 | 
			
		||||
        help_text = _('message to display there is an error an incomplete form.'),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    auto_create = models.BooleanField(
 | 
			
		||||
        _('automatic publications'),
 | 
			
		||||
        default = False,
 | 
			
		||||
        help_text = _(
 | 
			
		||||
            'Create automatically new publications for new programs and '
 | 
			
		||||
            'diffusions in the timetable. If set, please complete other '
 | 
			
		||||
            'options of this panel'
 | 
			
		||||
        )
 | 
			
		||||
    )
 | 
			
		||||
    default_program_parent_page = ParentalKey(
 | 
			
		||||
        Page,
 | 
			
		||||
        verbose_name = _('default program parent page'),
 | 
			
		||||
        blank = True, null = True,
 | 
			
		||||
        help_text = _(
 | 
			
		||||
            'Default parent page for program\'s pages. It is used to assign '
 | 
			
		||||
            'a page to the publication of a newly created program and can '
 | 
			
		||||
            'be changed later'
 | 
			
		||||
        ),
 | 
			
		||||
        limit_choices_to = lambda: {
 | 
			
		||||
            'show_in_menus': True,
 | 
			
		||||
            'publication__isnull': False,
 | 
			
		||||
        },
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    panels = [
 | 
			
		||||
        ImageChooserPanel('logo'),
 | 
			
		||||
        ImageChooserPanel('favicon'),
 | 
			
		||||
@ -84,7 +115,11 @@ class WebsiteSettings(BaseSetting):
 | 
			
		||||
            FieldPanel('comment_success_message'),
 | 
			
		||||
            FieldPanel('comment_wait_message'),
 | 
			
		||||
            FieldPanel('comment_error_message'),
 | 
			
		||||
        ], heading = _('Comments'))
 | 
			
		||||
        ], heading = _('Comments')),
 | 
			
		||||
        MultiFieldPanel([
 | 
			
		||||
            FieldPanel('auto_create'),
 | 
			
		||||
            FieldPanel('default_program_parent_page'),
 | 
			
		||||
        ], heading = _('Programs and controls')),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
@ -373,11 +408,6 @@ class DiffusionPage(Publication):
 | 
			
		||||
        related_name = 'page',
 | 
			
		||||
        on_delete=models.SET_NULL,
 | 
			
		||||
        null=True,
 | 
			
		||||
        limit_choices_to = lambda: {
 | 
			
		||||
            'initial__isnull': True,
 | 
			
		||||
            'start__gt': tz.now() - tz.timedelta(days=10),
 | 
			
		||||
            'start__lt': tz.now() + tz.timedelta(days=10),
 | 
			
		||||
        }
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
@ -390,30 +420,39 @@ class DiffusionPage(Publication):
 | 
			
		||||
        InlinePanel('tracks', label=_('Tracks'))
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def from_diffusion(cl, diff, model = None, **kwargs):
 | 
			
		||||
        model = model or cl
 | 
			
		||||
        model_kwargs = {
 | 
			
		||||
            'diffusion': diff,
 | 
			
		||||
            'title': '{}, {}'.format(
 | 
			
		||||
                diff.program.name, tz.localtime(diff.date).strftime('%d %B %Y')
 | 
			
		||||
            ),
 | 
			
		||||
            'cover': (diff.program.page.count() and \
 | 
			
		||||
                        diff.program.page.first().cover) or None,
 | 
			
		||||
            'date': diff.start,
 | 
			
		||||
        }
 | 
			
		||||
        model_kwargs.update(kwargs)
 | 
			
		||||
        r = model(**model_kwargs)
 | 
			
		||||
        return r
 | 
			
		||||
 | 
			
		||||
    @classmethod
 | 
			
		||||
    def as_item(cl, diff):
 | 
			
		||||
        """
 | 
			
		||||
        Return a DiffusionPage or ListItem from a Diffusion
 | 
			
		||||
        Return a DiffusionPage or ListItem from a Diffusion.
 | 
			
		||||
        """
 | 
			
		||||
        if diff.page.all().count():
 | 
			
		||||
            item = diff.page.all().first()
 | 
			
		||||
        else:
 | 
			
		||||
            item = ListItem(
 | 
			
		||||
                title = '{}, {}'.format(
 | 
			
		||||
                    diff.program.name, diff.date.strftime('%d %B %Y')
 | 
			
		||||
                ),
 | 
			
		||||
                cover = (diff.program.page.count() and \
 | 
			
		||||
                            diff.program.page.first().cover) or '',
 | 
			
		||||
                live = True,
 | 
			
		||||
                date = diff.start,
 | 
			
		||||
            )
 | 
			
		||||
            item = cl.from_diffusion(diff, ListItem)
 | 
			
		||||
            item.live = True
 | 
			
		||||
 | 
			
		||||
        if diff.initial:
 | 
			
		||||
            item.info = _('Rerun of %(date)s') % {
 | 
			
		||||
                'date': diff.initial.start.strftime('%A %d')
 | 
			
		||||
            }
 | 
			
		||||
        diff.css_class = 'diffusion'
 | 
			
		||||
 | 
			
		||||
        return item
 | 
			
		||||
 | 
			
		||||
    def save(self, *args, **kwargs):
 | 
			
		||||
 | 
			
		||||
@ -5,6 +5,7 @@ from enum import IntEnum
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.utils.translation import ugettext as _, ugettext_lazy
 | 
			
		||||
from django.utils import timezone as tz
 | 
			
		||||
from django.utils.functional import cached_property
 | 
			
		||||
from django.core.urlresolvers import reverse
 | 
			
		||||
from django.template.loader import render_to_string
 | 
			
		||||
from django.contrib.contenttypes.models import ContentType
 | 
			
		||||
@ -195,7 +196,7 @@ class ListBase(models.Model):
 | 
			
		||||
        reusable by other classes if needed.
 | 
			
		||||
        """
 | 
			
		||||
        from aircox.cms.models import Publication
 | 
			
		||||
        related = self.related and self.related.specific()
 | 
			
		||||
        related = self.related and self.related.specific
 | 
			
		||||
 | 
			
		||||
        # model
 | 
			
		||||
        if self.model:
 | 
			
		||||
@ -487,7 +488,7 @@ class Section(ClusterableModel):
 | 
			
		||||
 | 
			
		||||
    def render(self, request, page = None, *args, **kwargs):
 | 
			
		||||
        return ''.join([
 | 
			
		||||
            place.item.specific().render(request, page, *args, **kwargs)
 | 
			
		||||
            place.item.specific.render(request, page, *args, **kwargs)
 | 
			
		||||
            for place in self.places.all()
 | 
			
		||||
        ])
 | 
			
		||||
 | 
			
		||||
@ -573,6 +574,7 @@ class SectionItem(models.Model,metaclass=SectionItemMeta):
 | 
			
		||||
        ], heading=_('General')),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
    @cached_property
 | 
			
		||||
    def specific(self):
 | 
			
		||||
        """
 | 
			
		||||
        Return a downcasted version of the post if it is from another
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										14
									
								
								notes.md
									
									
									
									
									
								
							
							
						
						
									
										14
									
								
								notes.md
									
									
									
									
									
								
							@ -33,21 +33,11 @@ This file is used as a reminder, can be used as crappy documentation too.
 | 
			
		||||
        - config generation and sound diffusion
 | 
			
		||||
 | 
			
		||||
- cms:
 | 
			
		||||
    - update documentation:
 | 
			
		||||
        - cms.script
 | 
			
		||||
        - cms.exposure; make it right, see nomenclature, + docstring
 | 
			
		||||
        - cms.actions;
 | 
			
		||||
    - admin cms
 | 
			
		||||
        -> sections/actions and django decorator?
 | 
			
		||||
        -> enhance calendar with possible actions?
 | 
			
		||||
 | 
			
		||||
- website:
 | 
			
		||||
    - article list with the focus
 | 
			
		||||
    - command to sync programs with cms's
 | 
			
		||||
    - player:
 | 
			
		||||
        - mixcloud
 | 
			
		||||
        - remove from playing playlist -> stop
 | 
			
		||||
    - date_by_list:
 | 
			
		||||
        - sections' url
 | 
			
		||||
    - filter choices on DiffusionPage and ProgramPage related objects
 | 
			
		||||
 | 
			
		||||
# Long term TODO
 | 
			
		||||
- automatic cancel of passed diffusion based on logs?
 | 
			
		||||
 | 
			
		||||
@ -510,11 +510,6 @@ class Program(Nameable):
 | 
			
		||||
        default = True,
 | 
			
		||||
        help_text = _('if not set this program is no longer active')
 | 
			
		||||
    )
 | 
			
		||||
    public = models.BooleanField(
 | 
			
		||||
        _('public'),
 | 
			
		||||
        default = True,
 | 
			
		||||
        help_text = _('information are available to the public')
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def path(self):
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user