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,13 +1,15 @@
 | 
			
		||||
import copy
 | 
			
		||||
 | 
			
		||||
from django.contrib     import admin
 | 
			
		||||
from django.db          import models
 | 
			
		||||
from django import forms
 | 
			
		||||
from django.contrib import admin
 | 
			
		||||
from django.db import models
 | 
			
		||||
 | 
			
		||||
from suit.admin import SortableTabularInline, SortableModelAdmin
 | 
			
		||||
from autocomplete_light.contrib.taggit_field import TaggitWidget, TaggitField
 | 
			
		||||
 | 
			
		||||
from programs.forms     import *
 | 
			
		||||
from programs.models    import *
 | 
			
		||||
from programs.forms import *
 | 
			
		||||
from programs.models import *
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#
 | 
			
		||||
# Inlines
 | 
			
		||||
@ -43,7 +45,7 @@ class MetadataAdmin (admin.ModelAdmin):
 | 
			
		||||
            'fields': [ 'title', 'tags' ]
 | 
			
		||||
        }),
 | 
			
		||||
        ( None, {
 | 
			
		||||
            'fields': [ 'date', 'public', 'enumerable' ],
 | 
			
		||||
            'fields': [ 'date', 'public' ],
 | 
			
		||||
        }),
 | 
			
		||||
    ]
 | 
			
		||||
 | 
			
		||||
@ -57,9 +59,9 @@ class MetadataAdmin (admin.ModelAdmin):
 | 
			
		||||
class PublicationAdmin (MetadataAdmin):
 | 
			
		||||
    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_editable = ('public', 'enumerable')
 | 
			
		||||
    list_editable = ('public',)
 | 
			
		||||
    search_fields = ['title', 'content']
 | 
			
		||||
 | 
			
		||||
    fieldsets[0][1]['fields'].insert(1, 'subtitle')
 | 
			
		||||
@ -77,22 +79,15 @@ class SoundAdmin (MetadataAdmin):
 | 
			
		||||
 | 
			
		||||
@admin.register(Stream)
 | 
			
		||||
class StreamAdmin (SortableModelAdmin):
 | 
			
		||||
    list_display = ('id', 'name', 'type', 'public', 'enumerable', 'priority')
 | 
			
		||||
    list_editable = ('public', 'enumerable')
 | 
			
		||||
    list_display = ('id', 'title', 'type', 'public', 'priority')
 | 
			
		||||
    list_editable = ('public',)
 | 
			
		||||
    sortable = "priority"
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@admin.register(Article)
 | 
			
		||||
class ArticleAdmin (PublicationAdmin):
 | 
			
		||||
    fieldsets           = copy.deepcopy(PublicationAdmin.fieldsets)
 | 
			
		||||
 | 
			
		||||
    fieldsets[1][1]['fields'] += ['static_page']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@admin.register(Program)
 | 
			
		||||
class ProgramAdmin (PublicationAdmin):
 | 
			
		||||
    fieldsets           = copy.deepcopy(PublicationAdmin.fieldsets)
 | 
			
		||||
    inlines             = [ ScheduleInline ]
 | 
			
		||||
    fieldsets = copy.deepcopy(PublicationAdmin.fieldsets)
 | 
			
		||||
    inlines = [ ScheduleInline ]
 | 
			
		||||
 | 
			
		||||
    fieldsets[1][1]['fields'] += ['email', 'url']
 | 
			
		||||
 | 
			
		||||
@ -100,7 +95,7 @@ class ProgramAdmin (PublicationAdmin):
 | 
			
		||||
@admin.register(Episode)
 | 
			
		||||
class EpisodeAdmin (PublicationAdmin):
 | 
			
		||||
    fieldsets = copy.deepcopy(PublicationAdmin.fieldsets)
 | 
			
		||||
    list_filter         = ['parent'] + PublicationAdmin.list_filter
 | 
			
		||||
    list_filter = ['parent'] + PublicationAdmin.list_filter
 | 
			
		||||
 | 
			
		||||
    fieldsets[0][1]['fields'] += ['sounds']
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -119,16 +119,10 @@ class Model:
 | 
			
		||||
        elif options.get('tail'):
 | 
			
		||||
            items = items[-options.get('tail'):]
 | 
			
		||||
 | 
			
		||||
        if options.get('json'):
 | 
			
		||||
            if options.get('fields'):
 | 
			
		||||
                print(json.dumps(fields))
 | 
			
		||||
            print(json.dumps(items, default = lambda x: str(x)))
 | 
			
		||||
            return
 | 
			
		||||
 | 
			
		||||
        if options.get('fields'):
 | 
			
		||||
            print(' || '.join(fields))
 | 
			
		||||
        for item in items:
 | 
			
		||||
            print(' || '.join(item))
 | 
			
		||||
            print(json.dumps(fields))
 | 
			
		||||
        print(json.dumps(items, default = lambda x: str(x)))
 | 
			
		||||
        return
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def DateTime (string):
 | 
			
		||||
 | 
			
		||||
@ -1,19 +1,17 @@
 | 
			
		||||
import os
 | 
			
		||||
 | 
			
		||||
# django
 | 
			
		||||
from django.db                              import models
 | 
			
		||||
from django.contrib.auth.models             import User
 | 
			
		||||
from django.template.defaultfilters         import slugify
 | 
			
		||||
from django.contrib.contenttypes.fields     import GenericForeignKey
 | 
			
		||||
from django.contrib.contenttypes.models     import ContentType
 | 
			
		||||
from django.utils.translation               import ugettext as _, ugettext_lazy
 | 
			
		||||
from django.utils                           import timezone as tz
 | 
			
		||||
from django.utils.html                      import strip_tags
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.contrib.auth.models import User
 | 
			
		||||
from django.template.defaultfilters import slugify
 | 
			
		||||
from django.contrib.contenttypes.fields import GenericForeignKey
 | 
			
		||||
from django.contrib.contenttypes.models import ContentType
 | 
			
		||||
from django.utils.translation import ugettext as _, ugettext_lazy
 | 
			
		||||
from django.utils import timezone as tz
 | 
			
		||||
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
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
def date_or_default (date, date_only = False):
 | 
			
		||||
@ -29,28 +27,6 @@ def date_or_default (date, date_only = False):
 | 
			
		||||
    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):
 | 
			
		||||
    """
 | 
			
		||||
    meta is used to extend a model for future needs
 | 
			
		||||
@ -73,16 +49,14 @@ class Metadata (models.Model):
 | 
			
		||||
        default = True,
 | 
			
		||||
        help_text = _('publication is public'),
 | 
			
		||||
    )
 | 
			
		||||
    enumerable = models.BooleanField(
 | 
			
		||||
        _('enumerable'),
 | 
			
		||||
        default = True,
 | 
			
		||||
        help_text = _('publication is listable'),
 | 
			
		||||
    )
 | 
			
		||||
    tags = TaggableManager(
 | 
			
		||||
        _('tags'),
 | 
			
		||||
        blank = True,
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    def get_slug_name (self):
 | 
			
		||||
        return slugify(self.title)
 | 
			
		||||
 | 
			
		||||
    class Meta:
 | 
			
		||||
        abstract = True
 | 
			
		||||
 | 
			
		||||
@ -108,28 +82,6 @@ class Publication (Metadata):
 | 
			
		||||
        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
 | 
			
		||||
    def _exclude_args (allow_unpublished = False, prefix = ''):
 | 
			
		||||
        if allow_unpublished:
 | 
			
		||||
@ -181,7 +133,7 @@ class Track (models.Model):
 | 
			
		||||
    )
 | 
			
		||||
    tags = TaggableManager( blank = True )
 | 
			
		||||
    # position can be used to specify a position in seconds for non-stop
 | 
			
		||||
    # programs
 | 
			
		||||
    # programs or a position in the playlist
 | 
			
		||||
    position = models.SmallIntegerField(
 | 
			
		||||
        default = 0,
 | 
			
		||||
        help_text=_('position in the playlist'),
 | 
			
		||||
@ -389,11 +341,11 @@ class Schedule (models.Model):
 | 
			
		||||
 | 
			
		||||
        # others
 | 
			
		||||
        for date in dates:
 | 
			
		||||
            ep_date = date
 | 
			
		||||
            first_date = date
 | 
			
		||||
            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)
 | 
			
		||||
            episode = episode[0] if episode.count() else None
 | 
			
		||||
 | 
			
		||||
@ -418,7 +370,7 @@ class Schedule (models.Model):
 | 
			
		||||
 | 
			
		||||
class Diffusion (models.Model):
 | 
			
		||||
    Type = {
 | 
			
		||||
        'normal':       0x00,   # simple diffusion (done/planed)
 | 
			
		||||
        'default':      0x00,   # simple diffusion (done/planed)
 | 
			
		||||
        'unconfirmed':  0x01,   # scheduled by the generator but not confirmed for diffusion
 | 
			
		||||
        'cancel':       0x02,   # cancellation happened; used to inform users
 | 
			
		||||
        'restart':      0x03,   # manual restart; used to remix/give up antenna
 | 
			
		||||
@ -472,9 +424,8 @@ class Stream (models.Model):
 | 
			
		||||
    for key, value in Type.items():
 | 
			
		||||
        ugettext_lazy(key)
 | 
			
		||||
 | 
			
		||||
    # FIXME: id as integer?
 | 
			
		||||
    name = models.CharField(
 | 
			
		||||
        _('name'),
 | 
			
		||||
    title = models.CharField(
 | 
			
		||||
        _('title'),
 | 
			
		||||
        max_length = 32,
 | 
			
		||||
        blank = True,
 | 
			
		||||
        null = True,
 | 
			
		||||
@ -483,8 +434,6 @@ class Stream (models.Model):
 | 
			
		||||
        verbose_name = _('type'),
 | 
			
		||||
        choices = [ (y, x) for x,y in Type.items() ],
 | 
			
		||||
    )
 | 
			
		||||
    # FIXME unique value / suit's orderable
 | 
			
		||||
    #
 | 
			
		||||
    priority = models.SmallIntegerField(
 | 
			
		||||
        _('priority'),
 | 
			
		||||
        default = 0,
 | 
			
		||||
@ -493,12 +442,7 @@ class Stream (models.Model):
 | 
			
		||||
    public = models.BooleanField(
 | 
			
		||||
        _('public'),
 | 
			
		||||
        default = True,
 | 
			
		||||
        help_text = _('content is public'),
 | 
			
		||||
    )
 | 
			
		||||
    enumerable = models.BooleanField(
 | 
			
		||||
        _('enumerable'),
 | 
			
		||||
        default = True,
 | 
			
		||||
        help_text = _('publication is listable'),
 | 
			
		||||
        help_text = _('program list is public'),
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    # get info for:
 | 
			
		||||
@ -509,39 +453,11 @@ class Stream (models.Model):
 | 
			
		||||
    #   - stream/pgm
 | 
			
		||||
 | 
			
		||||
    def __str__ (self):
 | 
			
		||||
        return self.name + ' / ' + str(self.priority)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
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')
 | 
			
		||||
        return '#{} {}'.format(self.priority, self.title)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Program (Publication):
 | 
			
		||||
    parent = models.ForeignKey(
 | 
			
		||||
        Article,
 | 
			
		||||
        verbose_name = _('parent'),
 | 
			
		||||
        blank = True, null = True,
 | 
			
		||||
        help_text = _('parent article'),
 | 
			
		||||
    )
 | 
			
		||||
    stream = models.ForeignKey(
 | 
			
		||||
        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