forked from rc/aircox
		
	add website app, move articles to it, fix programs.models
This commit is contained in:
		
							
								
								
									
										49
									
								
								programs/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										49
									
								
								programs/README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,49 @@
 | 
				
			|||||||
 | 
					This application defines all base classes for the aircox platform. This includes:
 | 
				
			||||||
 | 
					* **Metadata**:         generic class that contains metadata
 | 
				
			||||||
 | 
					* **Publication**:      generic class for models that can be publicated
 | 
				
			||||||
 | 
					* **Track**:            informations on a track in a playlist
 | 
				
			||||||
 | 
					* **SoundFile**:        informations on a sound (podcast)
 | 
				
			||||||
 | 
					* **Schedule**:         schedule informations for programs
 | 
				
			||||||
 | 
					* **Article**:          simple article
 | 
				
			||||||
 | 
					* **Program**:          radio program
 | 
				
			||||||
 | 
					* **Episode**:          occurence of a radio program
 | 
				
			||||||
 | 
					* **Event**:            log info on what has been or what should be played
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Program
 | 
				
			||||||
 | 
					Each program has a directory in **AIRCOX_PROGRAMS_DATA**; For each, subdir:
 | 
				
			||||||
 | 
					* **public**:   public sound files and data (accessible from the website)
 | 
				
			||||||
 | 
					* **private**:  private sound files and data
 | 
				
			||||||
 | 
					* **podcasts**: podcasts that can be upload to external plateforms
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Event
 | 
				
			||||||
 | 
					Event have a double purpose:
 | 
				
			||||||
 | 
					- log played sounds
 | 
				
			||||||
 | 
					- plannify diffusions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# manage.py schedule
 | 
				
			||||||
 | 
					Return the next songs to be played and the schedule and the programmed emissions
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# manage.py monitor
 | 
				
			||||||
 | 
					The manage.py has a command **monitor** that:
 | 
				
			||||||
 | 
					* check for new sound files
 | 
				
			||||||
 | 
					* stat the sound files
 | 
				
			||||||
 | 
					* match sound files against episodes and eventually program them
 | 
				
			||||||
 | 
					* upload public podcasts to mixcloud if required
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					The command will try to match file name against a planified episode by detecting
 | 
				
			||||||
 | 
					a date (ISO 8601 date notation YYYY-MM-DD or YYYYMMDD) as name prefix
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					Tags set:
 | 
				
			||||||
 | 
					* **incorrect**:    the sound is not correct for diffusion (TODO: parameters)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -1,5 +1,6 @@
 | 
				
			|||||||
import copy
 | 
					import copy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django import forms
 | 
				
			||||||
from django.contrib import admin
 | 
					from django.contrib import admin
 | 
				
			||||||
from django.db import models
 | 
					from django.db import models
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -9,6 +10,7 @@ from autocomplete_light.contrib.taggit_field import TaggitWidget, TaggitField
 | 
				
			|||||||
from programs.forms import *
 | 
					from programs.forms import *
 | 
				
			||||||
from programs.models import *
 | 
					from programs.models import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
# Inlines
 | 
					# Inlines
 | 
				
			||||||
#
 | 
					#
 | 
				
			||||||
@ -43,7 +45,7 @@ class MetadataAdmin (admin.ModelAdmin):
 | 
				
			|||||||
            'fields': [ 'title', 'tags' ]
 | 
					            'fields': [ 'title', 'tags' ]
 | 
				
			||||||
        }),
 | 
					        }),
 | 
				
			||||||
        ( None, {
 | 
					        ( None, {
 | 
				
			||||||
            'fields': [ 'date', 'public', 'enumerable' ],
 | 
					            'fields': [ 'date', 'public' ],
 | 
				
			||||||
        }),
 | 
					        }),
 | 
				
			||||||
    ]
 | 
					    ]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -57,9 +59,9 @@ class MetadataAdmin (admin.ModelAdmin):
 | 
				
			|||||||
class PublicationAdmin (MetadataAdmin):
 | 
					class PublicationAdmin (MetadataAdmin):
 | 
				
			||||||
    fieldsets = copy.deepcopy(MetadataAdmin.fieldsets)
 | 
					    fieldsets = copy.deepcopy(MetadataAdmin.fieldsets)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    list_display = ('id', 'title', 'date', 'public', 'enumerable', 'parent')
 | 
					    list_display = ('id', 'title', 'date', 'public', 'parent')
 | 
				
			||||||
    list_filter = ['date', 'public', 'parent', 'author']
 | 
					    list_filter = ['date', 'public', 'parent', 'author']
 | 
				
			||||||
    list_editable = ('public', 'enumerable')
 | 
					    list_editable = ('public',)
 | 
				
			||||||
    search_fields = ['title', 'content']
 | 
					    search_fields = ['title', 'content']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fieldsets[0][1]['fields'].insert(1, 'subtitle')
 | 
					    fieldsets[0][1]['fields'].insert(1, 'subtitle')
 | 
				
			||||||
@ -77,18 +79,11 @@ class SoundAdmin (MetadataAdmin):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
@admin.register(Stream)
 | 
					@admin.register(Stream)
 | 
				
			||||||
class StreamAdmin (SortableModelAdmin):
 | 
					class StreamAdmin (SortableModelAdmin):
 | 
				
			||||||
    list_display = ('id', 'name', 'type', 'public', 'enumerable', 'priority')
 | 
					    list_display = ('id', 'title', 'type', 'public', 'priority')
 | 
				
			||||||
    list_editable = ('public', 'enumerable')
 | 
					    list_editable = ('public',)
 | 
				
			||||||
    sortable = "priority"
 | 
					    sortable = "priority"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@admin.register(Article)
 | 
					 | 
				
			||||||
class ArticleAdmin (PublicationAdmin):
 | 
					 | 
				
			||||||
    fieldsets           = copy.deepcopy(PublicationAdmin.fieldsets)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    fieldsets[1][1]['fields'] += ['static_page']
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@admin.register(Program)
 | 
					@admin.register(Program)
 | 
				
			||||||
class ProgramAdmin (PublicationAdmin):
 | 
					class ProgramAdmin (PublicationAdmin):
 | 
				
			||||||
    fieldsets = copy.deepcopy(PublicationAdmin.fieldsets)
 | 
					    fieldsets = copy.deepcopy(PublicationAdmin.fieldsets)
 | 
				
			||||||
 | 
				
			|||||||
@ -119,17 +119,11 @@ class Model:
 | 
				
			|||||||
        elif options.get('tail'):
 | 
					        elif options.get('tail'):
 | 
				
			||||||
            items = items[-options.get('tail'):]
 | 
					            items = items[-options.get('tail'):]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if options.get('json'):
 | 
					 | 
				
			||||||
        if options.get('fields'):
 | 
					        if options.get('fields'):
 | 
				
			||||||
            print(json.dumps(fields))
 | 
					            print(json.dumps(fields))
 | 
				
			||||||
        print(json.dumps(items, default = lambda x: str(x)))
 | 
					        print(json.dumps(items, default = lambda x: str(x)))
 | 
				
			||||||
        return
 | 
					        return
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        if options.get('fields'):
 | 
					 | 
				
			||||||
            print(' || '.join(fields))
 | 
					 | 
				
			||||||
        for item in items:
 | 
					 | 
				
			||||||
            print(' || '.join(item))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
def DateTime (string):
 | 
					def DateTime (string):
 | 
				
			||||||
    dt = timezone.datetime.strptime(string, '%Y-%m-%d %H:%M:%S')
 | 
					    dt = timezone.datetime.strptime(string, '%Y-%m-%d %H:%M:%S')
 | 
				
			||||||
 | 
				
			|||||||
@ -1,6 +1,5 @@
 | 
				
			|||||||
import os
 | 
					import os
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# 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
 | 
				
			||||||
@ -10,7 +9,6 @@ from django.utils.translation               import ugettext as _, ugettext_lazy
 | 
				
			|||||||
from django.utils import timezone as tz
 | 
					from django.utils import timezone as tz
 | 
				
			||||||
from django.utils.html import strip_tags
 | 
					from django.utils.html import strip_tags
 | 
				
			||||||
 | 
					
 | 
				
			||||||
# extensions
 | 
					 | 
				
			||||||
from taggit.managers import TaggableManager
 | 
					from taggit.managers import TaggableManager
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import programs.settings as settings
 | 
					import programs.settings as settings
 | 
				
			||||||
@ -29,28 +27,6 @@ def date_or_default (date, date_only = False):
 | 
				
			|||||||
    return date
 | 
					    return date
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
#class Model (models.Model):
 | 
					 | 
				
			||||||
#    @classmethod
 | 
					 | 
				
			||||||
#    def type (cl):
 | 
					 | 
				
			||||||
#        """
 | 
					 | 
				
			||||||
#        Return a string with the type of the model (class name lowered)
 | 
					 | 
				
			||||||
#        """
 | 
					 | 
				
			||||||
#        name = cl.__name__.lower()
 | 
					 | 
				
			||||||
#        return name
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
#    @classmethod
 | 
					 | 
				
			||||||
#    def name (cl, plural = False):
 | 
					 | 
				
			||||||
#        """
 | 
					 | 
				
			||||||
#        Return the name of the model using meta.verbose_name
 | 
					 | 
				
			||||||
#        """
 | 
					 | 
				
			||||||
#        if plural:
 | 
					 | 
				
			||||||
#            return cl._meta.verbose_name_plural.title()
 | 
					 | 
				
			||||||
#        return cl._meta.verbose_name.title()
 | 
					 | 
				
			||||||
#
 | 
					 | 
				
			||||||
#    class Meta:
 | 
					 | 
				
			||||||
#        abstract = True
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Metadata (models.Model):
 | 
					class Metadata (models.Model):
 | 
				
			||||||
    """
 | 
					    """
 | 
				
			||||||
    meta is used to extend a model for future needs
 | 
					    meta is used to extend a model for future needs
 | 
				
			||||||
@ -73,16 +49,14 @@ class Metadata (models.Model):
 | 
				
			|||||||
        default = True,
 | 
					        default = True,
 | 
				
			||||||
        help_text = _('publication is public'),
 | 
					        help_text = _('publication is public'),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    enumerable = models.BooleanField(
 | 
					 | 
				
			||||||
        _('enumerable'),
 | 
					 | 
				
			||||||
        default = True,
 | 
					 | 
				
			||||||
        help_text = _('publication is listable'),
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    tags = TaggableManager(
 | 
					    tags = TaggableManager(
 | 
				
			||||||
        _('tags'),
 | 
					        _('tags'),
 | 
				
			||||||
        blank = True,
 | 
					        blank = True,
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_slug_name (self):
 | 
				
			||||||
 | 
					        return slugify(self.title)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    class Meta:
 | 
					    class Meta:
 | 
				
			||||||
        abstract = True
 | 
					        abstract = True
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -108,28 +82,6 @@ class Publication (Metadata):
 | 
				
			|||||||
        help_text = _('comments are enabled on this publication'),
 | 
					        help_text = _('comments are enabled on this publication'),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_slug_name (self):
 | 
					 | 
				
			||||||
        return slugify(self.title)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def get_parents (self, order_by = "desc", include_fields = None):
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Return an array of the parents of the item.
 | 
					 | 
				
			||||||
        If include_fields is an array of files to include.
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        # TODO: fields included
 | 
					 | 
				
			||||||
        # FIXME: parameter name + container
 | 
					 | 
				
			||||||
        parents = [ self ]
 | 
					 | 
				
			||||||
        while parents[-1].parent:
 | 
					 | 
				
			||||||
            parent = parents[-1].parent
 | 
					 | 
				
			||||||
            if parent not in parents:
 | 
					 | 
				
			||||||
                # avoid cycles
 | 
					 | 
				
			||||||
                parents.append(parent)
 | 
					 | 
				
			||||||
        parents = parents[1:]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if order_by == 'desc':
 | 
					 | 
				
			||||||
            return reversed(parents)
 | 
					 | 
				
			||||||
        return parents
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    @staticmethod
 | 
					    @staticmethod
 | 
				
			||||||
    def _exclude_args (allow_unpublished = False, prefix = ''):
 | 
					    def _exclude_args (allow_unpublished = False, prefix = ''):
 | 
				
			||||||
        if allow_unpublished:
 | 
					        if allow_unpublished:
 | 
				
			||||||
@ -181,7 +133,7 @@ class Track (models.Model):
 | 
				
			|||||||
    )
 | 
					    )
 | 
				
			||||||
    tags = TaggableManager( blank = True )
 | 
					    tags = TaggableManager( blank = True )
 | 
				
			||||||
    # position can be used to specify a position in seconds for non-stop
 | 
					    # position can be used to specify a position in seconds for non-stop
 | 
				
			||||||
    # programs
 | 
					    # programs or a position in the playlist
 | 
				
			||||||
    position = models.SmallIntegerField(
 | 
					    position = models.SmallIntegerField(
 | 
				
			||||||
        default = 0,
 | 
					        default = 0,
 | 
				
			||||||
        help_text=_('position in the playlist'),
 | 
					        help_text=_('position in the playlist'),
 | 
				
			||||||
@ -389,11 +341,11 @@ class Schedule (models.Model):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        # others
 | 
					        # others
 | 
				
			||||||
        for date in dates:
 | 
					        for date in dates:
 | 
				
			||||||
            ep_date = date
 | 
					            first_date = date
 | 
				
			||||||
            if self.rerun:
 | 
					            if self.rerun:
 | 
				
			||||||
                ep_date -= self.date - self.rerun.date
 | 
					                first_date -= self.date - self.rerun.date
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            episode = Episode.objects.filter(date = date,
 | 
					            episode = Episode.objects.filter(date = first_date,
 | 
				
			||||||
                                             parent = self.parent)
 | 
					                                             parent = self.parent)
 | 
				
			||||||
            episode = episode[0] if episode.count() else None
 | 
					            episode = episode[0] if episode.count() else None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -418,7 +370,7 @@ class Schedule (models.Model):
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
class Diffusion (models.Model):
 | 
					class Diffusion (models.Model):
 | 
				
			||||||
    Type = {
 | 
					    Type = {
 | 
				
			||||||
        'normal':       0x00,   # simple diffusion (done/planed)
 | 
					        'default':      0x00,   # simple diffusion (done/planed)
 | 
				
			||||||
        'unconfirmed':  0x01,   # scheduled by the generator but not confirmed for diffusion
 | 
					        'unconfirmed':  0x01,   # scheduled by the generator but not confirmed for diffusion
 | 
				
			||||||
        'cancel':       0x02,   # cancellation happened; used to inform users
 | 
					        'cancel':       0x02,   # cancellation happened; used to inform users
 | 
				
			||||||
        'restart':      0x03,   # manual restart; used to remix/give up antenna
 | 
					        'restart':      0x03,   # manual restart; used to remix/give up antenna
 | 
				
			||||||
@ -472,9 +424,8 @@ class Stream (models.Model):
 | 
				
			|||||||
    for key, value in Type.items():
 | 
					    for key, value in Type.items():
 | 
				
			||||||
        ugettext_lazy(key)
 | 
					        ugettext_lazy(key)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # FIXME: id as integer?
 | 
					    title = models.CharField(
 | 
				
			||||||
    name = models.CharField(
 | 
					        _('title'),
 | 
				
			||||||
        _('name'),
 | 
					 | 
				
			||||||
        max_length = 32,
 | 
					        max_length = 32,
 | 
				
			||||||
        blank = True,
 | 
					        blank = True,
 | 
				
			||||||
        null = True,
 | 
					        null = True,
 | 
				
			||||||
@ -483,8 +434,6 @@ class Stream (models.Model):
 | 
				
			|||||||
        verbose_name = _('type'),
 | 
					        verbose_name = _('type'),
 | 
				
			||||||
        choices = [ (y, x) for x,y in Type.items() ],
 | 
					        choices = [ (y, x) for x,y in Type.items() ],
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
    # FIXME unique value / suit's orderable
 | 
					 | 
				
			||||||
    #
 | 
					 | 
				
			||||||
    priority = models.SmallIntegerField(
 | 
					    priority = models.SmallIntegerField(
 | 
				
			||||||
        _('priority'),
 | 
					        _('priority'),
 | 
				
			||||||
        default = 0,
 | 
					        default = 0,
 | 
				
			||||||
@ -493,12 +442,7 @@ class Stream (models.Model):
 | 
				
			|||||||
    public = models.BooleanField(
 | 
					    public = models.BooleanField(
 | 
				
			||||||
        _('public'),
 | 
					        _('public'),
 | 
				
			||||||
        default = True,
 | 
					        default = True,
 | 
				
			||||||
        help_text = _('content is public'),
 | 
					        help_text = _('program list is public'),
 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    enumerable = models.BooleanField(
 | 
					 | 
				
			||||||
        _('enumerable'),
 | 
					 | 
				
			||||||
        default = True,
 | 
					 | 
				
			||||||
        help_text = _('publication is listable'),
 | 
					 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # get info for:
 | 
					    # get info for:
 | 
				
			||||||
@ -509,39 +453,11 @@ class Stream (models.Model):
 | 
				
			|||||||
    #   - stream/pgm
 | 
					    #   - stream/pgm
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def __str__ (self):
 | 
					    def __str__ (self):
 | 
				
			||||||
        return self.name + ' / ' + str(self.priority)
 | 
					        return '#{} {}'.format(self.priority, self.title)
 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
class Article (Publication):
 | 
					 | 
				
			||||||
    # FIXME: move to website?
 | 
					 | 
				
			||||||
    parent = models.ForeignKey(
 | 
					 | 
				
			||||||
        'self',
 | 
					 | 
				
			||||||
        verbose_name = _('parent'),
 | 
					 | 
				
			||||||
        blank = True, null = True,
 | 
					 | 
				
			||||||
        help_text = _('parent article'),
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    static_page = models.BooleanField(
 | 
					 | 
				
			||||||
        _('static page'),
 | 
					 | 
				
			||||||
        default = False,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    focus = models.BooleanField(
 | 
					 | 
				
			||||||
        _('article is focus'),
 | 
					 | 
				
			||||||
        default = False,
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    class Meta:
 | 
					 | 
				
			||||||
        verbose_name = _('Article')
 | 
					 | 
				
			||||||
        verbose_name_plural = _('Articles')
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Program (Publication):
 | 
					class Program (Publication):
 | 
				
			||||||
    parent = models.ForeignKey(
 | 
					    parent = models.ForeignKey(
 | 
				
			||||||
        Article,
 | 
					 | 
				
			||||||
        verbose_name = _('parent'),
 | 
					 | 
				
			||||||
        blank = True, null = True,
 | 
					 | 
				
			||||||
        help_text = _('parent article'),
 | 
					 | 
				
			||||||
    )
 | 
					 | 
				
			||||||
    stream = models.ForeignKey(
 | 
					 | 
				
			||||||
        Stream,
 | 
					        Stream,
 | 
				
			||||||
        verbose_name = _('stream'),
 | 
					        verbose_name = _('stream'),
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
 | 
				
			|||||||
@ -1,76 +0,0 @@
 | 
				
			|||||||
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 DiffusionList:
 | 
					 | 
				
			||||||
    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):
 | 
					 | 
				
			||||||
        diffusions = models.Diffusion.objects;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        if self.next:   diffusions = diffusions.filter( date_end__ge = timezone.now() )
 | 
					 | 
				
			||||||
        elif self.prev: diffusions = diffusions.filter( date_end__le = timezone.now() )
 | 
					 | 
				
			||||||
        else:           diffusions = diffusions.all()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        diffusions = diffusions.extra(order_by = ['date'])
 | 
					 | 
				
			||||||
        if self.at:     diffusions = diffusions[self.at:]
 | 
					 | 
				
			||||||
        if self.count:  diffusions = diffusions[:self.count]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        self.diffusions = diffusions
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def raw_string():
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        Return a string with diffusions rendered as raw
 | 
					 | 
				
			||||||
        """
 | 
					 | 
				
			||||||
        res = []
 | 
					 | 
				
			||||||
        for diffusion in diffusions:
 | 
					 | 
				
			||||||
            r = [ dateformat.format(diffusion.date, "Y/m/d H:i:s")
 | 
					 | 
				
			||||||
                , str(diffusion.type)
 | 
					 | 
				
			||||||
                , diffusion.parent.file.path
 | 
					 | 
				
			||||||
                , diffusion.parent.file.url
 | 
					 | 
				
			||||||
                ]
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            res.push(' '.join(r))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return '\n'.join(res)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    def json_string():
 | 
					 | 
				
			||||||
        import json
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        res = []
 | 
					 | 
				
			||||||
        for diffusion in diffusions:
 | 
					 | 
				
			||||||
            r = {
 | 
					 | 
				
			||||||
                  'date': dateformat.format(diffusion.date, "Y/m/d H:i:s")
 | 
					 | 
				
			||||||
                , 'date_end': dateformat.format(diffusion.date_end, "Y/m/d H:i:s")
 | 
					 | 
				
			||||||
                , 'type': str(diffusion.type)
 | 
					 | 
				
			||||||
                , 'file_path': diffusion.parent.file.path
 | 
					 | 
				
			||||||
                , 'file_url': diffusion.parent.file.url
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            res.push(json.dumps(r))
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        return '\n'.join(res)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
							
								
								
									
										0
									
								
								website/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								website/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										15
									
								
								website/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								website/admin.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,15 @@
 | 
				
			|||||||
 | 
					import copy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from django.contrib import admin
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from programs.admin import PublicationAdmin
 | 
				
			||||||
 | 
					from website.models import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@admin.register(Article)
 | 
				
			||||||
 | 
					class ArticleAdmin (PublicationAdmin):
 | 
				
			||||||
 | 
					    fieldsets = copy.deepcopy(PublicationAdmin.fieldsets)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fieldsets[1][1]['fields'] += ['static_page']
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										35
									
								
								website/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										35
									
								
								website/models.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,35 @@
 | 
				
			|||||||
 | 
					from django.db import models
 | 
				
			||||||
 | 
					from django.utils.translation import ugettext as _, ugettext_lazy
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					from programs.models import Publication
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class Article (Publication):
 | 
				
			||||||
 | 
					    parent = models.ForeignKey(
 | 
				
			||||||
 | 
					        'self',
 | 
				
			||||||
 | 
					        verbose_name = _('parent'),
 | 
				
			||||||
 | 
					        blank = True, null = True,
 | 
				
			||||||
 | 
					        help_text = _('parent article'),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    static_page = models.BooleanField(
 | 
				
			||||||
 | 
					        _('static page'),
 | 
				
			||||||
 | 
					        default = False,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    focus = models.BooleanField(
 | 
				
			||||||
 | 
					        _('article is focus'),
 | 
				
			||||||
 | 
					        default = False,
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					    referring_tag = models.CharField(
 | 
				
			||||||
 | 
					        _('referring tag'),
 | 
				
			||||||
 | 
					        max_length = 32,
 | 
				
			||||||
 | 
					        blank = True, null = True,
 | 
				
			||||||
 | 
					        help_text = _('tag used by other to refers to this article'),
 | 
				
			||||||
 | 
					    )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Meta:
 | 
				
			||||||
 | 
					        verbose_name = _('Article')
 | 
				
			||||||
 | 
					        verbose_name_plural = _('Articles')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								website/tests.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								website/tests.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					from django.test import TestCase
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Create your tests here.
 | 
				
			||||||
							
								
								
									
										83
									
								
								website/utils.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										83
									
								
								website/utils.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,83 @@
 | 
				
			|||||||
 | 
					from django.db import models
 | 
				
			||||||
 | 
					from django.utils import timezone, dateformat
 | 
				
			||||||
 | 
					from programs.models import *
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ListQueries:
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def search (qs, q):
 | 
				
			||||||
 | 
					        qs = qs.filter(tags__slug__in = re.compile(r'(\s|\+)+').split(q)) | \
 | 
				
			||||||
 | 
					             qs.filter(title__icontains = q) | \
 | 
				
			||||||
 | 
					             qs.filter(subtitle__icontains = q) | \
 | 
				
			||||||
 | 
					             qs.filter(content__icontains = q)
 | 
				
			||||||
 | 
					        qs.distinct()
 | 
				
			||||||
 | 
					        return qs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def thread (qs, q):
 | 
				
			||||||
 | 
					        return qs.filter(parent = q)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def next (qs, q):
 | 
				
			||||||
 | 
					        qs = qs.filter(date__gte = timezone.now())
 | 
				
			||||||
 | 
					        if q:
 | 
				
			||||||
 | 
					            qs = qs.filter(parent = q)
 | 
				
			||||||
 | 
					        return qs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def prev (qs, q):
 | 
				
			||||||
 | 
					        qs = qs.filter(date__lte = timezone.now())
 | 
				
			||||||
 | 
					        if q:
 | 
				
			||||||
 | 
					            qs = qs.filter(parent = q)
 | 
				
			||||||
 | 
					        return qs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    @staticmethod
 | 
				
			||||||
 | 
					    def date (qs, q):
 | 
				
			||||||
 | 
					        if not q:
 | 
				
			||||||
 | 
					            q = timezone.datetime.today()
 | 
				
			||||||
 | 
					        if type(q) is str:
 | 
				
			||||||
 | 
					            q = timezone.datetime.strptime(q, '%Y/%m/%d').date()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        return qs.filter(date__startswith = q)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    class Diffusion:
 | 
				
			||||||
 | 
					        @staticmethod
 | 
				
			||||||
 | 
					        def episode (qs, q):
 | 
				
			||||||
 | 
					            return qs.filter(episode = q)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        @staticmethod
 | 
				
			||||||
 | 
					        def program (qs, q):
 | 
				
			||||||
 | 
					            return qs.filter(program = q)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class ListQuery:
 | 
				
			||||||
 | 
					    model = None
 | 
				
			||||||
 | 
					    qs = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def __init__ (self, model, *kwargs):
 | 
				
			||||||
 | 
					        self.model = model
 | 
				
			||||||
 | 
					        self.__dict__.update(kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get_queryset (self, by, q):
 | 
				
			||||||
 | 
					        qs = model.objects.all()
 | 
				
			||||||
 | 
					        if model._meta.get_field_by_name('public'):
 | 
				
			||||||
 | 
					            qs = qs.filter(public = True)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # run query set
 | 
				
			||||||
 | 
					        queries = Queries.__dict__.get(self.model) or Queries
 | 
				
			||||||
 | 
					        filter = queries.__dict__.get(by)
 | 
				
			||||||
 | 
					        if filter:
 | 
				
			||||||
 | 
					            qs = filter(qs, q)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # order
 | 
				
			||||||
 | 
					        if self.sort == 'asc':
 | 
				
			||||||
 | 
					            qs = qs.order_by('date', 'id')
 | 
				
			||||||
 | 
					        else:
 | 
				
			||||||
 | 
					            qs = qs.order_by('-date', '-id')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # exclude
 | 
				
			||||||
 | 
					        qs = qs.exclude(id = exclude)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.qs = qs
 | 
				
			||||||
 | 
					        return qs
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
							
								
								
									
										3
									
								
								website/views.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								website/views.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,3 @@
 | 
				
			|||||||
 | 
					from django.shortcuts import render
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					# Create your views here.
 | 
				
			||||||
		Reference in New Issue
	
	Block a user