new management tool: add
This commit is contained in:
		@ -81,6 +81,7 @@ class SoundFileAdmin (MetadataAdmin):
 | 
				
			|||||||
    ]
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    #inlines = [ EpisodeInline ]
 | 
					    #inlines = [ EpisodeInline ]
 | 
				
			||||||
 | 
					    inlines = [ EventInline ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -93,16 +94,15 @@ class ArticleAdmin (PublicationAdmin):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class ProgramAdmin (PublicationAdmin):
 | 
					class ProgramAdmin (PublicationAdmin):
 | 
				
			||||||
    fieldsets           = copy.deepcopy(PublicationAdmin.fieldsets)
 | 
					    fieldsets           = copy.deepcopy(PublicationAdmin.fieldsets)
 | 
				
			||||||
    prepopulated_fields = { 'tag': ('title',) }
 | 
					 | 
				
			||||||
    inlines             = [ EpisodeInline, ScheduleInline ]
 | 
					    inlines             = [ EpisodeInline, ScheduleInline ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fieldsets[1][1]['fields'] += ['email', 'url', 'tag']
 | 
					    fieldsets[1][1]['fields'] += ['email', 'url', 'non_stop']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class EpisodeAdmin (PublicationAdmin):
 | 
					class EpisodeAdmin (PublicationAdmin):
 | 
				
			||||||
    fieldsets           = copy.deepcopy(PublicationAdmin.fieldsets)
 | 
					    fieldsets           = copy.deepcopy(PublicationAdmin.fieldsets)
 | 
				
			||||||
    inlines             = [ EventInline, SoundFileInline ]
 | 
					    inlines             = [ SoundFileInline ]
 | 
				
			||||||
    list_filter         = ['parent'] + PublicationAdmin.list_filter
 | 
					    list_filter         = ['parent'] + PublicationAdmin.list_filter
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fieldsets[0][1]['fields'] += ['tracks']
 | 
					    fieldsets[0][1]['fields'] += ['tracks']
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										143
									
								
								programs/management/commands/add.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										143
									
								
								programs/management/commands/add.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,143 @@
 | 
				
			|||||||
 | 
					from django.core.management.base    import BaseCommand, CommandError
 | 
				
			||||||
 | 
					from django.utils                   import timezone
 | 
				
			||||||
 | 
					import programs.models              as models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Model:
 | 
				
			||||||
 | 
					    # dict: key is the argument name, value is the constructor
 | 
				
			||||||
 | 
					    required = {}
 | 
				
			||||||
 | 
					    optional = {}
 | 
				
			||||||
 | 
					    model = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__ (self, model, required = {}, optional = {}, post = None):
 | 
				
			||||||
 | 
					        self.model = model
 | 
				
			||||||
 | 
					        self.required = required
 | 
				
			||||||
 | 
					        self.optional = optional
 | 
				
			||||||
 | 
					        self.post = post
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def check_or_raise (self, options):
 | 
				
			||||||
 | 
					        for req in self.required:
 | 
				
			||||||
 | 
					            if req not in options:
 | 
				
			||||||
 | 
					                raise ValueError('required argument ' + req + ' is missing')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_kargs (self, options):
 | 
				
			||||||
 | 
					        kargs = {}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for i in self.required:
 | 
				
			||||||
 | 
					            if options.get(i):
 | 
				
			||||||
 | 
					                fn = self.required[i]
 | 
				
			||||||
 | 
					                kargs[i] = fn(options[i])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        for i in self.optional:
 | 
				
			||||||
 | 
					            if options.get(i):
 | 
				
			||||||
 | 
					                print(i, options)
 | 
				
			||||||
 | 
					                fn = self.optional[i]
 | 
				
			||||||
 | 
					                kargs[i] = fn(options[i])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return kargs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def make (self, options):
 | 
				
			||||||
 | 
					        self.check_or_raise(options)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        kargs    = self.get_kargs(options)
 | 
				
			||||||
 | 
					        instance = self.model(**kargs)
 | 
				
			||||||
 | 
					        instance.save()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.post:
 | 
				
			||||||
 | 
					            self.post(instance, options)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        print(instance.__dict__)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def DateTime (string):
 | 
				
			||||||
 | 
					    dt = timezone.datetime.strptime(string, '%Y-%m-%d %H:%M:%S')
 | 
				
			||||||
 | 
					    return timezone.make_aware(dt, timezone.get_current_timezone())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def Time (string):
 | 
				
			||||||
 | 
					    dt = timezone.datetime.strptime(string, '%H:%M')
 | 
				
			||||||
 | 
					    return timezone.datetime.time(dt)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					def AddTags (instance, options):
 | 
				
			||||||
 | 
					    if options.get('tags'):
 | 
				
			||||||
 | 
					        instance.tags.add(*options['tags'])
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					models = {
 | 
				
			||||||
 | 
					    'program': Model( models.Program
 | 
				
			||||||
 | 
					                    , { 'title': str }
 | 
				
			||||||
 | 
					                    , { 'subtitle': str, 'can_comment': bool, 'date': DateTime
 | 
				
			||||||
 | 
					                      , 'parent_id': int, 'public': bool
 | 
				
			||||||
 | 
					                      , 'url': str, 'email': str, 'non_stop': bool
 | 
				
			||||||
 | 
					                      }
 | 
				
			||||||
 | 
					                    , AddTags
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					  , 'article': Model( models.Article
 | 
				
			||||||
 | 
					                    , { 'title': str }
 | 
				
			||||||
 | 
					                    , { 'subtitle': str, 'can_comment': bool, 'date': DateTime
 | 
				
			||||||
 | 
					                      , 'parent_id': int, 'public': bool
 | 
				
			||||||
 | 
					                      , 'static_page': bool, 'focus': bool
 | 
				
			||||||
 | 
					                      }
 | 
				
			||||||
 | 
					                    , AddTags
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					  , 'schedule': Model( models.Schedule
 | 
				
			||||||
 | 
					                    , { 'parent_id': int, 'date': DateTime, 'duration': Time
 | 
				
			||||||
 | 
					                      , 'frequency': int }
 | 
				
			||||||
 | 
					                    , { 'rerun': bool }
 | 
				
			||||||
 | 
					                    )
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Command (BaseCommand):
 | 
				
			||||||
 | 
					    help="Add an element of the given model"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def add_arguments (self, parser):
 | 
				
			||||||
 | 
					        parser.add_argument( 'model', type=str
 | 
				
			||||||
 | 
					                           , metavar="MODEL"
 | 
				
			||||||
 | 
					                           , help="model to add. It must be in [schedule,program,article]")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # publication/generic
 | 
				
			||||||
 | 
					        parser.add_argument('--parent_id',  type=str)
 | 
				
			||||||
 | 
					        parser.add_argument('--title',      type=str)
 | 
				
			||||||
 | 
					        parser.add_argument('--subtitle',   type=str)
 | 
				
			||||||
 | 
					        parser.add_argument('--can_comment',action='store_true')
 | 
				
			||||||
 | 
					        parser.add_argument('--public',     action='store_true')
 | 
				
			||||||
 | 
					        parser.add_argument( '--date',      type=str
 | 
				
			||||||
 | 
					                           , help='a valid date time (Y/m/d H:m:s')
 | 
				
			||||||
 | 
					        parser.add_argument('--tags',       type=str, nargs='+')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # program
 | 
				
			||||||
 | 
					        parser.add_argument('--url',        type=str)
 | 
				
			||||||
 | 
					        parser.add_argument('--email',      type=str)
 | 
				
			||||||
 | 
					        parser.add_argument('--non_stop',   type=int)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # article
 | 
				
			||||||
 | 
					        parser.add_argument('--static_page',action='store_true')
 | 
				
			||||||
 | 
					        parser.add_argument('--focus',      action='store_true')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # schedule
 | 
				
			||||||
 | 
					        parser.add_argument('--duration',   type=str)
 | 
				
			||||||
 | 
					        parser.add_argument('--frequency',  type=int)
 | 
				
			||||||
 | 
					        parser.add_argument('--rerun',      action='store_true')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def handle (self, *args, **options):
 | 
				
			||||||
 | 
					        model = options.get('model')
 | 
				
			||||||
 | 
					        if not model:
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        model = model.lower()
 | 
				
			||||||
 | 
					        if model not in models:
 | 
				
			||||||
 | 
					            raise ValueError("model {} is not supported".format(str(model)))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        models[model].make(options)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1,32 +0,0 @@
 | 
				
			|||||||
import os
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.core.management.base           import BaseCommand, CommandError
 | 
					 | 
				
			||||||
import programs.models                     as models
 | 
					 | 
				
			||||||
import programs.settings
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Command (BaseCommand):
 | 
					 | 
				
			||||||
    help= "Take a look at the programs directory to check on new podcasts"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def handle (self, *args, **options):
 | 
					 | 
				
			||||||
        programs = models.Program.objects.filter(schedule__isnull = True)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for program in programs:
 | 
					 | 
				
			||||||
            self.scan(program, program.path + '/public', public = True)
 | 
					 | 
				
			||||||
            self.scan(program, program.path + '/podcasts', embed = True)
 | 
					 | 
				
			||||||
            self.scan(program, program.path + '/private')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def scan (self, program, path, public = False, embed = False):
 | 
					 | 
				
			||||||
        try:
 | 
					 | 
				
			||||||
            for filename in os.listdir(path):
 | 
					 | 
				
			||||||
                long_filename = path + '/' + filename
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                # check for new sound files
 | 
					 | 
				
			||||||
                # stat the sound files
 | 
					 | 
				
			||||||
                # match sound files against episodes - if not found, create it
 | 
					 | 
				
			||||||
                # upload public podcasts to mixcloud if required
 | 
					 | 
				
			||||||
        except:
 | 
					 | 
				
			||||||
            pass
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@ -1,72 +0,0 @@
 | 
				
			|||||||
import datetime
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.core.management.base            import BaseCommand, CommandError
 | 
					 | 
				
			||||||
from django.utils                           import timezone, dateformat
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
import programs.models                      as models
 | 
					 | 
				
			||||||
import programs.settings
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Diffusion:
 | 
					 | 
				
			||||||
    ref = None
 | 
					 | 
				
			||||||
    date_start = None
 | 
					 | 
				
			||||||
    date_end = None
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __init__ (self, ref, date_start, date_end):
 | 
					 | 
				
			||||||
        self.ref = ref
 | 
					 | 
				
			||||||
        self.date_start = date_start
 | 
					 | 
				
			||||||
        self.date_end = date_end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def __lt__ (self, d):
 | 
					 | 
				
			||||||
        return self.date_start < d.date_start and \
 | 
					 | 
				
			||||||
               self.date_end < d.date_end
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Command (BaseCommand):
 | 
					 | 
				
			||||||
    help= "check sounds to diffuse"
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    diffusions = set()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def handle(self, *args, **options):
 | 
					 | 
				
			||||||
        self.get_next_events()
 | 
					 | 
				
			||||||
        self.get_next_episodes()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        for diffusion in self.diffusions:
 | 
					 | 
				
			||||||
            print( diffusion.ref.__str__()
 | 
					 | 
				
			||||||
                 , diffusion.date_start
 | 
					 | 
				
			||||||
                 , diffusion.date_end)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_next_episodes (self):
 | 
					 | 
				
			||||||
        schedules = models.Schedule.objects.filter()
 | 
					 | 
				
			||||||
        for schedule in schedules:
 | 
					 | 
				
			||||||
            date = schedule.next_date()
 | 
					 | 
				
			||||||
            if not date:
 | 
					 | 
				
			||||||
                continue
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            dt = datetime.timedelta( hours = schedule.duration.hour
 | 
					 | 
				
			||||||
                                   , minutes = schedule.duration.minute
 | 
					 | 
				
			||||||
                                   , seconds = schedule.duration.second )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            ref = models.Episode.objects.filter(date = date)[:1]
 | 
					 | 
				
			||||||
            if not ref:
 | 
					 | 
				
			||||||
                ref = ( schedule.parent, )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            diffusion = Diffusion(ref[0], date, date + dt)
 | 
					 | 
				
			||||||
            self.diffusions.add(diffusion)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_next_events (self):
 | 
					 | 
				
			||||||
        events = models.Event.objects.filter(date_end__gt = timezone.now(),
 | 
					 | 
				
			||||||
                                             canceled = False) \
 | 
					 | 
				
			||||||
                                     .extra(order_by = ['date'])[:10]
 | 
					 | 
				
			||||||
        for event in events:
 | 
					 | 
				
			||||||
            diffusion = Diffusion(event, event.date, event.date_end)
 | 
					 | 
				
			||||||
            self.diffusions.add(diffusion)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@ -1,45 +1,38 @@
 | 
				
			|||||||
import datetime
 | 
					import os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# django
 | 
					# django
 | 
				
			||||||
from django.db                              import models
 | 
					from django.db                              import models
 | 
				
			||||||
from django.contrib.auth.models             import User
 | 
					from django.contrib.auth.models             import User
 | 
				
			||||||
from django.template.defaultfilters         import slugify
 | 
					from django.template.defaultfilters         import slugify
 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.http                            import HttpResponse, Http404
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.contrib.contenttypes.fields     import GenericForeignKey
 | 
					from django.contrib.contenttypes.fields     import GenericForeignKey
 | 
				
			||||||
from django.contrib.contenttypes.models     import ContentType
 | 
					from django.contrib.contenttypes.models     import ContentType
 | 
				
			||||||
 | 
					 | 
				
			||||||
from django.shortcuts                       import get_object_or_404
 | 
					 | 
				
			||||||
from django.utils.translation               import ugettext as _, ugettext_lazy
 | 
					from django.utils.translation               import ugettext as _, ugettext_lazy
 | 
				
			||||||
from django.utils                           import timezone
 | 
					from django.utils                           import timezone
 | 
				
			||||||
from django.utils.html                      import strip_tags
 | 
					from django.utils.html                      import strip_tags
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.conf                            import settings
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
# extensions
 | 
					# extensions
 | 
				
			||||||
from taggit.managers                        import TaggableManager
 | 
					from taggit.managers                        import TaggableManager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
import programs.settings                    as settings
 | 
					import programs.settings                    as settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
AFrequency = {
 | 
					Frequency = {
 | 
				
			||||||
    'ponctual':          0x000000,
 | 
					    'ponctual':          0b000000
 | 
				
			||||||
    'every week':        0b001111,
 | 
					  , 'every week':        0b001111
 | 
				
			||||||
    'first week':        0x000001,
 | 
					  , 'first week':        0b000001
 | 
				
			||||||
    'second week':       0x000010,
 | 
					  , 'second week':       0b000010
 | 
				
			||||||
    'third week':        0x000100,
 | 
					  , 'third week':        0b000100
 | 
				
			||||||
    'fourth week':       0x001000,
 | 
					  , 'fourth week':       0b001000
 | 
				
			||||||
    'first and third':   0x000101,
 | 
					  , 'first and third':   0b000101
 | 
				
			||||||
    'second and fourth': 0x001010,
 | 
					  , 'second and fourth': 0b001010
 | 
				
			||||||
    'one week on two':   0x010010,
 | 
					  , 'one week on two':   0b010010
 | 
				
			||||||
    #'uneven week':         0x100000,
 | 
					    #'uneven week':         0b100000
 | 
				
			||||||
    # TODO 'every day':     0x110000
 | 
					    # TODO 'every day':     0b110000
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# Translators: html safe values
 | 
					# Translators: html safe values
 | 
				
			||||||
ugettext_lazy('ponctual')
 | 
					ugettext_lazy('ponctual')
 | 
				
			||||||
ugettext_lazy('every week')
 | 
					ugettext_lazy('every week')
 | 
				
			||||||
@ -52,10 +45,15 @@ ugettext_lazy('second and fourth')
 | 
				
			|||||||
ugettext_lazy('one week on two')
 | 
					ugettext_lazy('one week on two')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Frequency = [ (y, x) for x,y in AFrequency.items() ]
 | 
					EventType = {
 | 
				
			||||||
RFrequency = { y: x for x,y in AFrequency.items() }
 | 
					    'play':     0x02    # the sound is playing / planified to play
 | 
				
			||||||
 | 
					  , 'cancel':   0x03    # the sound has been canceled from grid; useful to give
 | 
				
			||||||
 | 
					                        # the info to the users
 | 
				
			||||||
 | 
					  , 'stop':     0x04    # the sound has been arbitrary stopped (non-stop or not)
 | 
				
			||||||
 | 
					  , 'non-stop': 0x05    # the sound has been played as non-stop
 | 
				
			||||||
 | 
					#, 'streaming'
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Frequency.sort(key = lambda e: e[0])
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Model (models.Model):
 | 
					class Model (models.Model):
 | 
				
			||||||
@ -106,7 +104,7 @@ class Metadata (Model):
 | 
				
			|||||||
                  )
 | 
					                  )
 | 
				
			||||||
    date        = models.DateTimeField(
 | 
					    date        = models.DateTimeField(
 | 
				
			||||||
                      _('date')
 | 
					                      _('date')
 | 
				
			||||||
                    , default = datetime.datetime.now
 | 
					                    , default = timezone.datetime.now
 | 
				
			||||||
                  )
 | 
					                  )
 | 
				
			||||||
    public      = models.BooleanField(
 | 
					    public      = models.BooleanField(
 | 
				
			||||||
                      _('public')
 | 
					                      _('public')
 | 
				
			||||||
@ -190,12 +188,6 @@ class Publication (Metadata):
 | 
				
			|||||||
    #
 | 
					    #
 | 
				
			||||||
    # Instance's methods
 | 
					    # Instance's methods
 | 
				
			||||||
    #
 | 
					    #
 | 
				
			||||||
    def get_parent (self, raise_404 = False ):
 | 
					 | 
				
			||||||
        if not parent and raise_404:
 | 
					 | 
				
			||||||
            raise Http404
 | 
					 | 
				
			||||||
        return parent
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_parents ( self, order_by = "desc", include_fields = None ):
 | 
					    def get_parents ( self, order_by = "desc", include_fields = None ):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Return an array of the parents of the item.
 | 
					        Return an array of the parents of the item.
 | 
				
			||||||
@ -262,10 +254,9 @@ class SoundFile (Metadata):
 | 
				
			|||||||
                    , blank = True
 | 
					                    , blank = True
 | 
				
			||||||
                    , null = True
 | 
					                    , null = True
 | 
				
			||||||
                  )
 | 
					                  )
 | 
				
			||||||
    file        = models.FileField(
 | 
					    file        = models.FileField( #FIXME: filefield
 | 
				
			||||||
                      _('file')
 | 
					                      _('file')
 | 
				
			||||||
                    , upload_to = "data/tracks"
 | 
					                    , upload_to = lambda i, f: SoundFile.__upload_path(i,f)
 | 
				
			||||||
                    , blank = True
 | 
					 | 
				
			||||||
                  )
 | 
					                  )
 | 
				
			||||||
    duration    = models.TimeField(
 | 
					    duration    = models.TimeField(
 | 
				
			||||||
                      _('duration')
 | 
					                      _('duration')
 | 
				
			||||||
@ -277,12 +268,24 @@ class SoundFile (Metadata):
 | 
				
			|||||||
                    , default = False
 | 
					                    , default = False
 | 
				
			||||||
                    , help_text = _("the file has been cut")
 | 
					                    , help_text = _("the file has been cut")
 | 
				
			||||||
                  )
 | 
					                  )
 | 
				
			||||||
    embed       = models.TextField (
 | 
					    embed       = models.TextField(
 | 
				
			||||||
                      _('embed HTML code from external website')
 | 
					                      _('embed HTML code from external website')
 | 
				
			||||||
                    , blank = True
 | 
					                    , blank = True
 | 
				
			||||||
                    , null = True
 | 
					                    , null = True
 | 
				
			||||||
                    , help_text = _('if set, consider the sound podcastable from there')
 | 
					                    , help_text = _('if set, consider the sound podcastable from there')
 | 
				
			||||||
                  )
 | 
					                  )
 | 
				
			||||||
 | 
					    removed     = models.BooleanField(
 | 
				
			||||||
 | 
					                      default = False
 | 
				
			||||||
 | 
					                    , help_text = _('this sound has been removed from filesystem')
 | 
				
			||||||
 | 
					                  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __upload_path (self, filename):
 | 
				
			||||||
 | 
					        if self.parent and self.parent.parent:
 | 
				
			||||||
 | 
					            path = self.parent.parent.path
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            path = settings.AIRCOX_SOUNDFILE_DEFAULT_DIR
 | 
				
			||||||
 | 
					        return os.path.join(path, filename)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__ (self):
 | 
					    def __str__ (self):
 | 
				
			||||||
@ -294,29 +297,44 @@ class SoundFile (Metadata):
 | 
				
			|||||||
        verbose_name_plural = _('Sounds')
 | 
					        verbose_name_plural = _('Sounds')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
class Schedule (Model):
 | 
					class Schedule (Model):
 | 
				
			||||||
    parent      = models.ForeignKey( 'Program', blank = True, null = True )
 | 
					    parent      = models.ForeignKey( 'Program', blank = True, null = True )
 | 
				
			||||||
    date        = models.DateTimeField(_('start'))
 | 
					    date        = models.DateTimeField(_('start'))
 | 
				
			||||||
    duration    = models.TimeField(_('duration'))
 | 
					    duration    = models.TimeField(
 | 
				
			||||||
    frequency   = models.SmallIntegerField(_('frequency'), choices = Frequency)
 | 
					                      _('duration')
 | 
				
			||||||
 | 
					                    , blank = True
 | 
				
			||||||
 | 
					                    , null = True
 | 
				
			||||||
 | 
					                  )
 | 
				
			||||||
 | 
					    frequency   = models.SmallIntegerField(
 | 
				
			||||||
 | 
					                      _('frequency')
 | 
				
			||||||
 | 
					                    , choices = [ (y, x) for x,y in Frequency.items() ]
 | 
				
			||||||
 | 
					                  )
 | 
				
			||||||
    rerun       = models.BooleanField(_('rerun'), default = False)
 | 
					    rerun       = models.BooleanField(_('rerun'), default = False)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def match_week (self, at = datetime.date.today()):
 | 
					    def match_date (self, at = timezone.datetime.today()):
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Return True if the given datetime matches the schedule
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        if self.date.weekday() == at.weekday() and self.match_week(date):
 | 
				
			||||||
 | 
					            return self.date.time() == at.date.time()
 | 
				
			||||||
 | 
					        return False
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def match_week (self, at = timezone.datetime.today()):
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        Return True if the given week number matches the schedule, False
 | 
					        Return True if the given week number matches the schedule, False
 | 
				
			||||||
        otherwise.
 | 
					        otherwise.
 | 
				
			||||||
        If the schedule is ponctual, return None.
 | 
					        If the schedule is ponctual, return None.
 | 
				
			||||||
        """
 | 
					        """
 | 
				
			||||||
        if self.frequency == AFrequency['ponctual']:
 | 
					        if self.frequency == Frequency['ponctual']:
 | 
				
			||||||
            return None
 | 
					            return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if self.frequency == AFrequency['one week on two']:
 | 
					        if self.frequency == Frequency['one week on two']:
 | 
				
			||||||
            week = at.isocalendar()[1]
 | 
					            week = at.isocalendar()[1]
 | 
				
			||||||
            return (week % 2) == (self.date.isocalendar()[1] % 2)
 | 
					            return (week % 2) == (self.date.isocalendar()[1] % 2)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        first_of_month = datetime.date(at.year, at.month, 1)
 | 
					        first_of_month = timezone.datetime.date(at.year, at.month, 1)
 | 
				
			||||||
        week = at.isocalendar()[1] - first_of_month.isocalendar()[1]
 | 
					        week = at.isocalendar()[1] - first_of_month.isocalendar()[1]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # weeks of month
 | 
					        # weeks of month
 | 
				
			||||||
@ -326,30 +344,29 @@ class Schedule (Model):
 | 
				
			|||||||
        return (self.frequency & (0b0001 << week) > 0)
 | 
					        return (self.frequency & (0b0001 << week) > 0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def next_date (self, at = timezone.datetime.today()):
 | 
				
			||||||
    def next_date (self, at = datetime.date.today()):
 | 
					        if self.frequency == Frequency['ponctual']:
 | 
				
			||||||
        if self.frequency == AFrequency['ponctual']:
 | 
					 | 
				
			||||||
            return None
 | 
					            return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # first day of the week
 | 
					        # first day of the week
 | 
				
			||||||
        date = at - datetime.timedelta( days = at.weekday() )
 | 
					        date = at - timezone.timedelta( days = at.weekday() )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        # for the next five week, we look for a matching week.
 | 
					        # for the next five week, we look for a matching week.
 | 
				
			||||||
        # when found, add the number of day since de start of the
 | 
					        # when found, add the number of day since de start of the
 | 
				
			||||||
        # we need to test if the result is >= at
 | 
					        # we need to test if the result is >= at
 | 
				
			||||||
        for i in range(0,5):
 | 
					        for i in range(0,5):
 | 
				
			||||||
            if self.match_week(date):
 | 
					            if self.match_week(date):
 | 
				
			||||||
                date_ = date + datetime.timedelta( days = self.date.weekday() )
 | 
					                date_ = date + timezone.timedelta( days = self.date.weekday() )
 | 
				
			||||||
                if date_ >= at:
 | 
					                if date_ >= at:
 | 
				
			||||||
                    # we don't want past events
 | 
					                    # we don't want past events
 | 
				
			||||||
                    return datetime.datetime(date_.year, date_.month, date_.day,
 | 
					                    return timezone.datetime(date_.year, date_.month, date_.day,
 | 
				
			||||||
                                                self.date.hour, self.date.minute)
 | 
					                                                self.date.hour, self.date.minute)
 | 
				
			||||||
            date += datetime.timedelta( days = 7 )
 | 
					            date += timezone.timedelta( days = 7 )
 | 
				
			||||||
        else:
 | 
					        else:
 | 
				
			||||||
            return None
 | 
					            return None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def next_dates (self, at = datetime.date.today(), n = 52):
 | 
					    def next_dates (self, at = timezone.datetime.today(), n = 52):
 | 
				
			||||||
        # we could have optimized this function, but since it should not
 | 
					        # we could have optimized this function, but since it should not
 | 
				
			||||||
        # be use too often, we keep a more readable and easier to debug
 | 
					        # be use too often, we keep a more readable and easier to debug
 | 
				
			||||||
        # solution
 | 
					        # solution
 | 
				
			||||||
@ -362,20 +379,20 @@ class Schedule (Model):
 | 
				
			|||||||
            if not e:
 | 
					            if not e:
 | 
				
			||||||
                break
 | 
					                break
 | 
				
			||||||
            res.append(e)
 | 
					            res.append(e)
 | 
				
			||||||
            at = res[-1] + datetime.timedelta(days = 1)
 | 
					            at = res[-1] + timezone.timedelta(days = 1)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        return res
 | 
					        return res
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def to_string(self):
 | 
					    #def to_string(self):
 | 
				
			||||||
        s = ugettext_lazy( RFrequency[self.frequency] )
 | 
					    #    s = ugettext_lazy( RFrequency[self.frequency] )
 | 
				
			||||||
        if self.rerun:
 | 
					    #    if self.rerun:
 | 
				
			||||||
            return s + ' (' + _('rerun') + ')'
 | 
					    #        return s + ' (' + _('rerun') + ')'
 | 
				
			||||||
        return s
 | 
					    #    return s
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__ (self):
 | 
					    def __str__ (self):
 | 
				
			||||||
        return self.parent.title + ': ' + RFrequency[self.frequency]
 | 
					        frequency = [ x for x,y in Frequency.items() if y == self.frequency ]
 | 
				
			||||||
 | 
					        return self.parent.title + ': ' + frequency[0]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
@ -427,15 +444,17 @@ class Program (Publication):
 | 
				
			|||||||
                    , blank = True
 | 
					                    , blank = True
 | 
				
			||||||
                    , null = True
 | 
					                    , null = True
 | 
				
			||||||
                  )
 | 
					                  )
 | 
				
			||||||
    tag         = models.CharField(
 | 
					    non_stop    = models.SmallIntegerField(
 | 
				
			||||||
                      _('tag')
 | 
					                      _('non-stop priority')
 | 
				
			||||||
                    , max_length = 64
 | 
					                    , help_text = _('this program can be used as non-stop')
 | 
				
			||||||
                    , help_text = _('used in articles to refer to it')
 | 
					                    , default = -1
 | 
				
			||||||
                  )
 | 
					                  )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    @property
 | 
					    @property
 | 
				
			||||||
    def path(self):
 | 
					    def path(self):
 | 
				
			||||||
        return settings.AIRCOX_PROGRAMS_DATA + slugify(self.title + '_' + self.id)
 | 
					        return os.path.join( settings.AIRCOX_PROGRAMS_DIR
 | 
				
			||||||
 | 
					                           , slugify(self.title + '_' + str(self.id))
 | 
				
			||||||
 | 
					                           )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
@ -455,8 +474,6 @@ class Episode (Publication):
 | 
				
			|||||||
    parent      = models.ForeignKey(
 | 
					    parent      = models.ForeignKey(
 | 
				
			||||||
                      Program
 | 
					                      Program
 | 
				
			||||||
                    , verbose_name = _('parent')
 | 
					                    , verbose_name = _('parent')
 | 
				
			||||||
                    , blank = True
 | 
					 | 
				
			||||||
                    , null = True
 | 
					 | 
				
			||||||
                  )
 | 
					                  )
 | 
				
			||||||
    tracks      = models.ManyToManyField(
 | 
					    tracks      = models.ManyToManyField(
 | 
				
			||||||
                      Track
 | 
					                      Track
 | 
				
			||||||
@ -473,37 +490,22 @@ class Episode (Publication):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class Event (Model):
 | 
					class Event (Model):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
 | 
					    Event logs and planification of a sound file
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    parent      = models.ForeignKey (
 | 
					    sound       = models.ForeignKey (
 | 
				
			||||||
                      Episode
 | 
					                      SoundFile
 | 
				
			||||||
                    , verbose_name = _('episode')
 | 
					                    , verbose_name = _('sound file')
 | 
				
			||||||
                    , blank = True
 | 
					 | 
				
			||||||
                    , null = True
 | 
					 | 
				
			||||||
                  )
 | 
					                  )
 | 
				
			||||||
    date        = models.DateTimeField( _('date of start') )
 | 
					    type        = models.SmallIntegerField(
 | 
				
			||||||
    date_end    = models.DateTimeField(
 | 
					                      _('type')
 | 
				
			||||||
                      _('date of end')
 | 
					                    , choices = [ (y, x) for x,y in EventType.items() ]
 | 
				
			||||||
                    , blank = True
 | 
					 | 
				
			||||||
                    , null = True
 | 
					 | 
				
			||||||
                  )
 | 
					 | 
				
			||||||
    public      = models.BooleanField(
 | 
					 | 
				
			||||||
                      _('public')
 | 
					 | 
				
			||||||
                    , default = False
 | 
					 | 
				
			||||||
                    , help_text = _('publication is accessible to the public')
 | 
					 | 
				
			||||||
                  )
 | 
					                  )
 | 
				
			||||||
 | 
					    date        = models.DateTimeField( _('date of event start') )
 | 
				
			||||||
    meta        = models.TextField (
 | 
					    meta        = models.TextField (
 | 
				
			||||||
                      _('meta')
 | 
					                      _('meta')
 | 
				
			||||||
                    , blank = True
 | 
					                    , blank = True
 | 
				
			||||||
                    , null = True
 | 
					                    , null = True
 | 
				
			||||||
                  )
 | 
					                  )
 | 
				
			||||||
    canceled    = models.BooleanField( _('canceled'), default = False )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def testify (self):
 | 
					 | 
				
			||||||
        parent = self.parent
 | 
					 | 
				
			||||||
        self.parent.testify()
 | 
					 | 
				
			||||||
        self.parent.date = self.date
 | 
					 | 
				
			||||||
        return self.parent
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
 | 
				
			|||||||
@ -1,10 +1,15 @@
 | 
				
			|||||||
 | 
					import os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
from django.conf import settings
 | 
					from django.conf import settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
def ensure (key, default):
 | 
					def ensure (key, default):
 | 
				
			||||||
    globals()[key] = getattr(settings, key, default)
 | 
					    globals()[key] = getattr(settings, key, default)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
ensure('AIRCOX_PROGRAMS_DATA', settings.MEDIA_ROOT + '/programs')
 | 
					ensure('AIRCOX_PROGRAMS_DIR',
 | 
				
			||||||
 | 
					       os.path.join(settings.MEDIA_ROOT, 'programs'))
 | 
				
			||||||
 | 
					ensure('AIRCOX_SOUNDFILE_DEFAULT_DIR',
 | 
				
			||||||
 | 
					       os.path.join(AIRCOX_PROGRAMS_DIR + 'default'))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -1,2 +1,76 @@
 | 
				
			|||||||
from django.shortcuts           import render
 | 
					from django.shortcuts               import render
 | 
				
			||||||
 | 
					from django.core.serializers.json   import DjangoJSONEncoder
 | 
				
			||||||
 | 
					from django.utils                   import timezone, dateformat
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					import programs.models              as models
 | 
				
			||||||
 | 
					import programs.settings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class EventList:
 | 
				
			||||||
 | 
					    type  = None
 | 
				
			||||||
 | 
					    next  = None
 | 
				
			||||||
 | 
					    prev  = None
 | 
				
			||||||
 | 
					    at    = None
 | 
				
			||||||
 | 
					    count = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__ (self, **kwargs):
 | 
				
			||||||
 | 
					        self.__dict__ = kwargs
 | 
				
			||||||
 | 
					        if kwargs:
 | 
				
			||||||
 | 
					            self.get_queryset()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_queryset (self):
 | 
				
			||||||
 | 
					        events = models.Event.objects;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        if self.next:   events = events.filter( date_end__ge = timezone.now() )
 | 
				
			||||||
 | 
					        elif self.prev: events = events.filter( date_end__le = timezone.now() )
 | 
				
			||||||
 | 
					        else:           events = events.all()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        events = events.extra(order_by = ['date'])
 | 
				
			||||||
 | 
					        if self.at:     events = events[self.at:]
 | 
				
			||||||
 | 
					        if self.count:  events = events[:self.count]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.events = events
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def raw_string():
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        Return a string with events rendered as raw
 | 
				
			||||||
 | 
					        """
 | 
				
			||||||
 | 
					        res = []
 | 
				
			||||||
 | 
					        for event in events:
 | 
				
			||||||
 | 
					            r = [ dateformat.format(event.date, "Y/m/d H:i:s")
 | 
				
			||||||
 | 
					                , str(event.type)
 | 
				
			||||||
 | 
					                , event.parent.file.path
 | 
				
			||||||
 | 
					                , event.parent.file.url
 | 
				
			||||||
 | 
					                ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            res.push(' '.join(r))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return '\n'.join(res)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def json_string():
 | 
				
			||||||
 | 
					        import json
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        res = []
 | 
				
			||||||
 | 
					        for event in events:
 | 
				
			||||||
 | 
					            r = {
 | 
				
			||||||
 | 
					                  'date': dateformat.format(event.date, "Y/m/d H:i:s")
 | 
				
			||||||
 | 
					                , 'date_end': dateformat.format(event.date_end, "Y/m/d H:i:s")
 | 
				
			||||||
 | 
					                , 'type': str(event.type)
 | 
				
			||||||
 | 
					                , 'file_path': event.parent.file.path
 | 
				
			||||||
 | 
					                , 'file_url': event.parent.file.url
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            res.push(json.dumps(r))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return '\n'.join(res)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user