add website application
This commit is contained in:
		@ -14,6 +14,11 @@ Simple CMS generator used in Aircox. Main features includes:
 | 
			
		||||
We aims here to automatize most common tasks and to ease website
 | 
			
		||||
configuration.
 | 
			
		||||
 | 
			
		||||
# Dependencies
 | 
			
		||||
* ```django-taggit```: publications tags
 | 
			
		||||
 | 
			
		||||
Note: this application can be used outside Aircox if needed.
 | 
			
		||||
 | 
			
		||||
# Architecture
 | 
			
		||||
A **Website** holds all required informations to run the server instance. It
 | 
			
		||||
is used to register all kind of posts, routes to the views, menus, etc.
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,8 @@
 | 
			
		||||
from django.contrib import admin
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
import aircox.cms.models as models
 | 
			
		||||
 | 
			
		||||
admin.site.register(cms.Article)
 | 
			
		||||
admin.site.register(cms.Comment)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -75,6 +75,7 @@
 | 
			
		||||
{% endfor %}
 | 
			
		||||
</ul>
 | 
			
		||||
 | 
			
		||||
{% if object_list %}
 | 
			
		||||
{% if page_obj or list.url %}
 | 
			
		||||
<nav>
 | 
			
		||||
    {% if not page_obj or embed %}
 | 
			
		||||
@ -117,5 +118,6 @@
 | 
			
		||||
    {% endif %}
 | 
			
		||||
</nav>
 | 
			
		||||
{% endif %}
 | 
			
		||||
{% endif %}
 | 
			
		||||
{% endblock %}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										30
									
								
								website/README.md
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										30
									
								
								website/README.md
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,30 @@
 | 
			
		||||
# Website
 | 
			
		||||
Application that propose a set of different tools that might be common to
 | 
			
		||||
different radio projects. This application has been started to avoid to
 | 
			
		||||
pollute *aircox.cms* with aircox specific code and models that might not
 | 
			
		||||
be used in other cases.
 | 
			
		||||
 | 
			
		||||
We define here different models and sections that can be used to construct
 | 
			
		||||
a website in a fast and simple manner.
 | 
			
		||||
 | 
			
		||||
# Dependencies
 | 
			
		||||
* ```django-suit```: admin interface;
 | 
			
		||||
* ```django-autocomplete-light```: autocompletion in the admin interface;
 | 
			
		||||
* ```aircox.cms```, ```aircox.programs```
 | 
			
		||||
 | 
			
		||||
# Features
 | 
			
		||||
## Models
 | 
			
		||||
* **Program**: publication related to a program;
 | 
			
		||||
* **Diffusion**: publication related to an initial Diffusion;
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
## Sections
 | 
			
		||||
* **Diffusions**: generic section list to retrieve diffusions by date, related
 | 
			
		||||
  or not to a specific Program. If wanted, can show schedule in the header of
 | 
			
		||||
  the section (with indication of reruns).
 | 
			
		||||
* **Playlist**: playlist of a given Diffusion
 | 
			
		||||
 | 
			
		||||
## Admin
 | 
			
		||||
Register all models declared upper, uses django-suit features in order to manage
 | 
			
		||||
some fields and autocompletion.
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										0
									
								
								website/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										0
									
								
								website/__init__.py
									
									
									
									
									
										Normal file
									
								
							
							
								
								
									
										8
									
								
								website/admin.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										8
									
								
								website/admin.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,8 @@
 | 
			
		||||
from django.contrib import admin
 | 
			
		||||
 | 
			
		||||
import aircox.website.models as models
 | 
			
		||||
 | 
			
		||||
admin.site.register(models.Program)
 | 
			
		||||
admin.site.register(models.Diffusion)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										52
									
								
								website/models.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										52
									
								
								website/models.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,52 @@
 | 
			
		||||
from django.db import models
 | 
			
		||||
from django.utils.translation import ugettext as _, ugettext_lazy
 | 
			
		||||
 | 
			
		||||
from aircox.cms.models import RelatedPost, Article
 | 
			
		||||
import aircox.programs.models as programs
 | 
			
		||||
 | 
			
		||||
class Program (RelatedPost):
 | 
			
		||||
    url = models.URLField(_('website'), blank=True, null=True)
 | 
			
		||||
    # rss = models.URLField()
 | 
			
		||||
    email = models.EmailField(
 | 
			
		||||
        _('email'), blank=True, null=True,
 | 
			
		||||
        help_text=_('contact address, stays private')
 | 
			
		||||
    )
 | 
			
		||||
 | 
			
		||||
    class Relation:
 | 
			
		||||
        model = programs.Program
 | 
			
		||||
        bindings = {
 | 
			
		||||
            'title': 'name',
 | 
			
		||||
        }
 | 
			
		||||
        rel_to_post = True
 | 
			
		||||
        auto_create = True
 | 
			
		||||
 | 
			
		||||
class Diffusion (RelatedPost):
 | 
			
		||||
    class Relation:
 | 
			
		||||
        model = programs.Diffusion
 | 
			
		||||
        bindings = {
 | 
			
		||||
            'thread': 'program',
 | 
			
		||||
            'date': 'start',
 | 
			
		||||
        }
 | 
			
		||||
        fields_args = {
 | 
			
		||||
            'limit_choice_to': {
 | 
			
		||||
                'initial': None
 | 
			
		||||
            }
 | 
			
		||||
        }
 | 
			
		||||
        rel_to_post = True
 | 
			
		||||
        auto_create = True
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        if self.thread:
 | 
			
		||||
            if not self.title:
 | 
			
		||||
                self.title = _('{name} on {first_diff}').format(
 | 
			
		||||
                    self.related.program.name,
 | 
			
		||||
                    self.related.start.strftime('%A %d %B')
 | 
			
		||||
                )
 | 
			
		||||
            if not self.content:
 | 
			
		||||
                self.content = self.thread.content
 | 
			
		||||
            if not self.image:
 | 
			
		||||
                self.image = self.thread.image
 | 
			
		||||
            if not self.tags and self.pk:
 | 
			
		||||
                self.tags = self.thread.tags
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										140
									
								
								website/sections.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										140
									
								
								website/sections.py
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,140 @@
 | 
			
		||||
from django.utils import timezone as tz
 | 
			
		||||
from django.utils.translation import ugettext as _, ugettext_lazy
 | 
			
		||||
 | 
			
		||||
import aircox.programs.models as programs
 | 
			
		||||
import aircox.cms.models as cms
 | 
			
		||||
import aircox.cms.routes as routes
 | 
			
		||||
import aircox.cms.sections as sections
 | 
			
		||||
 | 
			
		||||
import website.models as models
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Diffusions(sections.List):
 | 
			
		||||
    """
 | 
			
		||||
    Section that print diffusions. When rendering, if there is no post yet
 | 
			
		||||
    associated, use the programs' article.
 | 
			
		||||
    """
 | 
			
		||||
    next_count = 5
 | 
			
		||||
    prev_count = 5
 | 
			
		||||
    show_schedule = False
 | 
			
		||||
 | 
			
		||||
    def __init__(self, *args, **kwargs):
 | 
			
		||||
        super().__init__(*args, **kwargs)
 | 
			
		||||
        self.__dict__.update(kwargs)
 | 
			
		||||
 | 
			
		||||
    def get_diffs(self):
 | 
			
		||||
        qs = programs.Diffusion.objects.filter(
 | 
			
		||||
            type = programs.Diffusion.Type.normal
 | 
			
		||||
        )
 | 
			
		||||
        if self.object:
 | 
			
		||||
            qs = qs.filter(program = self.object.related)
 | 
			
		||||
 | 
			
		||||
        r = []
 | 
			
		||||
        if self.next_count:
 | 
			
		||||
            r += list(programs.Diffusion.get(next=True, queryset = qs)
 | 
			
		||||
                        .order_by('-start')[:self.next_count])
 | 
			
		||||
        if self.prev_count:
 | 
			
		||||
            r += list(programs.Diffusion.get(prev=True, queryset = qs)
 | 
			
		||||
                        .order_by('-start')[:self.prev_count])
 | 
			
		||||
        return r
 | 
			
		||||
 | 
			
		||||
    def get_object_list(self):
 | 
			
		||||
        diffs = self.get_diffs()
 | 
			
		||||
 | 
			
		||||
        posts = models.Diffusion.objects.filter(related__in = diffs)
 | 
			
		||||
        r = []
 | 
			
		||||
        for diff in diffs:
 | 
			
		||||
            diff_ = diff.initial if diff.initial else diff
 | 
			
		||||
            post = next((x for x in posts if x.related == diff_), None)
 | 
			
		||||
            if not post:
 | 
			
		||||
                post = sections.ListItem(date = diff.start)
 | 
			
		||||
            else:
 | 
			
		||||
                post = sections.ListItem(post=post)
 | 
			
		||||
                post.date = diff.start
 | 
			
		||||
 | 
			
		||||
            if diff.initial:
 | 
			
		||||
                post.info = _('rerun of %(day)s') % {
 | 
			
		||||
                    'day': diff.initial.date.strftime('%A %d/%m')
 | 
			
		||||
                }
 | 
			
		||||
 | 
			
		||||
            if self.object:
 | 
			
		||||
                post.update(self.object)
 | 
			
		||||
            else:
 | 
			
		||||
                thread = models.Program.objects. \
 | 
			
		||||
                            filter(related = diff.program, published = True)
 | 
			
		||||
                if thread:
 | 
			
		||||
                    post.update(thread[0])
 | 
			
		||||
            r.append(post)
 | 
			
		||||
        return [ sections.ListItem(post=post) for post in r ]
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def url(self):
 | 
			
		||||
        if self.object:
 | 
			
		||||
            return models.Diffusion.route_url(routes.ThreadRoute, {
 | 
			
		||||
                'pk': self.object.id,
 | 
			
		||||
                'thread_model': 'program',
 | 
			
		||||
            })
 | 
			
		||||
        return models.Diffusion.route_url(routes.AllRoute)
 | 
			
		||||
 | 
			
		||||
    @property
 | 
			
		||||
    def header(self):
 | 
			
		||||
        if not self.show_schedule:
 | 
			
		||||
            return None
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
        def str_sched(sched):
 | 
			
		||||
            info = ' <span class="info">(' + _('rerun of %(day)s') % {
 | 
			
		||||
                'day': sched.initial.date.strftime('%A')
 | 
			
		||||
            } + ')</span>' if sched.initial else ''
 | 
			
		||||
 | 
			
		||||
            text = _('%(day)s at %(time)s, %(freq)s') % {
 | 
			
		||||
                'day': sched.date.strftime('%A'),
 | 
			
		||||
                'time': sched.date.strftime('%H:%M'),
 | 
			
		||||
                'freq': sched.get_frequency_display(),
 | 
			
		||||
            }
 | 
			
		||||
            return text + info
 | 
			
		||||
 | 
			
		||||
        return ' / \n'.join([str_sched(sched)
 | 
			
		||||
                 for sched in programs.Schedule.objects \
 | 
			
		||||
                    .filter(program = self.object.related.pk)
 | 
			
		||||
        ])
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
class Playlist(sections.List):
 | 
			
		||||
    title = _('Playlist')
 | 
			
		||||
    message_empty = ''
 | 
			
		||||
 | 
			
		||||
    def get_object_list(self):
 | 
			
		||||
        tracks = programs.Track.objects \
 | 
			
		||||
                     .filter(diffusion = self.object.related) \
 | 
			
		||||
                     .order_by('position')
 | 
			
		||||
        return [ sections.ListItem(title=track.title, content=track.artist)
 | 
			
		||||
                    for track in tracks ]
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
#class DatesOfDiffusion(sections.List):
 | 
			
		||||
#    title = _('Dates of diffusion')
 | 
			
		||||
#
 | 
			
		||||
#    def get_object_list(self):
 | 
			
		||||
#        diffs = list(programs.Diffusion.objects. \
 | 
			
		||||
#            filter(initial = self.object.related). \
 | 
			
		||||
#            exclude(type = programs.Diffusion.Type.unconfirmed)
 | 
			
		||||
#        )
 | 
			
		||||
#        diffs.append(self.object.related)
 | 
			
		||||
#
 | 
			
		||||
#        items = []
 | 
			
		||||
#        for diff in sorted(diffs, key = lambda d: d.date, reverse = True):
 | 
			
		||||
#            info = ''
 | 
			
		||||
#            if diff.initial:
 | 
			
		||||
#                info = _('rerun')
 | 
			
		||||
#            if diff.type == programs.Diffusion.Type.canceled:
 | 
			
		||||
#                info += ' ' + _('canceled')
 | 
			
		||||
#            items.append(
 | 
			
		||||
#                sections.List.Item(None, diff.start.strftime('%c'), info, None,
 | 
			
		||||
#                                   'canceled')
 | 
			
		||||
#            )
 | 
			
		||||
#        return items
 | 
			
		||||
#
 | 
			
		||||
## TODO sounds
 | 
			
		||||
#
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user