work on pages, filters, lists
This commit is contained in:
parent
c68e21ee57
commit
215a6ac331
|
@ -11,11 +11,8 @@ __all__ = ['ArticleAdmin']
|
||||||
|
|
||||||
@admin.register(Article)
|
@admin.register(Article)
|
||||||
class ArticleAdmin(PageAdmin):
|
class ArticleAdmin(PageAdmin):
|
||||||
list_display = PageAdmin.list_display + ('program',)
|
list_filter = PageAdmin.list_filter
|
||||||
list_filter = PageAdmin.list_filter + ('program',)
|
search_fields = PageAdmin.search_fields + ['parent__title']
|
||||||
search_fields = PageAdmin.search_fields + ['program__title']
|
|
||||||
# TODO: readonly field
|
# TODO: readonly field
|
||||||
|
|
||||||
fieldsets = copy.deepcopy(PageAdmin.fieldsets)
|
|
||||||
fieldsets[1][1]['fields'].insert(0, 'program')
|
|
||||||
|
|
||||||
|
|
|
@ -48,13 +48,11 @@ class DiffusionInline(DiffusionBaseAdmin, admin.TabularInline):
|
||||||
|
|
||||||
@admin.register(Episode)
|
@admin.register(Episode)
|
||||||
class EpisodeAdmin(PageAdmin):
|
class EpisodeAdmin(PageAdmin):
|
||||||
list_display = PageAdmin.list_display + ('program',)
|
list_display = PageAdmin.list_display
|
||||||
list_filter = PageAdmin.list_filter + ('program',)
|
list_filter = PageAdmin.list_filter
|
||||||
search_fields = PageAdmin.search_fields + ['program__title']
|
search_fields = PageAdmin.search_fields + ['parent__title']
|
||||||
readonly_fields = ('program',)
|
# readonly_fields = ('parent',)
|
||||||
|
|
||||||
fieldsets = copy.deepcopy(PageAdmin.fieldsets)
|
|
||||||
fieldsets[1][1]['fields'].insert(0, 'program')
|
|
||||||
inlines = [TracksInline, SoundInline, DiffusionInline]
|
inlines = [TracksInline, SoundInline, DiffusionInline]
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ class CategoryAdmin(admin.ModelAdmin):
|
||||||
|
|
||||||
# limit category choice
|
# limit category choice
|
||||||
class PageAdmin(admin.ModelAdmin):
|
class PageAdmin(admin.ModelAdmin):
|
||||||
list_display = ('cover_thumb', 'title', 'status', 'category')
|
list_display = ('cover_thumb', 'title', 'status', 'category', 'parent')
|
||||||
list_display_links = ('cover_thumb', 'title')
|
list_display_links = ('cover_thumb', 'title')
|
||||||
list_editable = ('status', 'category')
|
list_editable = ('status', 'category')
|
||||||
list_filter = ('status', 'category')
|
list_filter = ('status', 'category')
|
||||||
|
@ -33,7 +33,7 @@ class PageAdmin(admin.ModelAdmin):
|
||||||
'fields': ['title', 'slug', 'category', 'cover', 'content'],
|
'fields': ['title', 'slug', 'category', 'cover', 'content'],
|
||||||
}),
|
}),
|
||||||
(_('Publication Settings'), {
|
(_('Publication Settings'), {
|
||||||
'fields': ['featured', 'allow_comments', 'status'],
|
'fields': ['featured', 'allow_comments', 'status', 'parent'],
|
||||||
'classes': ('collapse',),
|
'classes': ('collapse',),
|
||||||
}),
|
}),
|
||||||
]
|
]
|
||||||
|
|
|
@ -33,7 +33,8 @@ class WeekConverter:
|
||||||
return datetime.datetime.strptime(value + '/1', '%G/%V/%u').date()
|
return datetime.datetime.strptime(value + '/1', '%G/%V/%u').date()
|
||||||
|
|
||||||
def to_url(self, value):
|
def to_url(self, value):
|
||||||
return '{:04d}/{:02d}'.format(*value.isocalendar())
|
return value if isinstance(value, str) else \
|
||||||
|
'{:04d}/{:02d}'.format(*value.isocalendar())
|
||||||
|
|
||||||
|
|
||||||
class DateConverter:
|
class DateConverter:
|
||||||
|
@ -41,8 +42,9 @@ class DateConverter:
|
||||||
regex = r'[0-9]{4}/[0-9]{2}/[0-9]{2}'
|
regex = r'[0-9]{4}/[0-9]{2}/[0-9]{2}'
|
||||||
|
|
||||||
def to_python(self, value):
|
def to_python(self, value):
|
||||||
return str_to_date(value)
|
value = value.split('/')[:3]
|
||||||
|
return datetime.date(int(value[0]), int(value[1]), int(value[2]))
|
||||||
|
|
||||||
def to_url(self, value):
|
def to_url(self, value):
|
||||||
return '{:04d}/{:02d}/{:02d}'.format(value.year, value.month,
|
return value if isinstance(value, str) else \
|
||||||
value.day)
|
'{:04d}/{:02d}/{:02d}'.format(value.year, value.month, value.day)
|
||||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -2,28 +2,20 @@ from django.db import models
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
|
|
||||||
from .page import Page, PageQuerySet
|
from .page import Page, PageQuerySet
|
||||||
from .program import Program, InProgramQuerySet
|
from .program import Program, ProgramChildQuerySet
|
||||||
|
|
||||||
|
|
||||||
class ArticleQuerySet(InProgramQuerySet, PageQuerySet):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Article(Page):
|
class Article(Page):
|
||||||
detail_url_name = 'article-detail'
|
detail_url_name = 'article-detail'
|
||||||
|
|
||||||
program = models.ForeignKey(
|
|
||||||
Program, models.SET_NULL,
|
|
||||||
verbose_name=_('program'), blank=True, null=True,
|
|
||||||
help_text=_("publish as this program's article"),
|
|
||||||
)
|
|
||||||
is_static = models.BooleanField(
|
is_static = models.BooleanField(
|
||||||
_('is static'), default=False,
|
_('is static'), default=False,
|
||||||
help_text=_('Should this article be considered as a page '
|
help_text=_('Should this article be considered as a page '
|
||||||
'instead of a blog article'),
|
'instead of a blog article'),
|
||||||
)
|
)
|
||||||
|
|
||||||
objects = ArticleQuerySet.as_manager()
|
objects = ProgramChildQuerySet.as_manager()
|
||||||
|
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('Article')
|
verbose_name = _('Article')
|
||||||
|
|
|
@ -8,7 +8,7 @@ from django.utils.functional import cached_property
|
||||||
|
|
||||||
|
|
||||||
from aircox import settings, utils
|
from aircox import settings, utils
|
||||||
from .program import Program, InProgramQuerySet, \
|
from .program import Program, ProgramChildQuerySet, \
|
||||||
BaseRerun, BaseRerunQuerySet
|
BaseRerun, BaseRerunQuerySet
|
||||||
from .page import Page, PageQuerySet
|
from .page import Page, PageQuerySet
|
||||||
|
|
||||||
|
@ -16,18 +16,18 @@ from .page import Page, PageQuerySet
|
||||||
__all__ = ['Episode', 'Diffusion', 'DiffusionQuerySet']
|
__all__ = ['Episode', 'Diffusion', 'DiffusionQuerySet']
|
||||||
|
|
||||||
|
|
||||||
class EpisodeQuerySet(PageQuerySet, InProgramQuerySet):
|
|
||||||
pass
|
|
||||||
|
|
||||||
|
|
||||||
class Episode(Page):
|
class Episode(Page):
|
||||||
program = models.ForeignKey(
|
objects = ProgramChildQuerySet.as_manager()
|
||||||
Program, models.CASCADE,
|
|
||||||
verbose_name=_('program'),
|
|
||||||
)
|
|
||||||
|
|
||||||
objects = EpisodeQuerySet.as_manager()
|
|
||||||
detail_url_name = 'episode-detail'
|
detail_url_name = 'episode-detail'
|
||||||
|
item_template_name = 'aircox/episode_item.html'
|
||||||
|
|
||||||
|
@property
|
||||||
|
def program(self):
|
||||||
|
return getattr(self.parent, 'program', None)
|
||||||
|
|
||||||
|
@program.setter
|
||||||
|
def program(self, value):
|
||||||
|
self.parent = value
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('Episode')
|
verbose_name = _('Episode')
|
||||||
|
@ -41,6 +41,8 @@ class Episode(Page):
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
if self.cover is None:
|
if self.cover is None:
|
||||||
self.cover = self.program.cover
|
self.cover = self.program.cover
|
||||||
|
if self.parent is None:
|
||||||
|
raise ValueError('missing parent program')
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
@classmethod
|
@classmethod
|
||||||
|
@ -155,6 +157,8 @@ class Diffusion(BaseRerun):
|
||||||
# help_text = _('use this input port'),
|
# help_text = _('use this input port'),
|
||||||
# )
|
# )
|
||||||
|
|
||||||
|
item_template_name = 'aircox/diffusion_item.html'
|
||||||
|
|
||||||
class Meta:
|
class Meta:
|
||||||
verbose_name = _('Diffusion')
|
verbose_name = _('Diffusion')
|
||||||
verbose_name_plural = _('Diffusions')
|
verbose_name_plural = _('Diffusions')
|
||||||
|
|
|
@ -46,6 +46,11 @@ class PageQuerySet(InheritanceQuerySet):
|
||||||
def trash(self):
|
def trash(self):
|
||||||
return self.filter(status=Page.STATUS_TRASH)
|
return self.filter(status=Page.STATUS_TRASH)
|
||||||
|
|
||||||
|
def parent(self, parent=None, id=None):
|
||||||
|
""" Return pages having this parent. """
|
||||||
|
return self.filter(parent=parent) if id is None else \
|
||||||
|
self.filter(parent__id=id)
|
||||||
|
|
||||||
|
|
||||||
class Page(models.Model):
|
class Page(models.Model):
|
||||||
""" Base class for publishable content """
|
""" Base class for publishable content """
|
||||||
|
@ -58,6 +63,8 @@ class Page(models.Model):
|
||||||
(STATUS_TRASH, _('trash')),
|
(STATUS_TRASH, _('trash')),
|
||||||
)
|
)
|
||||||
|
|
||||||
|
parent = models.ForeignKey('self', models.CASCADE, blank=True, null=True,
|
||||||
|
related_name='child_set')
|
||||||
title = models.CharField(max_length=128)
|
title = models.CharField(max_length=128)
|
||||||
slug = models.SlugField(_('slug'), blank=True, unique=True)
|
slug = models.SlugField(_('slug'), blank=True, unique=True)
|
||||||
status = models.PositiveSmallIntegerField(
|
status = models.PositiveSmallIntegerField(
|
||||||
|
@ -74,7 +81,7 @@ class Page(models.Model):
|
||||||
content = RichTextField(
|
content = RichTextField(
|
||||||
_('content'), blank=True, null=True,
|
_('content'), blank=True, null=True,
|
||||||
)
|
)
|
||||||
date = models.DateTimeField(default=tz.now)
|
pub_date = models.DateTimeField(blank=True, null=True)
|
||||||
featured = models.BooleanField(
|
featured = models.BooleanField(
|
||||||
_('featured'), default=False,
|
_('featured'), default=False,
|
||||||
)
|
)
|
||||||
|
@ -85,17 +92,19 @@ class Page(models.Model):
|
||||||
objects = PageQuerySet.as_manager()
|
objects = PageQuerySet.as_manager()
|
||||||
|
|
||||||
detail_url_name = None
|
detail_url_name = None
|
||||||
|
item_template_name = 'aircox/page_item.html'
|
||||||
|
|
||||||
def __str__(self):
|
def __str__(self):
|
||||||
return '{}: {}'.format(self._meta.verbose_name,
|
return '{}'.format(self.title or self.pk)
|
||||||
self.title or self.pk)
|
|
||||||
|
|
||||||
def save(self, *args, **kwargs):
|
def save(self, *args, **kwargs):
|
||||||
# TODO: ensure unique slug
|
# TODO: ensure unique slug
|
||||||
if not self.slug:
|
if not self.slug:
|
||||||
self.slug = slugify(self.title)
|
self.slug = slugify(self.title)
|
||||||
print(self.title, '--', self.slug)
|
if self.is_published and self.pub_date is None:
|
||||||
|
self.pub_date = tz.datetime.now()
|
||||||
|
elif not self.is_published:
|
||||||
|
self.pub_date = None
|
||||||
super().save(*args, **kwargs)
|
super().save(*args, **kwargs)
|
||||||
|
|
||||||
def get_absolute_url(self):
|
def get_absolute_url(self):
|
||||||
|
|
|
@ -23,7 +23,8 @@ from .station import Station
|
||||||
logger = logging.getLogger('aircox')
|
logger = logging.getLogger('aircox')
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['Program', 'ProgramQuerySet', 'Stream', 'Schedule']
|
__all__ = ['Program', 'ProgramQuerySet', 'Stream', 'Schedule',
|
||||||
|
'ProgramChildQuerySet', 'BaseRerun', 'BaseRerunQuerySet']
|
||||||
|
|
||||||
|
|
||||||
class ProgramQuerySet(PageQuerySet):
|
class ProgramQuerySet(PageQuerySet):
|
||||||
|
@ -49,15 +50,8 @@ class Program(Page):
|
||||||
name if it does not exists.
|
name if it does not exists.
|
||||||
"""
|
"""
|
||||||
# explicit foreign key in order to avoid related name clashes
|
# explicit foreign key in order to avoid related name clashes
|
||||||
page = models.OneToOneField(
|
station = models.ForeignKey(Station, models.CASCADE,
|
||||||
Page, models.CASCADE,
|
verbose_name=_('station'))
|
||||||
parent_link=True, related_name='program_page'
|
|
||||||
)
|
|
||||||
station = models.ForeignKey(
|
|
||||||
Station,
|
|
||||||
verbose_name=_('station'),
|
|
||||||
on_delete=models.CASCADE,
|
|
||||||
)
|
|
||||||
active = models.BooleanField(
|
active = models.BooleanField(
|
||||||
_('active'),
|
_('active'),
|
||||||
default=True,
|
default=True,
|
||||||
|
@ -146,10 +140,17 @@ class Program(Page):
|
||||||
.update(path=Concat('path', Substr(F('path'), len(path_))))
|
.update(path=Concat('path', Substr(F('path'), len(path_))))
|
||||||
|
|
||||||
|
|
||||||
class InProgramQuerySet(models.QuerySet):
|
class ProgramChildQuerySet(PageQuerySet):
|
||||||
"""
|
def station(self, station=None, id=None):
|
||||||
Queryset for model having a ForeignKey field "program" to `Program`.
|
return self.filter(parent__program__station=station) if id is None else \
|
||||||
"""
|
self.filter(parent__program__station__id=id)
|
||||||
|
|
||||||
|
def program(self, program=None, id=None):
|
||||||
|
return self.parent(program, id)
|
||||||
|
|
||||||
|
|
||||||
|
class BaseRerunQuerySet(models.QuerySet):
|
||||||
|
""" Queryset for BaseRerun (sub)classes. """
|
||||||
def station(self, station=None, id=None):
|
def station(self, station=None, id=None):
|
||||||
return self.filter(program__station=station) if id is None else \
|
return self.filter(program__station=station) if id is None else \
|
||||||
self.filter(program__station__id=id)
|
self.filter(program__station__id=id)
|
||||||
|
@ -158,9 +159,6 @@ class InProgramQuerySet(models.QuerySet):
|
||||||
return self.filter(program=program) if id is None else \
|
return self.filter(program=program) if id is None else \
|
||||||
self.filter(program__id=id)
|
self.filter(program__id=id)
|
||||||
|
|
||||||
|
|
||||||
class BaseRerunQuerySet(InProgramQuerySet):
|
|
||||||
""" Queryset for BaseRerun (sub)classes. """
|
|
||||||
def rerun(self):
|
def rerun(self):
|
||||||
return self.filter(initial__isnull=False)
|
return self.filter(initial__isnull=False)
|
||||||
|
|
||||||
|
|
|
@ -93,7 +93,7 @@ def schedule_pre_delete(sender, instance, *args, **kwargs):
|
||||||
|
|
||||||
@receiver(signals.post_delete, sender=Diffusion)
|
@receiver(signals.post_delete, sender=Diffusion)
|
||||||
def diffusion_post_delete(sender, instance, *args, **kwargs):
|
def diffusion_post_delete(sender, instance, *args, **kwargs):
|
||||||
Episode.objects.filter(diffusion__isnull=True, content_isnull=True,
|
Episode.objects.filter(diffusion__isnull=True, content__isnull=True,
|
||||||
sound__isnull=True) \
|
sound__isnull=True) \
|
||||||
.delete()
|
.delete()
|
||||||
|
|
||||||
|
|
|
@ -16,6 +16,9 @@
|
||||||
width: 100%;
|
width: 100%;
|
||||||
margin: 1em 0em; }
|
margin: 1em 0em; }
|
||||||
|
|
||||||
|
ul.menu-list li {
|
||||||
|
list-style-type: none; }
|
||||||
|
|
||||||
@keyframes spinAround {
|
@keyframes spinAround {
|
||||||
from {
|
from {
|
||||||
transform: rotate(0deg); }
|
transform: rotate(0deg); }
|
||||||
|
@ -7180,6 +7183,11 @@ label.panel-block {
|
||||||
.has-background-transparent {
|
.has-background-transparent {
|
||||||
background-color: transparent; }
|
background-color: transparent; }
|
||||||
|
|
||||||
|
.is-opacity-light {
|
||||||
|
opacity: 0.7; }
|
||||||
|
.is-opacity-light:hover {
|
||||||
|
opacity: 1; }
|
||||||
|
|
||||||
.navbar + .container {
|
.navbar + .container {
|
||||||
margin-top: 1em; }
|
margin-top: 1em; }
|
||||||
|
|
||||||
|
@ -7192,6 +7200,29 @@ a.navbar-item.is-active {
|
||||||
.navbar .navbar-dropdown {
|
.navbar .navbar-dropdown {
|
||||||
z-index: 2000; }
|
z-index: 2000; }
|
||||||
|
|
||||||
|
.navbar .navbar-split {
|
||||||
|
margin: 0.2em 0em;
|
||||||
|
margin-right: 1em;
|
||||||
|
padding-right: 1em;
|
||||||
|
border-right: 1px #b5b5b5 solid;
|
||||||
|
display: inline-block; }
|
||||||
|
|
||||||
|
.navbar form {
|
||||||
|
margin: 0em;
|
||||||
|
padding: 0em; }
|
||||||
|
|
||||||
|
.filters {
|
||||||
|
margin: 1em 0em;
|
||||||
|
background-color: transparent;
|
||||||
|
margin-bottom: 1em; }
|
||||||
|
.filters .title {
|
||||||
|
padding-right: 2em;
|
||||||
|
margin-right: 1em;
|
||||||
|
border-right: 1px #b5b5b5 solid;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
color: #7a7a7a;
|
||||||
|
font-weight: 300; }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
.navbar-brand img {
|
.navbar-brand img {
|
||||||
min-height: 6em;
|
min-height: 6em;
|
||||||
|
|
|
@ -305,7 +305,7 @@ eval("// extracted by mini-css-extract-plugin\n\n//# sourceURL=webpack:///./asse
|
||||||
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
/***/ (function(module, __webpack_exports__, __webpack_require__) {
|
||||||
|
|
||||||
"use strict";
|
"use strict";
|
||||||
eval("__webpack_require__.r(__webpack_exports__);\n//\n//\n//\n//\n//\n//\n\n\nconst splitReg = new RegExp(`,\\s*`, 'g');\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n data() {\n return {\n counts: {},\n }\n },\n\n methods: {\n update() {\n const items = this.$el.querySelectorAll('input[name=\"data\"]:checked')\n const counts = {};\n\n console.log(items)\n for(var item of items)\n if(item.value)\n for(var tag of item.value.split(splitReg))\n counts[tag.trim()] = (counts[tag.trim()] || 0) + 1;\n this.counts = counts;\n console.log('counts', this.counts)\n }\n },\n\n mounted() {\n this.$refs.form.addEventListener('change', () => this.update())\n this.update()\n }\n});\n\n\n//# sourceURL=webpack:///./assets/admin/statistics.vue?./node_modules/vue-loader/lib??vue-loader-options");
|
eval("__webpack_require__.r(__webpack_exports__);\n//\n//\n//\n//\n//\n//\n\n\nconst splitReg = new RegExp(`,\\s*`, 'g');\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n data() {\n return {\n counts: {},\n }\n },\n\n methods: {\n update() {\n const items = this.$el.querySelectorAll('input[name=\"data\"]:checked')\n const counts = {};\n\n console.log(items)\n for(var item of items)\n if(item.value)\n for(var tag of item.value.split(splitReg))\n counts[tag.trim()] = (counts[tag.trim()] || 0) + 1;\n this.counts = counts;\n }\n },\n\n mounted() {\n this.$refs.form.addEventListener('change', () => this.update())\n this.update()\n }\n});\n\n\n//# sourceURL=webpack:///./assets/admin/statistics.vue?./node_modules/vue-loader/lib??vue-loader-options");
|
||||||
|
|
||||||
/***/ }),
|
/***/ }),
|
||||||
|
|
||||||
|
|
|
@ -7162,6 +7162,11 @@ label.panel-block {
|
||||||
.has-background-transparent {
|
.has-background-transparent {
|
||||||
background-color: transparent; }
|
background-color: transparent; }
|
||||||
|
|
||||||
|
.is-opacity-light {
|
||||||
|
opacity: 0.7; }
|
||||||
|
.is-opacity-light:hover {
|
||||||
|
opacity: 1; }
|
||||||
|
|
||||||
.navbar + .container {
|
.navbar + .container {
|
||||||
margin-top: 1em; }
|
margin-top: 1em; }
|
||||||
|
|
||||||
|
@ -7174,6 +7179,29 @@ a.navbar-item.is-active {
|
||||||
.navbar .navbar-dropdown {
|
.navbar .navbar-dropdown {
|
||||||
z-index: 2000; }
|
z-index: 2000; }
|
||||||
|
|
||||||
|
.navbar .navbar-split {
|
||||||
|
margin: 0.2em 0em;
|
||||||
|
margin-right: 1em;
|
||||||
|
padding-right: 1em;
|
||||||
|
border-right: 1px #b5b5b5 solid;
|
||||||
|
display: inline-block; }
|
||||||
|
|
||||||
|
.navbar form {
|
||||||
|
margin: 0em;
|
||||||
|
padding: 0em; }
|
||||||
|
|
||||||
|
.filters {
|
||||||
|
margin: 1em 0em;
|
||||||
|
background-color: transparent;
|
||||||
|
margin-bottom: 1em; }
|
||||||
|
.filters .title {
|
||||||
|
padding-right: 2em;
|
||||||
|
margin-right: 1em;
|
||||||
|
border-right: 1px #b5b5b5 solid;
|
||||||
|
font-size: 1.25rem;
|
||||||
|
color: #7a7a7a;
|
||||||
|
font-weight: 300; }
|
||||||
|
|
||||||
/*
|
/*
|
||||||
.navbar-brand img {
|
.navbar-brand img {
|
||||||
min-height: 6em;
|
min-height: 6em;
|
||||||
|
|
|
@ -4,7 +4,9 @@
|
||||||
|
|
||||||
{% block content %}{{ block.super }}
|
{% block content %}{{ block.super }}
|
||||||
{# TODO: date subtitle #}
|
{# TODO: date subtitle #}
|
||||||
<a-statistics>
|
<div class="columns">
|
||||||
|
|
||||||
|
<a-statistics class="column">
|
||||||
<template v-slot:default="{counts}">
|
<template v-slot:default="{counts}">
|
||||||
<table class="table is-hoverable is-fullwidth">
|
<table class="table is-hoverable is-fullwidth">
|
||||||
<thead>
|
<thead>
|
||||||
|
@ -63,6 +65,15 @@
|
||||||
</template>
|
</template>
|
||||||
</a-statistics>
|
</a-statistics>
|
||||||
|
|
||||||
|
|
||||||
|
<nav class="column menu is-one-fifth-desktop" role="menu">
|
||||||
|
{% with "admin:tools-stats" as url_name %}
|
||||||
|
{% include "aircox/widgets/dates_menu.html" %}
|
||||||
|
{% endwith %}
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -46,7 +46,7 @@
|
||||||
<a class="navbar-link" href="{% url "admin:aircox_article_changelist" %}">{% trans "Articles" %}</a>
|
<a class="navbar-link" href="{% url "admin:aircox_article_changelist" %}">{% trans "Articles" %}</a>
|
||||||
<div class="navbar-dropdown is-boxed is-right">
|
<div class="navbar-dropdown is-boxed is-right">
|
||||||
{% for program in programs %}
|
{% for program in programs %}
|
||||||
<a class="navbar-item" href="{% url "admin:aircox_article_changelist" %}?program={{ program.pk }}">
|
<a class="navbar-item" href="{% url "admin:aircox_article_changelist" %}?parent={{ program.pk }}">
|
||||||
{{ program.title }}</a>
|
{{ program.title }}</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
@ -56,7 +56,7 @@
|
||||||
<a class="navbar-link" href="{% url "admin:aircox_episode_changelist" %}">{% trans "Episodes" %}</a>
|
<a class="navbar-link" href="{% url "admin:aircox_episode_changelist" %}">{% trans "Episodes" %}</a>
|
||||||
<div class="navbar-dropdown is-boxed is-right">
|
<div class="navbar-dropdown is-boxed is-right">
|
||||||
{% for program in programs %}
|
{% for program in programs %}
|
||||||
<a class="navbar-item" href="{% url "admin:aircox_episode_changelist" %}?program={{ program.pk }}">
|
<a class="navbar-item" href="{% url "admin:aircox_episode_changelist" %}?parent={{ program.pk }}">
|
||||||
{{ program.title }}</a>
|
{{ program.title }}</a>
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
{% extends "aircox/page_detail.html" %}
|
{% extends "aircox/page_detail.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block side_nav %}
|
{% block sidebar %}
|
||||||
{{ block.super }}
|
{{ block.super }}
|
||||||
|
|
||||||
{% if side_items %}
|
{% if sidebar_items %}
|
||||||
<section>
|
<section>
|
||||||
<h4 class="title is-4">{% trans "Latest news" %}</h4>
|
<h4 class="title is-4">{% trans "Latest news" %}</h4>
|
||||||
|
|
||||||
{% for object in side_items %}
|
{% for object in sidebar_items %}
|
||||||
{% include "aircox/page_item.html" %}
|
{% include "aircox/page_item.html" %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@
|
||||||
Context:
|
Context:
|
||||||
- cover: image cover
|
- cover: image cover
|
||||||
- site: current website
|
- site: current website
|
||||||
|
- has_filters: display filter bar (using block "filters")
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
|
@ -71,19 +72,50 @@ Context:
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</header>
|
</header>
|
||||||
|
|
||||||
|
{% if has_filters %}
|
||||||
|
<nav class="navbar filters"
|
||||||
|
aria-label="{% trans "list filters" %}">
|
||||||
|
{% block filters %}{% endblock %}
|
||||||
|
</nav>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
{% block main %}{% endblock main %}
|
{% block main %}{% endblock main %}
|
||||||
</main>
|
</main>
|
||||||
|
|
||||||
{% if show_side_nav %}
|
{% if has_sidebar %}
|
||||||
<aside class="column is-one-third-desktop">
|
<aside class="column is-one-third-desktop">
|
||||||
{# FIXME: block cover into side_nav one #}
|
{# FIXME: block cover into sidebar one #}
|
||||||
{% block cover %}
|
{% block cover %}
|
||||||
{% if cover is not None %}
|
{% if cover is not None %}
|
||||||
<img class="cover" src="{{ cover.url }}" class="cover"/>
|
<img class="cover" src="{{ cover.url }}" class="cover"/>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block side_nav %}
|
{% block sidebar %}
|
||||||
|
{% if sidebar_items %}
|
||||||
|
<section>
|
||||||
|
<h4 class="title is-4">
|
||||||
|
{% block sidebar_title %}{% trans "Recently" %}{% endblock %}
|
||||||
|
</h4>
|
||||||
|
|
||||||
|
{% for object in sidebar_items %}
|
||||||
|
{% include "aircox/episode_item.html" %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<br>
|
||||||
|
<nav class="pagination is-centered">
|
||||||
|
<ul class="pagination-list">
|
||||||
|
<li>
|
||||||
|
<a {% if parent %}href="{% url "page-list" parent_slug=parent.slug %}"{% else %}href="{% url "page-list" %}"{% endif %}
|
||||||
|
class="pagination-link"
|
||||||
|
aria-label="{% trans "Show all program's diffusions" %}">
|
||||||
|
{% trans "Show more" %}
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</nav>
|
||||||
|
{% endif %}
|
||||||
|
</section>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
</aside>
|
</aside>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
|
|
11
aircox/templates/aircox/diffusion_item.html
Normal file
11
aircox/templates/aircox/diffusion_item.html
Normal file
|
@ -0,0 +1,11 @@
|
||||||
|
{% comment %}
|
||||||
|
Context:
|
||||||
|
- object: diffusion
|
||||||
|
- "episode_item"'s context (except object and diffusion)
|
||||||
|
{% endcomment %}
|
||||||
|
{% with object as diffusion %}
|
||||||
|
{% with object.episode as object %}
|
||||||
|
{% include "aircox/episode_item.html" %}
|
||||||
|
{% endwith %}
|
||||||
|
{% endwith %}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
{% extends "aircox/page.html" %}
|
{% extends "aircox/page.html" %}
|
||||||
{% load i18n aircox %}
|
{% load i18n aircox humanize %}
|
||||||
|
|
||||||
{% block title %}
|
{% block title %}
|
||||||
{% with station.name as station %}
|
{% with station.name as station %}
|
||||||
|
@ -9,17 +9,22 @@
|
||||||
|
|
||||||
{% block subtitle %}{{ date|date:"l d F Y" }}{% endblock %}
|
{% block subtitle %}{{ date|date:"l d F Y" }}{% endblock %}
|
||||||
|
|
||||||
{% block main %}{{ block.super }}
|
{% block filters %}
|
||||||
<div class="columns">
|
{% with "diffusion-list" as url_name %}
|
||||||
|
{% include "aircox/widgets/dates_menu.html" %}
|
||||||
|
{% endwith %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block main %}{{ block.super }}
|
||||||
{% with True as hide_schedule %}
|
{% with True as hide_schedule %}
|
||||||
<section class="column">
|
<section role="list">
|
||||||
<div id="timetable-{{ date|date:"Y-m-d" }}">
|
<div id="timetable-{{ date|date:"Y-m-d" }}">
|
||||||
{% for diffusion in object_list %}
|
{% for diffusion in object_list %}
|
||||||
<div class="columns">
|
{# FIXME: opacity should work -- maybe hidden tz #}
|
||||||
|
<div class="columns {% if diffusion.start.date != date and diffusion.start.end <= date %}is-opacity-light{% endif %}">
|
||||||
<div class="column is-one-fifth has-text-right">
|
<div class="column is-one-fifth has-text-right">
|
||||||
<time datetime="{{ diffusion.start|date:"c" }}">
|
<time datetime="{{ diffusion.start|date:"c" }}">
|
||||||
{{ diffusion.start|date:"H:i" }} - {{ diffusion.end|date:"H:i" }}
|
{{ diffusion.start|date:"d H:i" }} - {{ diffusion.end|date:"d H:i" }}
|
||||||
</time>
|
</time>
|
||||||
</div>
|
</div>
|
||||||
<div class="column">
|
<div class="column">
|
||||||
|
@ -33,12 +38,10 @@
|
||||||
</section>
|
</section>
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
|
|
||||||
|
{% comment %}
|
||||||
<nav class="column menu is-one-third-desktop" role="menu">
|
<nav class="column menu is-one-third-desktop" role="menu">
|
||||||
{% with "diffusion-list" as url_name %}
|
|
||||||
{% include "aircox/widgets/dates_menu.html" %}
|
|
||||||
{% endwith %}
|
|
||||||
</nav>
|
</nav>
|
||||||
|
{% endcomment %}
|
||||||
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -11,9 +11,7 @@ for design review.
|
||||||
|
|
||||||
{% if object|is_diffusion %}
|
{% if object|is_diffusion %}
|
||||||
{% with object as diffusion %}
|
{% with object as diffusion %}
|
||||||
{% with diffusion.episode as object %}
|
{% include "aircox/diffusion_item.html" %}
|
||||||
{% include "aircox/episode_item.html" %}
|
|
||||||
{% endwith %}
|
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
{% else %}
|
{% else %}
|
||||||
{% with object.track as object %}
|
{% with object.track as object %}
|
||||||
|
|
|
@ -10,14 +10,17 @@
|
||||||
|
|
||||||
{% block subtitle %}{{ date|date:"l d F Y" }}{% endblock %}
|
{% block subtitle %}{{ date|date:"l d F Y" }}{% endblock %}
|
||||||
|
|
||||||
|
{% block filters %}
|
||||||
|
{% with "log-list" as url_name %}
|
||||||
|
{% include "aircox/widgets/dates_menu.html" %}
|
||||||
|
{% endwith %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<div class="columns">
|
<section>
|
||||||
|
|
||||||
<section class="section column">
|
|
||||||
{# <h4 class="subtitle size-4">{{ date }}</h4> #}
|
{# <h4 class="subtitle size-4">{{ date }}</h4> #}
|
||||||
{% with True as hide_schedule %}
|
{% with True as hide_schedule %}
|
||||||
<table class="table is-striped is-hoverable is-fullwidth has-background-transparent">
|
<table class="table is-striped is-hoverable is-fullwidth">
|
||||||
{% for object in object_list %}
|
{% for object in object_list %}
|
||||||
<tr>
|
<tr>
|
||||||
<td>
|
<td>
|
||||||
|
@ -37,13 +40,5 @@
|
||||||
</table>
|
</table>
|
||||||
{% endwith %}
|
{% endwith %}
|
||||||
</section>
|
</section>
|
||||||
|
|
||||||
<nav class="column menu is-one-third-desktop" role="menu">
|
|
||||||
{% with "logs" as url_name %}
|
|
||||||
{% include "aircox/widgets/dates_menu.html" %}
|
|
||||||
{% endwith %}
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
|
@ -2,16 +2,21 @@
|
||||||
{% load i18n aircox %}
|
{% load i18n aircox %}
|
||||||
|
|
||||||
{% block title %}
|
{% block title %}
|
||||||
{{ view.model|verbose_name:True|title }}
|
{% if not parent %}{{ view.model|verbose_name:True|title }}
|
||||||
|
{% else %}
|
||||||
|
{% with parent.title as title %}
|
||||||
|
{% blocktrans %}Publications of {{ title }}{% endblocktrans %}
|
||||||
|
{% endwith %}
|
||||||
|
{% endif %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
{% block side_nav %}
|
{% block filters %}
|
||||||
{{ block.super }}
|
<div class="navbar-branding">
|
||||||
|
<h4 class="navbar-item title">{% trans "Filters" %}</h4>
|
||||||
{% if filter_categories|length != 1 %}
|
</div>
|
||||||
<section class="toolbar">
|
<form method="GET" action="" class="navbar-menu">
|
||||||
<h4 class="subtitle is-5">{% trans "Filters" %}</h4>
|
<div class="navbar-start">
|
||||||
<form method="GET" action="">
|
<div class="navbar-item">
|
||||||
{% block list_filters %}
|
{% block list_filters %}
|
||||||
<div class="field is-horizontal">
|
<div class="field is-horizontal">
|
||||||
<div class="field-label">
|
<div class="field-label">
|
||||||
|
@ -33,11 +38,10 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
<div class="field is-horizontal">
|
|
||||||
<div class="field-label">
|
|
||||||
<label class="label"></label>
|
|
||||||
</div>
|
</div>
|
||||||
<div class="field-body">
|
</div>
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item">
|
||||||
<div class="field is-grouped is-grouped-right">
|
<div class="field is-grouped is-grouped-right">
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<button class="button is-primary"/>{% trans "Apply" %}</button>
|
<button class="button is-primary"/>{% trans "Apply" %}</button>
|
||||||
|
@ -49,16 +53,14 @@
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
</section>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
{% block main %}
|
{% block main %}
|
||||||
<section>
|
<section role="list">
|
||||||
{% for object in object_list %}
|
{% for object in object_list %}
|
||||||
{% block list_object %}
|
{% block list_object %}
|
||||||
{% include item_template_name %}
|
{% include object.item_template_name|default:item_template_name %}
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</section>
|
</section>
|
||||||
|
|
|
@ -1,30 +1,16 @@
|
||||||
{% extends "aircox/page_detail.html" %}
|
{% extends "aircox/page_detail.html" %}
|
||||||
{% load i18n %}
|
{% load i18n %}
|
||||||
|
|
||||||
{% block side_nav %}
|
{% block sidebar_title %}
|
||||||
{{ block.super }}
|
{% with program.title as program %}
|
||||||
|
{% blocktrans %} Recently on {{ program }}{% endblocktrans %}
|
||||||
{% if side_items %}
|
{% endwith %}
|
||||||
<section>
|
|
||||||
<h4 class="title is-4">{% trans "Last shows" %}</h4>
|
|
||||||
|
|
||||||
{% for object in side_items %}
|
|
||||||
{% include "aircox/episode_item.html" %}
|
|
||||||
{% endfor %}
|
|
||||||
|
|
||||||
<br>
|
|
||||||
<nav class="pagination is-centered">
|
|
||||||
<ul class="pagination-list">
|
|
||||||
<li>
|
|
||||||
<a href="{% url "episode-list" parent_slug=program.slug %}"
|
|
||||||
class="pagination-link"
|
|
||||||
aria-label="{% trans "Show all program's diffusions" %}">
|
|
||||||
{% trans "More shows" %}
|
|
||||||
</a>
|
|
||||||
</li>
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
</section>
|
|
||||||
{% endif %}
|
|
||||||
{% endblock %}
|
{% endblock %}
|
||||||
|
|
||||||
|
{% block sidebar %}
|
||||||
|
{% with program as parent %}
|
||||||
|
{{ block.super }}
|
||||||
|
{% endwith %}
|
||||||
|
{% endblock %}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,9 +4,9 @@ Context:
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
|
|
||||||
<span class="has-text-info is-size-5">♬</span>
|
<span class="has-text-info is-size-5">♬</span>
|
||||||
<span>{{ track.title }}</span>
|
<span>{{ object.title }}</span>
|
||||||
<span class="has-text-grey-dark has-text-weight-light">
|
<span class="has-text-grey-dark has-text-weight-light">
|
||||||
— {{ track.artist }}
|
— {{ object.artist }}
|
||||||
{% if track.info %}(<i>{{ track.info }}</i>){% endif %}
|
{% if object.info %}(<i>{{ object.info }}</i>){% endif %}
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
|
|
@ -9,12 +9,21 @@ Context:
|
||||||
|
|
||||||
An empty date results to a title or a separator
|
An empty date results to a title or a separator
|
||||||
{% endcomment %}
|
{% endcomment %}
|
||||||
{% load i18n humanize %}
|
{% load i18n %}
|
||||||
|
|
||||||
<nav class="menu is-one-third-desktop " role="menu"
|
<div class="navbar-menu" role="menu"
|
||||||
aria-label="{% trans "pick a date" %}">
|
aria-label="{% trans "pick a date" %}">
|
||||||
<p class="menu-label">{% trans "Pick a date" %}</p>
|
<div class="navbar-start">
|
||||||
<form action="{% url url_name %}" method="GET"
|
{% for day in dates %}
|
||||||
|
<a href="{% url url_name date=day %}" class="navbar-item {% if day == date %}is-active{% endif %}">
|
||||||
|
{{ day|date:"D. d" }}
|
||||||
|
</a>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="navbar-end">
|
||||||
|
<div class="navbar-item">
|
||||||
|
<form action="{% url url_name %}" method="GET" class="navbar-body"
|
||||||
aria-label="{% trans "Jump to date" %}">
|
aria-label="{% trans "Jump to date" %}">
|
||||||
<div class="field has-addons">
|
<div class="field has-addons">
|
||||||
<div class="control has-icons-left">
|
<div class="control has-icons-left">
|
||||||
|
@ -27,22 +36,8 @@ An empty date results to a title or a separator
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</form>
|
</form>
|
||||||
|
</div>
|
||||||
<ul class="menu-list">
|
</div>
|
||||||
{% for day, title in dates %}
|
</div>
|
||||||
{% if not day %}
|
|
||||||
{% if title %}
|
|
||||||
</ul>
|
|
||||||
<p class="menu-label">{{ title }}</p>
|
|
||||||
<ul class="menu-list">
|
|
||||||
{% else %}<hr class="dropdown-divider">{% endif %}
|
|
||||||
{% else %}
|
|
||||||
<li><a href="{% url url_name date=day %}" {% if day == date %}class="is-active"{% endif %}>
|
|
||||||
{% if title %}{{ title }}{% else %}{{ day|naturalday:"l d" }}{% endif %}
|
|
||||||
</a></li>
|
|
||||||
{% endif %}
|
|
||||||
{% endfor %}
|
|
||||||
</ul>
|
|
||||||
</nav>
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,6 +24,8 @@ api = [
|
||||||
|
|
||||||
|
|
||||||
urls = [
|
urls = [
|
||||||
|
path(_(''),
|
||||||
|
views.DiffusionListView.as_view(), name='home'),
|
||||||
path('api/', include(api)),
|
path('api/', include(api)),
|
||||||
|
|
||||||
# path('', views.PageDetailView.as_view(model=models.Article),
|
# path('', views.PageDetailView.as_view(model=models.Article),
|
||||||
|
@ -35,15 +37,6 @@ urls = [
|
||||||
views.ArticleDetailView.as_view(),
|
views.ArticleDetailView.as_view(),
|
||||||
name='article-detail'),
|
name='article-detail'),
|
||||||
|
|
||||||
path(_('programs/'), views.PageListView.as_view(model=models.Program),
|
|
||||||
name='program-list'),
|
|
||||||
path(_('programs/<slug:slug>/'),
|
|
||||||
views.ProgramDetailView.as_view(), name='program-detail'),
|
|
||||||
path(_('programs/<slug:parent_slug>/episodes/'),
|
|
||||||
views.EpisodeListView.as_view(), name='episode-list'),
|
|
||||||
path(_('programs/<slug:parent_slug>/articles/'),
|
|
||||||
views.ArticleListView.as_view(), name='article-list'),
|
|
||||||
|
|
||||||
path(_('episodes/'),
|
path(_('episodes/'),
|
||||||
views.EpisodeListView.as_view(), name='episode-list'),
|
views.EpisodeListView.as_view(), name='episode-list'),
|
||||||
path(_('episodes/<slug:slug>/'),
|
path(_('episodes/<slug:slug>/'),
|
||||||
|
@ -53,7 +46,23 @@ urls = [
|
||||||
path(_('week/<date:date>/'),
|
path(_('week/<date:date>/'),
|
||||||
views.DiffusionListView.as_view(), name='diffusion-list'),
|
views.DiffusionListView.as_view(), name='diffusion-list'),
|
||||||
|
|
||||||
path(_('logs/'), views.LogListView.as_view(), name='logs'),
|
path(_('logs/'), views.LogListView.as_view(), name='log-list'),
|
||||||
path(_('logs/<date:date>/'), views.LogListView.as_view(), name='logs'),
|
path(_('logs/<date:date>/'), views.LogListView.as_view(), name='log-list'),
|
||||||
# path('<page_path:path>', views.route_page, name='page'),
|
# path('<page_path:path>', views.route_page, name='page'),
|
||||||
|
|
||||||
|
path(_('publications/'),
|
||||||
|
views.ProgramPageListView.as_view(), name='page-list'),
|
||||||
|
|
||||||
|
path(_('programs/'), views.PageListView.as_view(model=models.Program),
|
||||||
|
name='program-list'),
|
||||||
|
path(_('programs/<slug:slug>/'),
|
||||||
|
views.ProgramDetailView.as_view(), name='program-detail'),
|
||||||
|
path(_('programs/<slug:parent_slug>/episodes/'),
|
||||||
|
views.EpisodeListView.as_view(), name='episode-list'),
|
||||||
|
path(_('programs/<slug:parent_slug>/articles/'),
|
||||||
|
views.ArticleListView.as_view(), name='article-list'),
|
||||||
|
path(_('programs/<slug:parent_slug>/publications/'),
|
||||||
|
views.ProgramPageListView.as_view(), name='page-list'),
|
||||||
|
|
||||||
|
|
||||||
]
|
]
|
||||||
|
|
|
@ -5,5 +5,5 @@ from .base import BaseView
|
||||||
from .episode import EpisodeDetailView, EpisodeListView, DiffusionListView
|
from .episode import EpisodeDetailView, EpisodeListView, DiffusionListView
|
||||||
from .log import LogListView
|
from .log import LogListView
|
||||||
from .page import PageDetailView, PageListView
|
from .page import PageDetailView, PageListView
|
||||||
from .program import ProgramDetailView
|
from .program import ProgramDetailView, ProgramPageListView
|
||||||
|
|
||||||
|
|
|
@ -49,6 +49,9 @@ class AdminSite(admin.AdminSite):
|
||||||
path('tools/statistics/',
|
path('tools/statistics/',
|
||||||
self.admin_view(StatisticsView.as_view()),
|
self.admin_view(StatisticsView.as_view()),
|
||||||
name='tools-stats'),
|
name='tools-stats'),
|
||||||
|
path('tools/statistics/<date:date>/',
|
||||||
|
self.admin_view(StatisticsView.as_view()),
|
||||||
|
name='tools-stats'),
|
||||||
]
|
]
|
||||||
return urls
|
return urls
|
||||||
|
|
||||||
|
|
|
@ -7,10 +7,10 @@ __all__ = ['ArticleDetailView', 'ArticleListView']
|
||||||
|
|
||||||
|
|
||||||
class ArticleDetailView(PageDetailView):
|
class ArticleDetailView(PageDetailView):
|
||||||
show_side_nav = True
|
has_sidebar = True
|
||||||
model = Article
|
model = Article
|
||||||
|
|
||||||
def get_side_queryset(self):
|
def get_sidebar_queryset(self):
|
||||||
qs = Article.objects.select_related('cover') \
|
qs = Article.objects.select_related('cover') \
|
||||||
.filter(is_static=False) \
|
.filter(is_static=False) \
|
||||||
.order_by('-date')
|
.order_by('-date')
|
||||||
|
@ -27,9 +27,7 @@ class ArticleListView(ParentMixin, PageListView):
|
||||||
template_name = 'aircox/article_list.html'
|
template_name = 'aircox/article_list.html'
|
||||||
show_headline = True
|
show_headline = True
|
||||||
is_static = False
|
is_static = False
|
||||||
|
|
||||||
parent_model = Program
|
parent_model = Program
|
||||||
fk_parent = 'program'
|
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return super().get_queryset().filter(is_static=self.is_static)
|
return super().get_queryset().filter(is_static=self.is_static)
|
||||||
|
|
|
@ -3,6 +3,7 @@ from django.http import Http404
|
||||||
from django.views.generic import DetailView, ListView
|
from django.views.generic import DetailView, ListView
|
||||||
from django.views.generic.base import TemplateResponseMixin, ContextMixin
|
from django.views.generic.base import TemplateResponseMixin, ContextMixin
|
||||||
|
|
||||||
|
from ..models import Page
|
||||||
from ..utils import Redirect
|
from ..utils import Redirect
|
||||||
|
|
||||||
|
|
||||||
|
@ -15,8 +16,10 @@ class BaseView(TemplateResponseMixin, ContextMixin):
|
||||||
cover = None
|
cover = None
|
||||||
""" Page cover """
|
""" Page cover """
|
||||||
|
|
||||||
show_side_nav = False
|
has_sidebar = True
|
||||||
""" Show side navigation """
|
""" Show side navigation """
|
||||||
|
has_filters = False
|
||||||
|
""" Show filters nav """
|
||||||
list_count = 5
|
list_count = 5
|
||||||
""" Item count for small lists displayed on page. """
|
""" Item count for small lists displayed on page. """
|
||||||
|
|
||||||
|
@ -24,27 +27,29 @@ class BaseView(TemplateResponseMixin, ContextMixin):
|
||||||
def station(self):
|
def station(self):
|
||||||
return self.request.station
|
return self.request.station
|
||||||
|
|
||||||
def get_queryset(self):
|
# def get_queryset(self):
|
||||||
return super().get_queryset().station(self.station)
|
# return super().get_queryset().station(self.station)
|
||||||
|
|
||||||
def get_side_queryset(self):
|
def get_sidebar_queryset(self):
|
||||||
""" Return a queryset of items to render on the side nav. """
|
""" Return a queryset of items to render on the side nav. """
|
||||||
return None
|
return Page.objects.select_subclasses().published() \
|
||||||
|
.order_by('-pub_date')
|
||||||
|
|
||||||
def get_context_data(self, side_items=None, **kwargs):
|
def get_context_data(self, sidebar_items=None, **kwargs):
|
||||||
kwargs.setdefault('station', self.station)
|
kwargs.setdefault('station', self.station)
|
||||||
kwargs.setdefault('cover', self.cover)
|
kwargs.setdefault('cover', self.cover)
|
||||||
|
kwargs.setdefault('has_filters', self.has_filters)
|
||||||
|
|
||||||
show_side_nav = kwargs.setdefault('show_side_nav', self.show_side_nav)
|
has_sidebar = kwargs.setdefault('has_sidebar', self.has_sidebar)
|
||||||
if show_side_nav and side_items is None:
|
if has_sidebar and sidebar_items is None:
|
||||||
side_items = self.get_side_queryset()
|
sidebar_items = self.get_sidebar_queryset()
|
||||||
side_items = None if side_items is None else \
|
sidebar_items = None if sidebar_items is None else \
|
||||||
side_items[:self.list_count]
|
sidebar_items[:self.list_count]
|
||||||
|
|
||||||
if not 'audio_streams' in kwargs:
|
if not 'audio_streams' in kwargs:
|
||||||
streams = self.station.audio_streams
|
streams = self.station.audio_streams
|
||||||
streams = streams and streams.split('\n')
|
streams = streams and streams.split('\n')
|
||||||
kwargs['audio_streams'] = streams
|
kwargs['audio_streams'] = streams
|
||||||
|
|
||||||
return super().get_context_data(side_items=side_items, **kwargs)
|
return super().get_context_data(sidebar_items=sidebar_items, **kwargs)
|
||||||
|
|
||||||
|
|
|
@ -35,18 +35,14 @@ class EpisodeListView(ParentMixin, PageListView):
|
||||||
model = Episode
|
model = Episode
|
||||||
item_template_name = 'aircox/episode_item.html'
|
item_template_name = 'aircox/episode_item.html'
|
||||||
show_headline = True
|
show_headline = True
|
||||||
|
|
||||||
parent_model = Program
|
parent_model = Program
|
||||||
fk_parent = 'program'
|
|
||||||
|
|
||||||
|
|
||||||
class DiffusionListView(GetDateMixin, BaseView, ListView):
|
class DiffusionListView(GetDateMixin, BaseView, ListView):
|
||||||
""" View for timetables """
|
""" View for timetables """
|
||||||
model = Diffusion
|
model = Diffusion
|
||||||
|
has_filters = True
|
||||||
date = None
|
redirect_date_url = 'diffusion-list'
|
||||||
start = None
|
|
||||||
end = None
|
|
||||||
|
|
||||||
def get_date(self):
|
def get_date(self):
|
||||||
date = super().get_date()
|
date = super().get_date()
|
||||||
|
@ -56,19 +52,7 @@ class DiffusionListView(GetDateMixin, BaseView, ListView):
|
||||||
return super().get_queryset().today(self.date).order_by('start')
|
return super().get_queryset().today(self.date).order_by('start')
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
today = datetime.date.today()
|
|
||||||
start = self.date - datetime.timedelta(days=self.date.weekday())
|
start = self.date - datetime.timedelta(days=self.date.weekday())
|
||||||
dates = [
|
dates = [start + datetime.timedelta(days=i) for i in range(0, 7)]
|
||||||
(today, None),
|
|
||||||
(today - datetime.timedelta(days=1), None),
|
|
||||||
(today + datetime.timedelta(days=1), None),
|
|
||||||
(today - datetime.timedelta(days=7), _('next week')),
|
|
||||||
(today + datetime.timedelta(days=7), _('last week')),
|
|
||||||
(None, None)
|
|
||||||
] + [
|
|
||||||
(date, date.strftime('%A %d'))
|
|
||||||
for date in (start + datetime.timedelta(days=i)
|
|
||||||
for i in range(0, 7)) if date != today
|
|
||||||
]
|
|
||||||
return super().get_context_data(date=self.date, dates=dates, **kwargs)
|
return super().get_context_data(date=self.date, dates=dates, **kwargs)
|
||||||
|
|
||||||
|
|
|
@ -48,6 +48,9 @@ class LogListView(BaseView, LogListMixin, ListView):
|
||||||
Return list of logs for the provided date (from `kwargs` or
|
Return list of logs for the provided date (from `kwargs` or
|
||||||
`request.GET`, defaults to today).
|
`request.GET`, defaults to today).
|
||||||
"""
|
"""
|
||||||
|
redirect_date_url = 'log-list'
|
||||||
|
has_filters = True
|
||||||
|
|
||||||
def get_date(self):
|
def get_date(self):
|
||||||
date, today = super().get_date(), datetime.date.today()
|
date, today = super().get_date(), datetime.date.today()
|
||||||
return today if date is None else min(date, today)
|
return today if date is None else min(date, today)
|
||||||
|
@ -56,8 +59,7 @@ class LogListView(BaseView, LogListMixin, ListView):
|
||||||
today = datetime.date.today()
|
today = datetime.date.today()
|
||||||
kwargs.update({
|
kwargs.update({
|
||||||
'date': self.date,
|
'date': self.date,
|
||||||
'dates': ((today - datetime.timedelta(days=i), None)
|
'dates': (today - datetime.timedelta(days=i) for i in range(0, 7)),
|
||||||
for i in range(0, 7)),
|
|
||||||
'object_list': self.get_object_list(self.object_list),
|
'object_list': self.get_object_list(self.object_list),
|
||||||
})
|
})
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
|
@ -1,4 +1,6 @@
|
||||||
from django.shortcuts import get_object_or_404
|
import dateutil
|
||||||
|
from django.shortcuts import get_object_or_404, redirect
|
||||||
|
from django.urls import reverse
|
||||||
|
|
||||||
from ..utils import str_to_date
|
from ..utils import str_to_date
|
||||||
|
|
||||||
|
@ -12,13 +14,18 @@ class GetDateMixin:
|
||||||
`kwargs['date']`
|
`kwargs['date']`
|
||||||
"""
|
"""
|
||||||
date = None
|
date = None
|
||||||
|
redirect_date_url = None
|
||||||
|
|
||||||
def get_date(self):
|
def get_date(self):
|
||||||
if 'date' in self.request.GET:
|
date = self.request.GET.get('date')
|
||||||
return str_to_date(self.request.GET['date'], '-')
|
return str_to_date(date, '-') if date is not None else \
|
||||||
return self.kwargs['date'] if 'date' in self.kwargs else None
|
self.kwargs['date'] if 'date' in self.kwargs else None
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
|
if self.redirect_date_url and self.request.GET.get('date'):
|
||||||
|
return redirect(self.redirect_date_url,
|
||||||
|
date=self.request.GET['date'].replace('-', '/'))
|
||||||
|
|
||||||
self.date = self.get_date()
|
self.date = self.get_date()
|
||||||
return super().get(*args, **kwargs)
|
return super().get(*args, **kwargs)
|
||||||
|
|
||||||
|
@ -35,8 +42,6 @@ class ParentMixin:
|
||||||
""" Url lookup argument """
|
""" Url lookup argument """
|
||||||
parent_field = 'slug'
|
parent_field = 'slug'
|
||||||
""" Parent field for url lookup """
|
""" Parent field for url lookup """
|
||||||
fk_parent = 'page'
|
|
||||||
""" Page foreign key to the parent """
|
|
||||||
parent = None
|
parent = None
|
||||||
""" Parent page object """
|
""" Parent page object """
|
||||||
|
|
||||||
|
@ -54,8 +59,7 @@ class ParentMixin:
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
if self.parent is not None:
|
if self.parent is not None:
|
||||||
lookup = {self.fk_parent: self.parent}
|
return super().get_queryset().filter(parent=self.parent)
|
||||||
return super().get_queryset().filter(**lookup)
|
|
||||||
return super().get_queryset()
|
return super().get_queryset()
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
|
|
|
@ -1,6 +1,5 @@
|
||||||
|
|
||||||
from django.http import Http404, HttpResponse
|
from django.http import Http404, HttpResponse
|
||||||
from django.shortcuts import get_object_or_404
|
|
||||||
from django.utils.translation import ugettext_lazy as _
|
from django.utils.translation import ugettext_lazy as _
|
||||||
from django.views.generic import DetailView, ListView
|
from django.views.generic import DetailView, ListView
|
||||||
|
|
||||||
|
@ -19,9 +18,11 @@ __all__ = ['PageDetailView', 'PageListView']
|
||||||
class PageListView(BaseView, ListView):
|
class PageListView(BaseView, ListView):
|
||||||
template_name = 'aircox/page_list.html'
|
template_name = 'aircox/page_list.html'
|
||||||
item_template_name = 'aircox/page_item.html'
|
item_template_name = 'aircox/page_item.html'
|
||||||
|
has_sidebar = True
|
||||||
|
has_filters = True
|
||||||
|
|
||||||
paginate_by = 20
|
paginate_by = 20
|
||||||
show_headline = True
|
show_headline = True
|
||||||
show_side_nav = True
|
|
||||||
categories = None
|
categories = None
|
||||||
|
|
||||||
def get(self, *args, **kwargs):
|
def get(self, *args, **kwargs):
|
||||||
|
@ -36,7 +37,7 @@ class PageListView(BaseView, ListView):
|
||||||
# (by id)
|
# (by id)
|
||||||
if self.categories:
|
if self.categories:
|
||||||
qs = qs.filter(category__slug__in=self.categories)
|
qs = qs.filter(category__slug__in=self.categories)
|
||||||
return qs.order_by('-date')
|
return qs.order_by('-pub_date')
|
||||||
|
|
||||||
def get_categories_queryset(self):
|
def get_categories_queryset(self):
|
||||||
# TODO: use generic reverse field lookup
|
# TODO: use generic reverse field lookup
|
||||||
|
@ -56,6 +57,7 @@ class PageListView(BaseView, ListView):
|
||||||
class PageDetailView(BaseView, DetailView):
|
class PageDetailView(BaseView, DetailView):
|
||||||
""" Base view class for pages. """
|
""" Base view class for pages. """
|
||||||
context_object_name = 'page'
|
context_object_name = 'page'
|
||||||
|
has_filters = False
|
||||||
|
|
||||||
def get_queryset(self):
|
def get_queryset(self):
|
||||||
return super().get_queryset().select_related('cover', 'category')
|
return super().get_queryset().select_related('cover', 'category')
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
|
from django.db.models import Q
|
||||||
from django.core.exceptions import ObjectDoesNotExist
|
from django.core.exceptions import ObjectDoesNotExist
|
||||||
from django.shortcuts import get_object_or_404
|
from django.shortcuts import get_object_or_404
|
||||||
|
|
||||||
from aircox.models import Episode, Program
|
from ..models import Episode, Program, Page
|
||||||
|
from .mixins import ParentMixin
|
||||||
from .page import PageDetailView, PageListView
|
from .page import PageDetailView, PageListView
|
||||||
|
|
||||||
|
|
||||||
__all__ = ['ProgramPageDetailView', 'ProgramDetailView']
|
__all__ = ['ProgramPageDetailView', 'ProgramDetailView', 'ProgramPageListView']
|
||||||
|
|
||||||
|
|
||||||
class ProgramPageDetailView(PageDetailView):
|
class ProgramPageDetailView(PageDetailView):
|
||||||
|
@ -13,24 +15,25 @@ class ProgramPageDetailView(PageDetailView):
|
||||||
Base view class for a page that is displayed as a program's child page.
|
Base view class for a page that is displayed as a program's child page.
|
||||||
"""
|
"""
|
||||||
program = None
|
program = None
|
||||||
show_side_nav = True
|
has_sidebar = True
|
||||||
list_count = 5
|
list_count = 5
|
||||||
|
|
||||||
def get_side_queryset(self):
|
def get_sidebar_queryset(self):
|
||||||
return self.program.episode_set.published().order_by('-date')
|
return super().get_sidebar_queryset().filter(parent=self.object)
|
||||||
|
|
||||||
|
|
||||||
|
class ProgramPageListView(ParentMixin, PageListView):
|
||||||
|
model = Page
|
||||||
|
parent_model = Program
|
||||||
|
queryset = Page.objects.select_subclasses()
|
||||||
|
|
||||||
|
|
||||||
class ProgramDetailView(ProgramPageDetailView):
|
class ProgramDetailView(ProgramPageDetailView):
|
||||||
model = Program
|
model = Program
|
||||||
|
|
||||||
def get_articles_queryset(self):
|
|
||||||
return self.program.article_set.published().order_by('-date')
|
|
||||||
|
|
||||||
def get_context_data(self, **kwargs):
|
def get_context_data(self, **kwargs):
|
||||||
self.program = kwargs.setdefault('program', self.object)
|
self.program = kwargs.setdefault('program', self.object)
|
||||||
if 'articles' not in kwargs:
|
|
||||||
kwargs['articles'] = \
|
|
||||||
self.get_articles_queryset()[:self.list_count]
|
|
||||||
return super().get_context_data(**kwargs)
|
return super().get_context_data(**kwargs)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -24,3 +24,9 @@
|
||||||
margin: 1em 0em;
|
margin: 1em 0em;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
ul.menu-list li {
|
||||||
|
list-style-type: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -26,7 +26,6 @@ export default {
|
||||||
for(var tag of item.value.split(splitReg))
|
for(var tag of item.value.split(splitReg))
|
||||||
counts[tag.trim()] = (counts[tag.trim()] || 0) + 1;
|
counts[tag.trim()] = (counts[tag.trim()] || 0) + 1;
|
||||||
this.counts = counts;
|
this.counts = counts;
|
||||||
console.log('counts', this.counts)
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ $body-background-color: $light;
|
||||||
|
|
||||||
@import "~bulma/bulma";
|
@import "~bulma/bulma";
|
||||||
|
|
||||||
|
//-- helpers/modifiers
|
||||||
.is-fullwidth { width: 100%; }
|
.is-fullwidth { width: 100%; }
|
||||||
.is-fixed-bottom {
|
.is-fixed-bottom {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
|
@ -18,6 +19,14 @@ $body-background-color: $light;
|
||||||
background-color: transparent;
|
background-color: transparent;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.is-opacity-light {
|
||||||
|
opacity: 0.7;
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//-- navbar
|
||||||
.navbar + .container {
|
.navbar + .container {
|
||||||
margin-top: 1em;
|
margin-top: 1em;
|
||||||
}
|
}
|
||||||
|
@ -30,10 +39,44 @@ a.navbar-item.is-active {
|
||||||
border-bottom: 1px grey solid;
|
border-bottom: 1px grey solid;
|
||||||
}
|
}
|
||||||
|
|
||||||
.navbar .navbar-dropdown {
|
.navbar {
|
||||||
|
.navbar-dropdown {
|
||||||
z-index: 2000;
|
z-index: 2000;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.navbar-split {
|
||||||
|
margin: 0.2em 0em;
|
||||||
|
margin-right: 1em;
|
||||||
|
padding-right: 1em;
|
||||||
|
border-right: 1px $grey-light solid;
|
||||||
|
display: inline-block;
|
||||||
|
}
|
||||||
|
|
||||||
|
form {
|
||||||
|
margin: 0em;
|
||||||
|
padding: 0em;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//-- filters
|
||||||
|
.filters {
|
||||||
|
margin: 1em 0em;
|
||||||
|
background-color: transparent;
|
||||||
|
margin-bottom: 1em;
|
||||||
|
|
||||||
|
.title {
|
||||||
|
padding-right: 2em;
|
||||||
|
margin-right: 1em;
|
||||||
|
border-right: 1px $grey-light solid;
|
||||||
|
|
||||||
|
font-size: $size-5;
|
||||||
|
color: $text-light;
|
||||||
|
font-weight: $weight-light;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
.navbar-brand img {
|
.navbar-brand img {
|
||||||
min-height: 6em;
|
min-height: 6em;
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
Django>=2.2.0
|
Django>=2.2.0
|
||||||
djangorestframework>=3.9.4
|
djangorestframework>=3.9.4
|
||||||
|
django-model-utils>=3.2.0
|
||||||
|
|
||||||
dateutils>=0.6.6
|
|
||||||
watchdog>=0.8.3
|
watchdog>=0.8.3
|
||||||
psutil>=5.0.1
|
psutil>=5.0.1
|
||||||
tzlocal>=1.4
|
tzlocal>=1.4
|
||||||
|
@ -12,7 +12,6 @@ django-filer>=1.5.0
|
||||||
django-ckeditor>=5.7.1
|
django-ckeditor>=5.7.1
|
||||||
django-admin-sortable2>=0.7.2
|
django-admin-sortable2>=0.7.2
|
||||||
django-content-editor>=1.4.2
|
django-content-editor>=1.4.2
|
||||||
|
|
||||||
django-honeypot>=0.5.0
|
django-honeypot>=0.5.0
|
||||||
|
|
||||||
gunicorn>=19.6.0
|
gunicorn>=19.6.0
|
||||||
|
|
Loading…
Reference in New Issue
Block a user