work on home page, fix views & templates issues

This commit is contained in:
bkfox 2019-09-10 17:57:24 +02:00
parent 215a6ac331
commit e30d1b54ef
51 changed files with 658 additions and 276 deletions

View File

@ -19,7 +19,7 @@ __all__ = ['Episode', 'Diffusion', 'DiffusionQuerySet']
class Episode(Page): class Episode(Page):
objects = ProgramChildQuerySet.as_manager() objects = ProgramChildQuerySet.as_manager()
detail_url_name = 'episode-detail' detail_url_name = 'episode-detail'
item_template_name = 'aircox/episode_item.html' item_template_name = 'aircox/widgets/episode_item.html'
@property @property
def program(self): def program(self):
@ -75,7 +75,9 @@ class DiffusionQuerySet(BaseRerunQuerySet):
def today(self, today=None, order=True): def today(self, today=None, order=True):
""" Diffusions occuring today. """ """ Diffusions occuring today. """
today = today or datetime.date.today() today = today or datetime.date.today()
qs = self.filter(Q(start__date=today) | Q(end__date=today)) start = tz.datetime.combine(today, datetime.time())
end = tz.datetime.combine(today, datetime.time(23, 59, 59, 999))
qs = self.filter(start__range = (start, end))
return qs.order_by('start') if order else qs return qs.order_by('start') if order else qs
def at(self, date, order=True): def at(self, date, order=True):
@ -90,9 +92,9 @@ class DiffusionQuerySet(BaseRerunQuerySet):
""" """
date = utils.date_or_default(date) date = utils.date_or_default(date)
if isinstance(date, tz.datetime): if isinstance(date, tz.datetime):
qs = self.filter(start__gte=date) qs = self.filter(Q(start__gte=date) | Q(end__gte=date))
else: else:
qs = self.filter(start__date__gte=date) qs = self.filter(Q(start__date__gte=date) | Q(end__date__gte=date))
return qs.order_by('start') return qs.order_by('start')
def before(self, date=None): def before(self, date=None):
@ -157,7 +159,7 @@ class Diffusion(BaseRerun):
# help_text = _('use this input port'), # help_text = _('use this input port'),
# ) # )
item_template_name = 'aircox/diffusion_item.html' item_template_name = 'aircox/widgets/diffusion_item.html'
class Meta: class Meta:
verbose_name = _('Diffusion') verbose_name = _('Diffusion')
@ -181,12 +183,10 @@ class Diffusion(BaseRerun):
# self.check_conflicts() # self.check_conflicts()
def save_rerun(self): def save_rerun(self):
print('rerun save', self)
self.episode = self.initial.episode self.episode = self.initial.episode
self.program = self.episode.program self.program = self.episode.program
def save_initial(self): def save_initial(self):
print('initial save', self)
self.program = self.episode.program self.program = self.episode.program
if self.episode != self._initial['episode']: if self.episode != self._initial['episode']:
self.rerun_set.update(episode=self.episode, program=self.program) self.rerun_set.update(episode=self.episode, program=self.program)
@ -221,6 +221,13 @@ class Diffusion(BaseRerun):
return tz.localtime(self.end, tz.get_current_timezone()) return tz.localtime(self.end, tz.get_current_timezone())
@property
def is_now(self):
""" True if diffusion is currently running """
now = tz.now()
return self.type == self.TYPE_ON_AIR and \
self.start <= now and self.end >= now
# TODO: property? # TODO: property?
def is_live(self): def is_live(self):
""" True if Diffusion is live (False if there are sounds files). """ """ True if Diffusion is live (False if there are sounds files). """

View File

@ -271,9 +271,11 @@ class Log(models.Model):
object_list += logs[:index] object_list += logs[:index]
logs = logs[index:] logs = logs[index:]
if len(logs):
# FIXME
# - last log while diff is running # - last log while diff is running
if logs[0].date > diff.start: #if logs[0].date > diff.start:
object_list.append(logs[0]) # object_list.append(logs[0])
# - skips logs while diff is running # - skips logs while diff is running
index = next((i for i, v in enumerate(logs) index = next((i for i, v in enumerate(logs)
@ -284,8 +286,7 @@ class Log(models.Model):
# - add diff # - add diff
object_list.append(diff) object_list.append(diff)
return object_list return object_list if count is None else object_list[:count]
def print(self): def print(self):
r = [] r = []

View File

@ -92,7 +92,7 @@ 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' item_template_name = 'aircox/widgets/page_item.html'
def __str__(self): def __str__(self):
return '{}'.format(self.title or self.pk) return '{}'.format(self.title or self.pk)

View File

@ -1,6 +1,3 @@
.navbar {
box-shadow: 0em 0em 0.6em rgba(0, 0, 0, 0.4); }
.navbar .navbar-brand { .navbar .navbar-brand {
padding-right: 1em; } padding-right: 1em; }
@ -7211,6 +7208,28 @@ a.navbar-item.is-active {
margin: 0em; margin: 0em;
padding: 0em; } padding: 0em; }
.card .title {
padding: 0.2em;
font-size: 1.25rem;
font-weight: 500; }
.card .title a {
color: #363636; }
.card.is-primary {
box-shadow: 0em 0em 0.5em #0a0a0a; }
.card-super-title {
position: absolute;
z-index: 1000;
font-size: 1.2em;
font-weight: 700;
padding: 0.2em;
top: 1em;
background-color: white; }
.card-super-title .fas {
padding: 0.1em;
font-size: 0.8em; }
.filters { .filters {
margin: 1em 0em; margin: 1em 0em;
background-color: transparent; background-color: transparent;
@ -7223,16 +7242,6 @@ a.navbar-item.is-active {
color: #7a7a7a; color: #7a7a7a;
font-weight: 300; } font-weight: 300; }
/*
.navbar-brand img {
min-height: 6em;
}
.navbar-menu .navbar-item:not(:last-child) {
border-right: 1px $grey solid;
}
*/
/** page **/
.page > .cover { .page > .cover {
float: right; float: right;
max-width: 45%; } max-width: 45%; }
@ -7247,26 +7256,23 @@ a.navbar-item.is-active {
.page p { .page p {
padding: 0.4em 0em; } padding: 0.4em 0em; }
section > .toolbar { .media.item .headline {
background-color: rgba(0, 0, 0, 0.05); line-height: 1.2em;
padding: 1em; max-height: calc(1.2em * 3);
margin-bottom: 1.5em; } overflow: hidden; }
.media.item .headline + .headline-overflow {
main .cover { position: relative;
margin: 1em 0em; width: 100%;
border: 0.2em black solid; } height: 2em;
margin-top: -2em; }
main .small-cover { .media.item .headline + .headline-overflow:before {
width: 10em; } content: '';
width: 100%;
aside .small-cover { height: 100%;
width: 4em; } position: absolute;
left: 0;
aside .media .subtitle { bottom: 0;
font-size: 1em; } background: linear-gradient(transparent 1em, whitesmoke); }
aside .media .content {
display: none; }
.player { .player {
box-shadow: 0em 1.5em 2.5em rgba(0, 0, 0, 0.6); } box-shadow: 0em 1.5em 2.5em rgba(0, 0, 0, 0.6); }
@ -7287,3 +7293,37 @@ aside .media .content {
.player .title { .player .title {
margin: 0em; } margin: 0em; }
.media .subtitle {
margin-bottom: 0.4em; }
.media .media-content .headline {
font-size: 1em;
font-weight: 400; }
body {
background-color: whitesmoke; }
section > .toolbar {
background-color: rgba(0, 0, 0, 0.05);
padding: 1em;
margin-bottom: 1.5em; }
main .cover {
margin: 1em 0em;
border: 0.2em black solid; }
main .small-cover {
width: 10em; }
aside > section {
margin-bottom: 2em; }
aside .cover {
margin-bottom: 2em; }
aside .small-cover {
width: 4em; }
aside .media .subtitle {
font-size: 1em; }

View File

@ -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 }\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 onclick(event) {\n // TODO: row click => check checkbox\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");
/***/ }), /***/ }),
@ -317,7 +317,7 @@ eval("__webpack_require__.r(__webpack_exports__);\n//\n//\n//\n//\n//\n//\n\n\nc
/***/ (function(module, __webpack_exports__, __webpack_require__) { /***/ (function(module, __webpack_exports__, __webpack_require__) {
"use strict"; "use strict";
eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"State\", function() { return State; });\n/* harmony import */ var _liveInfo__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./liveInfo */ \"./assets/public/liveInfo.js\");\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\nconst State = {\n paused: 0,\n playing: 1,\n loading: 2,\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n data() {\n return {\n state: State.paused,\n liveInfo: new _liveInfo__WEBPACK_IMPORTED_MODULE_0__[\"default\"](this.liveInfoUrl, this.liveInfoTimeout),\n }\n },\n\n props: {\n buttonTitle: String,\n liveInfoUrl: String,\n liveInfoTimeout: { type: Number, default: 5},\n src: String,\n },\n\n computed: {\n paused() { return this.state == State.paused; },\n playing() { return this.state == State.playing; },\n loading() { return this.state == State.loading; },\n\n onAir() {\n return this.liveInfo.items && this.liveInfo.items[0];\n },\n\n buttonStyle() {\n if(!this.onAir)\n return;\n return { backgroundImage: `url(${this.onAir.cover})` }\n }\n },\n\n methods: {\n load(src) {\n const audio = this.$refs.audio;\n audio.src = src;\n audio.load()\n },\n\n play(src) {\n if(src)\n this.load(src);\n this.$refs.audio.play().catch(e => console.error(e))\n },\n\n pause() {\n this.$refs.audio.pause()\n },\n\n toggle() {\n if(this.paused)\n this.play()\n else\n this.pause()\n },\n\n onChange(event) {\n const audio = this.$refs.audio;\n this.state = audio.paused ? State.paused : State.playing;\n },\n },\n\n mounted() {\n this.liveInfo.refresh()\n },\n\n destroyed() {\n this.liveInfo.drop()\n },\n});\n\n\n\n//# sourceURL=webpack:///./assets/public/player.vue?./node_modules/vue-loader/lib??vue-loader-options"); eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"State\", function() { return State; });\n/* harmony import */ var _liveInfo__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./liveInfo */ \"./assets/public/liveInfo.js\");\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\nconst State = {\n paused: 0,\n playing: 1,\n loading: 2,\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n data() {\n return {\n state: State.paused,\n liveInfo: new _liveInfo__WEBPACK_IMPORTED_MODULE_0__[\"default\"](this.liveInfoUrl, this.liveInfoTimeout),\n }\n },\n\n props: {\n buttonTitle: String,\n liveInfoUrl: String,\n liveInfoTimeout: { type: Number, default: 5},\n src: String,\n },\n\n computed: {\n paused() { return this.state == State.paused; },\n playing() { return this.state == State.playing; },\n loading() { return this.state == State.loading; },\n\n onAir() {\n return this.liveInfo.items && this.liveInfo.items[0];\n },\n\n buttonStyle() {\n if(!this.onAir)\n return;\n return { backgroundImage: `url(${this.onAir.cover})` }\n }\n },\n\n methods: {\n load(src) {\n const audio = this.$refs.audio;\n audio.src = src;\n audio.load()\n },\n\n play(src) {\n if(src)\n this.load(src);\n this.$refs.audio.play().catch(e => console.error(e))\n },\n\n pause() {\n this.$refs.audio.pause()\n },\n\n toggle() {\n console.log('tooogle', this.paused, '-', this.$refs.audio.src)\n if(this.paused)\n this.play()\n else\n this.pause()\n },\n\n onChange(event) {\n const audio = this.$refs.audio;\n this.state = audio.paused ? State.paused : State.playing;\n },\n },\n\n mounted() {\n this.liveInfo.refresh()\n },\n\n destroyed() {\n this.liveInfo.drop()\n },\n});\n\n\n\n//# sourceURL=webpack:///./assets/public/player.vue?./node_modules/vue-loader/lib??vue-loader-options");
/***/ }), /***/ }),

View File

@ -7190,6 +7190,28 @@ a.navbar-item.is-active {
margin: 0em; margin: 0em;
padding: 0em; } padding: 0em; }
.card .title {
padding: 0.2em;
font-size: 1.25rem;
font-weight: 500; }
.card .title a {
color: #363636; }
.card.is-primary {
box-shadow: 0em 0em 0.5em #0a0a0a; }
.card-super-title {
position: absolute;
z-index: 1000;
font-size: 1.2em;
font-weight: 700;
padding: 0.2em;
top: 1em;
background-color: white; }
.card-super-title .fas {
padding: 0.1em;
font-size: 0.8em; }
.filters { .filters {
margin: 1em 0em; margin: 1em 0em;
background-color: transparent; background-color: transparent;
@ -7202,16 +7224,6 @@ a.navbar-item.is-active {
color: #7a7a7a; color: #7a7a7a;
font-weight: 300; } font-weight: 300; }
/*
.navbar-brand img {
min-height: 6em;
}
.navbar-menu .navbar-item:not(:last-child) {
border-right: 1px $grey solid;
}
*/
/** page **/
.page > .cover { .page > .cover {
float: right; float: right;
max-width: 45%; } max-width: 45%; }
@ -7226,26 +7238,23 @@ a.navbar-item.is-active {
.page p { .page p {
padding: 0.4em 0em; } padding: 0.4em 0em; }
section > .toolbar { .media.item .headline {
background-color: rgba(0, 0, 0, 0.05); line-height: 1.2em;
padding: 1em; max-height: calc(1.2em * 3);
margin-bottom: 1.5em; } overflow: hidden; }
.media.item .headline + .headline-overflow {
main .cover { position: relative;
margin: 1em 0em; width: 100%;
border: 0.2em black solid; } height: 2em;
margin-top: -2em; }
main .small-cover { .media.item .headline + .headline-overflow:before {
width: 10em; } content: '';
width: 100%;
aside .small-cover { height: 100%;
width: 4em; } position: absolute;
left: 0;
aside .media .subtitle { bottom: 0;
font-size: 1em; } background: linear-gradient(transparent 1em, whitesmoke); }
aside .media .content {
display: none; }
.player { .player {
box-shadow: 0em 1.5em 2.5em rgba(0, 0, 0, 0.6); } box-shadow: 0em 1.5em 2.5em rgba(0, 0, 0, 0.6); }
@ -7266,3 +7275,37 @@ aside .media .content {
.player .title { .player .title {
margin: 0em; } margin: 0em; }
.media .subtitle {
margin-bottom: 0.4em; }
.media .media-content .headline {
font-size: 1em;
font-weight: 400; }
body {
background-color: whitesmoke; }
section > .toolbar {
background-color: rgba(0, 0, 0, 0.05);
padding: 1em;
margin-bottom: 1.5em; }
main .cover {
margin: 1em 0em;
border: 0.2em black solid; }
main .small-cover {
width: 10em; }
aside > section {
margin-bottom: 2em; }
aside .cover {
margin-bottom: 2em; }
aside .small-cover {
width: 4em; }
aside .media .subtitle {
font-size: 1em; }

View File

@ -246,7 +246,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/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"State\", function() { return State; });\n/* harmony import */ var _liveInfo__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./liveInfo */ \"./assets/public/liveInfo.js\");\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\nconst State = {\n paused: 0,\n playing: 1,\n loading: 2,\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n data() {\n return {\n state: State.paused,\n liveInfo: new _liveInfo__WEBPACK_IMPORTED_MODULE_0__[\"default\"](this.liveInfoUrl, this.liveInfoTimeout),\n }\n },\n\n props: {\n buttonTitle: String,\n liveInfoUrl: String,\n liveInfoTimeout: { type: Number, default: 5},\n src: String,\n },\n\n computed: {\n paused() { return this.state == State.paused; },\n playing() { return this.state == State.playing; },\n loading() { return this.state == State.loading; },\n\n onAir() {\n return this.liveInfo.items && this.liveInfo.items[0];\n },\n\n buttonStyle() {\n if(!this.onAir)\n return;\n return { backgroundImage: `url(${this.onAir.cover})` }\n }\n },\n\n methods: {\n load(src) {\n const audio = this.$refs.audio;\n audio.src = src;\n audio.load()\n },\n\n play(src) {\n if(src)\n this.load(src);\n this.$refs.audio.play().catch(e => console.error(e))\n },\n\n pause() {\n this.$refs.audio.pause()\n },\n\n toggle() {\n if(this.paused)\n this.play()\n else\n this.pause()\n },\n\n onChange(event) {\n const audio = this.$refs.audio;\n this.state = audio.paused ? State.paused : State.playing;\n },\n },\n\n mounted() {\n this.liveInfo.refresh()\n },\n\n destroyed() {\n this.liveInfo.drop()\n },\n});\n\n\n\n//# sourceURL=webpack:///./assets/public/player.vue?./node_modules/vue-loader/lib??vue-loader-options"); eval("__webpack_require__.r(__webpack_exports__);\n/* harmony export (binding) */ __webpack_require__.d(__webpack_exports__, \"State\", function() { return State; });\n/* harmony import */ var _liveInfo__WEBPACK_IMPORTED_MODULE_0__ = __webpack_require__(/*! ./liveInfo */ \"./assets/public/liveInfo.js\");\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n//\n\n\n\nconst State = {\n paused: 0,\n playing: 1,\n loading: 2,\n}\n\n/* harmony default export */ __webpack_exports__[\"default\"] = ({\n data() {\n return {\n state: State.paused,\n liveInfo: new _liveInfo__WEBPACK_IMPORTED_MODULE_0__[\"default\"](this.liveInfoUrl, this.liveInfoTimeout),\n }\n },\n\n props: {\n buttonTitle: String,\n liveInfoUrl: String,\n liveInfoTimeout: { type: Number, default: 5},\n src: String,\n },\n\n computed: {\n paused() { return this.state == State.paused; },\n playing() { return this.state == State.playing; },\n loading() { return this.state == State.loading; },\n\n onAir() {\n return this.liveInfo.items && this.liveInfo.items[0];\n },\n\n buttonStyle() {\n if(!this.onAir)\n return;\n return { backgroundImage: `url(${this.onAir.cover})` }\n }\n },\n\n methods: {\n load(src) {\n const audio = this.$refs.audio;\n audio.src = src;\n audio.load()\n },\n\n play(src) {\n if(src)\n this.load(src);\n this.$refs.audio.play().catch(e => console.error(e))\n },\n\n pause() {\n this.$refs.audio.pause()\n },\n\n toggle() {\n console.log('tooogle', this.paused, '-', this.$refs.audio.src)\n if(this.paused)\n this.play()\n else\n this.pause()\n },\n\n onChange(event) {\n const audio = this.$refs.audio;\n this.state = audio.paused ? State.paused : State.playing;\n },\n },\n\n mounted() {\n this.liveInfo.refresh()\n },\n\n destroyed() {\n this.liveInfo.drop()\n },\n});\n\n\n\n//# sourceURL=webpack:///./assets/public/player.vue?./node_modules/vue-loader/lib??vue-loader-options");
/***/ }), /***/ }),

View File

@ -3,8 +3,14 @@
{% block content %}{{ block.super }} {% block content %}{{ block.super }}
<div id="app">
{# TODO: date subtitle #} {# TODO: date subtitle #}
<div class="columns"> <nav class="navbar" role="menu">
{% with "admin:tools-stats" as url_name %}
{% include "aircox/widgets/dates_menu.html" %}
{% endwith %}
</nav>
<a-statistics class="column"> <a-statistics class="column">
<template v-slot:default="{counts}"> <template v-slot:default="{counts}">
@ -23,13 +29,12 @@
{% for track in tracks %} {% for track in tracks %}
<tr> <tr>
{% if forloop.first %} {% if forloop.first %}
<td rowspan="{{ tracks|length }}">{{ object.start|time:"H:i" }}</td> <td rowspan="{{ tracks|length }}">{{ object.start|time:"H:i" }} {% if object|is_diffusion %} - {{ object.end|time:"H:i" }}{% endif %}</td>
<td rowspan="{{ tracks|length }}">{{ object.episode|default:"" }}</td> <td rowspan="{{ tracks|length }}">{{ object.episode|default:"" }}</td>
{% endif %} {% endif %}
{% if not object|is_diffusion %}
{% with track as object %} {% with track as object %}
<td>{% include "aircox/track_item.html" %}</td> <td>{% include "aircox/widgets/track_item.html" %}</td>
{% endwith %} {% endwith %}
{% with track.tags.all|join:', ' as tags %} {% with track.tags.all|join:', ' as tags %}
<td> <td>
@ -41,10 +46,17 @@
{% endif %} {% endif %}
</td> </td>
{% endwith %} {% endwith %}
{% else %} </tr>
{% empty %}
{% if object|is_diffusion %}
<tr>
<td>{{ object.start|time:"H:i" }} - {{ object.end|time:"H:i" }}</td>
<td>{{ object.episode|default:"" }}</td>
<td></td>
<td></td>
</tr>
{% endif %} {% endif %}
{% endfor %} {% endfor %}
<tr>
{% endwith %} {% endwith %}
{% endfor %} {% endfor %}
</tbody> </tbody>
@ -65,14 +77,6 @@
</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> </div>
{% endblock %} {% endblock %}

View File

@ -29,11 +29,10 @@
data-admin-utc-offset="{% now "Z" %}"> data-admin-utc-offset="{% now "Z" %}">
<!-- Container --> <!-- Container -->
<div id="app"> <div>
{% if not is_popup %} {% if not is_popup %}
<!-- Header --> <!-- Header -->
<nav class="navbar is-dark"> <nav class="navbar is-dark has-shadow">
<div class="navbar-brand"> <div class="navbar-brand">
{% block branding %}{% endblock %} {% block branding %}{% endblock %}
</div> </div>

View File

@ -37,7 +37,7 @@
{% block coltype %}flex{% endblock %} {% block coltype %}flex{% endblock %}
{% block content %} {% block content %}
<div id="content-main"> <div id="content-main">
{% block object-tools %} {% block object-tools %}
<ul class="object-tools"> <ul class="object-tools">
{% block object-tools-items %} {% block object-tools-items %}
@ -85,5 +85,5 @@
</div> </div>
</div> </div>
{% endblock %} {% endblock %}

View File

@ -4,12 +4,12 @@
{% block sidebar %} {% block sidebar %}
{{ block.super }} {{ block.super }}
{% if sidebar_items %} {% if sidebar_object_list %}
<section> <section>
<h4 class="title is-4">{% trans "Latest news" %}</h4> <h4 class="title is-4">{% trans "Latest news" %}</h4>
{% for object in sidebar_items %} {% for object in sidebar_object_list %}
{% include "aircox/page_item.html" %} {% include "aircox/widgets/page_item.html" %}
{% endfor %} {% endfor %}
<br> <br>

View File

@ -1,10 +1,29 @@
{% load static i18n thumbnail aircox %}
{% comment %} {% comment %}
Context: Context:
- cover: image cover - cover: image cover
- site: current website - site: current website
- has_filters: display filter bar (using block "filters") - has_filters: display filter bar (using block "filters")
- sidebar_object_list: item to display in sidebar
- sidebar_url_name: url name sidebar item complete list
- sidebar_url_parent: parent page for sidebar items complete list
Blocks:
- assets
- head_title
- head_extra
- top_nav
- header:
- title
- subtitle
- header_meta
- main:
- filters
- cover
- sidebar:
- sidebar_title
{% endcomment %} {% endcomment %}
{% load static i18n thumbnail aircox %}
<html> <html>
<head> <head>
<meta charset="utf-8" /> <meta charset="utf-8" />
@ -72,14 +91,14 @@ Context:
{% endblock %} {% endblock %}
</header> </header>
{% block main %}
{% if has_filters %} {% if has_filters %}
<nav class="navbar filters" <nav class="navbar filters"
aria-label="{% trans "list filters" %}"> aria-label="{% trans "list filters" %}">
{% block filters %}{% endblock %} {% block filters %}{% endblock %}
</nav> </nav>
{% endif %} {% endif %}
{% endblock main %}
{% block main %}{% endblock main %}
</main> </main>
{% if has_sidebar %} {% if has_sidebar %}
@ -92,28 +111,18 @@ Context:
{% endblock %} {% endblock %}
{% block sidebar %} {% block sidebar %}
{% if sidebar_items %} {% if sidebar_object_list %}
{% with object_list=sidebar_object_list %}
{% with list_url=sidebar_list_url %}
{% with has_headline=False %}
<section> <section>
<h4 class="title is-4"> <h4 class="title is-4">
{% block sidebar_title %}{% trans "Recently" %}{% endblock %} {% block sidebar_title %}{% trans "Recently" %}{% endblock %}
</h4> </h4>
{% include "aircox/widgets/page_list.html" %}
{% for object in sidebar_items %} {% endwith %}
{% include "aircox/episode_item.html" %} {% endwith %}
{% endfor %} {% endwith %}
<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 %} {% endif %}
</section> </section>
{% endblock %} {% endblock %}

View File

@ -16,7 +16,7 @@
{% endblock %} {% endblock %}
{% block main %}{{ block.super }} {% block main %}{{ block.super }}
{% with True as hide_schedule %} {% with hide_schedule=True %}
<section role="list"> <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 %}
@ -24,12 +24,12 @@
<div class="columns {% if diffusion.start.date != date and diffusion.start.end <= date %}is-opacity-light{% endif %}"> <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:"d H:i" }} - {{ diffusion.end|date:"d H:i" }} {{ diffusion.start|date:"H:i" }} - {{ diffusion.end|date:"H:i" }}
</time> </time>
</div> </div>
<div class="column"> <div class="column">
{% with diffusion.episode as object %} {% with diffusion.episode as object %}
{% include "aircox/episode_item.html" %} {% include "aircox/widgets/episode_item.html" %}
{% endwith %} {% endwith %}
</div> </div>
</div> </div>

View File

@ -50,7 +50,7 @@
<section class="column"> <section class="column">
<h4 class="title is-4">{% trans "Podcasts" %}</h4> <h4 class="title is-4">{% trans "Podcasts" %}</h4>
{% for object in podcasts %} {% for object in podcasts %}
{% include "aircox/podcast_item.html" %} {% include "aircox/widgets/podcast_item.html" %}
{% endfor %} {% endfor %}
</section> </section>
{% endif %} {% endif %}

View File

@ -0,0 +1,83 @@
{% extends "aircox/page_list.html" %}
{% comment %}
Context:
-
-
TODO:
- sidebar:
- logs
- diffusions
- main:
- focused
- nav to 'publications' view
-
{% endcomment %}
{% load i18n %}
{% block head_title %}{% block title %}{{ station.name }}{% endblock %}{% endblock %}
{% block main %}
<div class="columns">
{% with render_card=True %}
{% for object in top_diffs %}
{% with is_primary=object.is_now %}
<div class="column is-relative">
<h4 class="card-super-title">
{% if is_primary %}
<span class="fas fa-play"></span>
{% trans "Currently" %}
{% else %}{{ object.start|date:"H:i" }}{% endif %}
</h4>
{% include object.item_template_name %}
</div>
{% endwith %}
{% endfor %}
{% endwith %}
</div>
<hr>
<h4 class="title is-4">{% trans "Last publications" %}</h4>
{{ block.super }}
{% endblock %}
{% block pagination %}
<ul class="pagination-list">
<li>
<a href="{% url "page-list" %}" class="pagination-link"
aria-label="{% trans "Show all publication" %}">
{% trans "More publications..." %}
</a>
</li>
</ul>
{% endblock %}
{% block sidebar %}
<section>
<h4 class="title is-4">{% trans "Previously on air" %}</h4>
{% with logs as object_list %}
{% include "aircox/widgets/log_list.html" %}
{% endwith %}
</section>
<section>
<h4 class="title is-4">{% trans "Today" %}</h4>
{% with hide_schedule=True %}
{% with has_headline=False %}
<table class="table is-fullwidth has-background-transparent">
{% for object in sidebar_object_list %}
<tr>
<td>{{ object.start|date:"H:i" }}</td>
<td>{% include "aircox/widgets/diffusion_item.html" %}</td>
</tr>
{% endfor %}
</table>
{% endwith %}
{% endwith %}
</section>
{% endblock %}

View File

@ -16,29 +16,10 @@
{% endwith %} {% endwith %}
{% endblock %} {% endblock %}
{% block main %} {% block main %}{{ block.super }}
<section> <section>
{# <h4 class="subtitle size-4">{{ date }}</h4> #} {# <h4 class="subtitle size-4">{{ date }}</h4> #}
{% with True as hide_schedule %} {% include "aircox/widgets/log_list.html" %}
<table class="table is-striped is-hoverable is-fullwidth">
{% for object in object_list %}
<tr>
<td>
{% if object|is_diffusion %}
<time datetime="{{ object.start }}" title="{{ object.start }}">
{{ object.start|date:"H:i" }} - {{ object.end|date:"H:i" }}
</time>
{% else %}
<time datetime="{{ object.date }}" title="{{ object.date }}">
{{ object.date|date:"H:i" }}
</time>
{% endif %}
</td>
<td>{% include "aircox/log_item.html" %}</td>
</tr>
{% endfor %}
</table>
{% endwith %}
</section> </section>
{% endblock %} {% endblock %}

View File

@ -56,21 +56,23 @@
{% endblock %} {% endblock %}
{% block main %} {% block main %}{{ block.super }}
<section role="list"> <section role="list">
{% with has_headline=True %}
{% for object in object_list %} {% for object in object_list %}
{% block list_object %} {% block list_object %}
{% include object.item_template_name|default:item_template_name %} {% include object.item_template_name|default:item_template_name %}
{% endblock %} {% endblock %}
{% endfor %} {% endfor %}
{% endwith %}
</section> </section>
{% if is_paginated %} {% if is_paginated %}
<hr> <hr>
{% update_query request.GET.copy page=None as GET %} {% update_query request.GET.copy page=None as GET %}
{% with GET.urlencode as GET %} {% with GET.urlencode as GET %}
<nav class="pagination is-centered" role="pagination" aria-label="{% trans "pagination" %}"> <nav class="pagination is-centered" role="pagination" aria-label="{% trans "pagination" %}">
{% block pagination %}
{% if page_obj.has_previous %} {% if page_obj.has_previous %}
<a href="?{{ GET }}&page={{ page_obj.previous_page_number }}" class="pagination-previous"> <a href="?{{ GET }}&page={{ page_obj.previous_page_number }}" class="pagination-previous">
{% else %} {% else %}
@ -93,6 +95,7 @@
</li> </li>
{% endfor %} {% endfor %}
</ul> </ul>
{% endblock %}
</nav> </nav>
{% endwith %} {% endwith %}
{% endif %} {% endif %}

View File

@ -3,13 +3,7 @@
{% block sidebar_title %} {% block sidebar_title %}
{% with program.title as program %} {% with program.title as program %}
{% blocktrans %} Recently on {{ program }}{% endblocktrans %} {% blocktrans %}Recently on {{ program }}{% endblocktrans %}
{% endwith %}
{% endblock %}
{% block sidebar %}
{% with program as parent %}
{{ block.super }}
{% endwith %} {% endwith %}
{% endblock %} {% endblock %}

View File

@ -10,15 +10,14 @@
{% block content %}{{ block.super }} {% block content %}{{ block.super }}
<br> <br>
{% with has_headline=False %}
{% with show_headline=False %}
<div class="columns is-desktop"> <div class="columns is-desktop">
{% if articles %} {% if articles %}
<section class="column"> <section class="column">
<h4 class="title is-4">{% trans "Articles" %}</h4> <h4 class="title is-4">{% trans "Articles" %}</h4>
{% for object in articles %} {% for object in articles %}
{% include "aircox/page_item.html" %} {% include "aircox/widgets/page_item.html" %}
{% endfor %} {% endfor %}
<br> <br>

View File

@ -5,7 +5,7 @@ Context:
{% endcomment %} {% endcomment %}
{% with object as diffusion %} {% with object as diffusion %}
{% with object.episode as object %} {% with object.episode as object %}
{% include "aircox/episode_item.html" %} {% include "aircox/widgets/episode_item.html" %}
{% endwith %} {% endwith %}
{% endwith %} {% endwith %}

View File

@ -1,4 +1,4 @@
{% extends "aircox/page_item.html" %} {% extends "aircox/widgets/page_item.html" %}
{% load i18n easy_thumbnails_tags aircox %} {% load i18n easy_thumbnails_tags aircox %}
{% comment %} {% comment %}

View File

@ -11,11 +11,11 @@ for design review.
{% if object|is_diffusion %} {% if object|is_diffusion %}
{% with object as diffusion %} {% with object as diffusion %}
{% include "aircox/diffusion_item.html" %} {% include "aircox/widgets/diffusion_item.html" %}
{% endwith %} {% endwith %}
{% else %} {% else %}
{% with object.track as object %} {% with object.track as object %}
{% include "aircox/track_item.html" %} {% include "aircox/widgets/track_item.html" %}
{% endwith %} {% endwith %}
{% endif %} {% endif %}

View File

@ -0,0 +1,29 @@
{% comment %}
Render list of logs (as widget).
Context:
- object_list: list of logs to display
{% endcomment %}
{% load aircox %}
{% with True as hide_schedule %}
<table class="table is-striped is-hoverable is-fullwidth" role="list">
{% for object in object_list %}
<tr>
<td>
{% if object|is_diffusion %}
<time datetime="{{ object.start }}" title="{{ object.start }}">
{{ object.start|date:"H:i" }} - {{ object.end|date:"H:i" }}
</time>
{% else %}
<time datetime="{{ object.date }}" title="{{ object.date }}">
{{ object.date|date:"H:i" }}
</time>
{% endif %}
</td>
<td>{% include "aircox/widgets/log_item.html" %}</td>
</tr>
{% endfor %}
</table>
{% endwith %}

View File

@ -1,15 +1,36 @@
{% load i18n easy_thumbnails_tags aircox %} {% load i18n easy_thumbnails_tags aircox %}
{% comment %} {% comment %}
Context variables: Context variables:
- object: the actual diffusion - object: the object to render
- show_headline: if True, display headline - render_card: render as card
- is-primary: render as primary
- has_headline (=False): if True, display headline
- has_cover (=True): hide page cover
{% endcomment %} {% endcomment %}
<article class="media"> {% if render_card %}
<article class="card {% if is_primary %}is-primary{% endif %}">
<header class="card-image">
<figure class="image is-4by3">
<img src="{% thumbnail object.cover 480x480 %}">
</figure>
</header>
<div class="card-header">
<h4 class="title">
<a href="{{ object.get_absolute_url }}">{{ object.title }}</a>
</h4>
</div>
</article>
{% else %}
<article class="media item {% if is_primary %}is-primary{% endif %}">
{% if has_cover|default_if_none:True %}
<div class="media-left"> <div class="media-left">
<img src="{% thumbnail object.cover 128x128 crop=scale %}" <img src="{% thumbnail object.cover 128x128 crop=scale %}"
class="small-cover"> class="small-cover">
</div> </div>
{% endif %}
<div class="media-content"> <div class="media-content">
<h5 class="title is-5 has-text-weight-normal"> <h5 class="title is-5 has-text-weight-normal">
{% block title %} {% block title %}
@ -26,8 +47,8 @@ Context variables:
{% endblock %} {% endblock %}
</div> </div>
{% if show_headline %} {% if has_headline|default_if_none:True %}
<div class=""> <div class="headline">
{% block headline %} {% block headline %}
{{ object.headline|safe }} {{ object.headline|safe }}
{% endblock %} {% endblock %}
@ -35,4 +56,5 @@ Context variables:
{% endif %} {% endif %}
</div> </div>
</article> </article>
{% endif %}

View File

@ -0,0 +1,28 @@
{% comment %}
Display list of items as small list
Context:
- object_list: object list
- list_url: url to complete list page
{% endcomment %}
{% load i18n %}
{% for object in object_list %}
{% include object.item_template_name %}
{% endfor %}
{% if list_url %}
<br>
<nav class="pagination is-centered">
<ul class="pagination-list">
<li>
<a href="{{ list_url }}" class="pagination-link"
aria-label="{% trans "Show all publications" %}">
{% trans "Show more" %}
</a>
</li>
</ul>
</nav>
{% endif %}

View File

@ -24,8 +24,7 @@ api = [
urls = [ urls = [
path(_(''), path('', views.HomeView.as_view(), name='home'),
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),
@ -51,9 +50,9 @@ urls = [
# path('<page_path:path>', views.route_page, name='page'), # path('<page_path:path>', views.route_page, name='page'),
path(_('publications/'), path(_('publications/'),
views.ProgramPageListView.as_view(), name='page-list'), views.PageListView.as_view(model=models.Page), name='page-list'),
path(_('programs/'), views.PageListView.as_view(model=models.Program), path(_('programs/'), views.ProgramListView.as_view(model=models.Program),
name='program-list'), name='program-list'),
path(_('programs/<slug:slug>/'), path(_('programs/<slug:slug>/'),
views.ProgramDetailView.as_view(), name='program-detail'), views.ProgramDetailView.as_view(), name='program-detail'),
@ -62,7 +61,7 @@ urls = [
path(_('programs/<slug:parent_slug>/articles/'), path(_('programs/<slug:parent_slug>/articles/'),
views.ArticleListView.as_view(), name='article-list'), views.ArticleListView.as_view(), name='article-list'),
path(_('programs/<slug:parent_slug>/publications/'), path(_('programs/<slug:parent_slug>/publications/'),
views.ProgramPageListView.as_view(), name='page-list'), views.ProgramPageListView.as_view(), name='program-page-list'),
] ]

View File

@ -1,9 +1,12 @@
from . import api, admin from . import api, admin
from .article import ArticleDetailView, ArticleListView
from .base import BaseView from .base import BaseView
from .home import HomeView
from .article import ArticleDetailView, ArticleListView
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, ProgramPageListView from .program import ProgramDetailView, ProgramListView, \
ProgramPageDetailView, ProgramPageListView

View File

@ -44,8 +44,7 @@ class AdminSite(admin.AdminSite):
def get_urls(self): def get_urls(self):
from django.urls import path, include from django.urls import path, include
urls = super().get_urls() urls = super().get_urls() + [
urls += [
path('tools/statistics/', path('tools/statistics/',
self.admin_view(StatisticsView.as_view()), self.admin_view(StatisticsView.as_view()),
name='tools-stats'), name='tools-stats'),

View File

@ -4,7 +4,6 @@ from django.utils import timezone as tz
from rest_framework.generics import ListAPIView from rest_framework.generics import ListAPIView
from ..utils import str_to_date
from ..models import Log from ..models import Log
from ..serializers import LogInfo, LogInfoSerializer from ..serializers import LogInfo, LogInfoSerializer
from .log import LogListMixin from .log import LogListMixin

View File

@ -25,7 +25,7 @@ class ArticleDetailView(PageDetailView):
class ArticleListView(ParentMixin, PageListView): class ArticleListView(ParentMixin, PageListView):
model = Article model = Article
template_name = 'aircox/article_list.html' template_name = 'aircox/article_list.html'
show_headline = True has_headline = True
is_static = False is_static = False
parent_model = Program parent_model = Program

View File

@ -2,6 +2,7 @@
from django.http import Http404 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 django.urls import reverse
from ..models import Page from ..models import Page
from ..utils import Redirect from ..utils import Redirect
@ -35,21 +36,25 @@ class BaseView(TemplateResponseMixin, ContextMixin):
return Page.objects.select_subclasses().published() \ return Page.objects.select_subclasses().published() \
.order_by('-pub_date') .order_by('-pub_date')
def get_context_data(self, sidebar_items=None, **kwargs): def get_sidebar_url(self):
return reverse('page-list')
def get_context_data(self, **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) kwargs.setdefault('has_filters', self.has_filters)
has_sidebar = kwargs.setdefault('has_sidebar', self.has_sidebar) has_sidebar = kwargs.setdefault('has_sidebar', self.has_sidebar)
if has_sidebar and sidebar_items is None: if has_sidebar and 'sidebar_object_list' not in kwargs:
sidebar_items = self.get_sidebar_queryset() sidebar_object_list = self.get_sidebar_queryset()
sidebar_items = None if sidebar_items is None else \ if sidebar_object_list is not None:
sidebar_items[:self.list_count] kwargs['sidebar_object_list'] = sidebar_object_list[:self.list_count]
kwargs['sidebar_list_url'] = self.get_sidebar_url()
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(sidebar_items=sidebar_items, **kwargs) return super().get_context_data(**kwargs)

View File

@ -21,9 +21,6 @@ class EpisodeDetailView(ProgramPageDetailView):
return Sound.objects.diffusion(diffusion).podcasts() return Sound.objects.diffusion(diffusion).podcasts()
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
self.program = kwargs.setdefault('program', self.object.program)
kwargs.setdefault('parent', self.program)
if not 'tracks' in kwargs: if not 'tracks' in kwargs:
kwargs['tracks'] = self.object.track_set.order_by('position') kwargs['tracks'] = self.object.track_set.order_by('position')
if not 'podcasts' in kwargs: if not 'podcasts' in kwargs:
@ -33,8 +30,8 @@ class EpisodeDetailView(ProgramPageDetailView):
class EpisodeListView(ParentMixin, PageListView): class EpisodeListView(ParentMixin, PageListView):
model = Episode model = Episode
item_template_name = 'aircox/episode_item.html' item_template_name = 'aircox/widgets/episode_item.html'
show_headline = True has_headline = True
parent_model = Program parent_model = Program

44
aircox/views/home.py Normal file
View File

@ -0,0 +1,44 @@
import datetime
from django.utils.translation import ugettext as _
from django.utils import timezone as tz
from ..models import Diffusion, Log, Page
from .page import PageListView
class HomeView(PageListView):
template_name = 'aircox/home.html'
model = Page
queryset = Page.objects.select_subclasses()
paginate_by = 10
list_count = 40
logs_count = 5
has_filters = False
def get_logs(self):
today = datetime.date.today()
logs = Log.objects.on_air().today(today).filter(track__isnull=False)
diffs = Diffusion.objects.on_air().today(today)
return Log.merge_diffusions(logs, diffs, self.logs_count)
def get_sidebar_queryset(self):
today = datetime.date.today()
return Diffusion.objects.on_air().today(today)
def get_top_diffs(self):
now = tz.now()
current_diff = Diffusion.objects.on_air().now(now).first()
next_diffs = Diffusion.objects.on_air().after(now)
if current_diff:
diffs = [current_diff] + list(next_diffs.exclude(pk=current_diff.pk)[:2])
else:
diffs = next_diffs[:3]
return diffs
def get_context_data(self, **kwargs):
kwargs['logs'] = self.get_logs()
kwargs['top_diffs'] = self.get_top_diffs()
return super().get_context_data(**kwargs)

View File

@ -2,6 +2,7 @@ from collections import deque
import datetime import datetime
from django.views.generic import ListView from django.views.generic import ListView
from django.utils import timezone as tz
from ..models import Diffusion, Log from ..models import Diffusion, Log
from .base import BaseView from .base import BaseView
@ -23,12 +24,14 @@ class LogListMixin(GetDateMixin):
def get_queryset(self): def get_queryset(self):
# only get logs for tracks: log for diffusion will be retrieved # only get logs for tracks: log for diffusion will be retrieved
# by the diffusions' queryset. # by the diffusions' queryset.
qs = super().get_queryset().on_air().filter(track__isnull=False) qs = super().get_queryset().on_air().filter(track__isnull=False) \
.filter(date__lte=tz.now())
return qs.today(self.date) if self.date is not None else \ return qs.today(self.date) if self.date is not None else \
qs.after(self.min_date) if self.min_date is not None else qs qs.after(self.min_date) if self.min_date is not None else qs
def get_diffusions_queryset(self): def get_diffusions_queryset(self):
qs = Diffusion.objects.station(self.station).on_air() qs = Diffusion.objects.station(self.station).on_air() \
.filter(start__lte=tz.now())
return qs.today(self.date) if self.date is not None else \ return qs.today(self.date) if self.date is not None else \
qs.after(self.min_date) if self.min_date is not None else qs qs.after(self.min_date) if self.min_date is not None else qs

View File

@ -63,9 +63,9 @@ class ParentMixin:
return super().get_queryset() return super().get_queryset()
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
parent = kwargs.setdefault('parent', self.parent) self.parent = kwargs.setdefault('parent', self.parent)
if parent is not None: if self.parent is not None:
kwargs.setdefault('cover', parent.cover) kwargs.setdefault('cover', self.parent.cover)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)

View File

@ -6,7 +6,7 @@ from django.views.generic import DetailView, ListView
from honeypot.decorators import check_honeypot from honeypot.decorators import check_honeypot
from ..forms import CommentForm from ..forms import CommentForm
from ..models import Category, Comment from ..models import Category, Comment, Page
from ..utils import Redirect from ..utils import Redirect
from .base import BaseView from .base import BaseView
@ -17,12 +17,12 @@ __all__ = ['PageDetailView', 'PageListView']
# TODO: pagination: in template, only a limited number of pages displayed # TODO: pagination: in template, only a limited number of pages displayed
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/widgets/page_item.html'
has_sidebar = True has_sidebar = True
has_filters = True has_filters = True
paginate_by = 20 paginate_by = 20
show_headline = True has_headline = True
categories = None categories = None
def get(self, *args, **kwargs): def get(self, *args, **kwargs):
@ -30,7 +30,7 @@ class PageListView(BaseView, ListView):
return super().get(*args, **kwargs) return super().get(*args, **kwargs)
def get_queryset(self): def get_queryset(self):
qs = super().get_queryset().published() \ qs = super().get_queryset().select_subclasses().published() \
.select_related('cover', 'category') .select_related('cover', 'category')
# category can be filtered based on request.GET['categories'] # category can be filtered based on request.GET['categories']
@ -50,7 +50,7 @@ class PageListView(BaseView, ListView):
kwargs.setdefault('item_template_name', self.item_template_name) kwargs.setdefault('item_template_name', self.item_template_name)
kwargs.setdefault('filter_categories', self.get_categories_queryset()) kwargs.setdefault('filter_categories', self.get_categories_queryset())
kwargs.setdefault('categories', self.categories) kwargs.setdefault('categories', self.categories)
kwargs.setdefault('show_headline', self.show_headline) kwargs.setdefault('has_headline', self.has_headline)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)

View File

@ -1,6 +1,7 @@
from django.db.models import Q 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 django.urls import reverse
from ..models import Episode, Program, Page from ..models import Episode, Program, Page
from .mixins import ParentMixin from .mixins import ParentMixin
@ -10,30 +11,54 @@ from .page import PageDetailView, PageListView
__all__ = ['ProgramPageDetailView', 'ProgramDetailView', 'ProgramPageListView'] __all__ = ['ProgramPageDetailView', 'ProgramDetailView', 'ProgramPageListView']
class ProgramPageDetailView(PageDetailView): class BaseProgramMixin:
def get_program(self):
return self.object
def get_sidebar_url(self):
return reverse('program-page-list',
kwargs={"parent_slug": self.program.slug})
def get_context_data(self, **kwargs):
self.program = self.get_program()
kwargs['program'] = self.program
return super().get_context_data(**kwargs)
class ProgramDetailView(BaseProgramMixin, PageDetailView):
model = Program
def get_sidebar_queryset(self):
return super().get_sidebar_queryset().filter(parent=self.program)
class ProgramListView(PageListView):
model = Program
class ProgramPageDetailView(BaseProgramMixin, ParentMixin, 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 parent_model = Program
has_sidebar = True
list_count = 5 def get_program(self):
self.parent = self.object.program
return self.object.program
def get_sidebar_queryset(self): def get_sidebar_queryset(self):
return super().get_sidebar_queryset().filter(parent=self.object) return super().get_sidebar_queryset().filter(parent=self.program)
class ProgramPageListView(ParentMixin, PageListView): class ProgramPageListView(BaseProgramMixin, ParentMixin, PageListView):
model = Page model = Page
parent_model = Program parent_model = Program
queryset = Page.objects.select_subclasses() queryset = Page.objects.select_subclasses()
def get_program(self):
class ProgramDetailView(ProgramPageDetailView): return self.parent
model = Program
def get_context_data(self, **kwargs): def get_context_data(self, **kwargs):
self.program = kwargs.setdefault('program', self.object) kwargs.setdefault('sidebar_url_parent', None)
return super().get_context_data(**kwargs) return super().get_context_data(**kwargs)

View File

@ -1,10 +1,4 @@
// @import './bulmacolors';
.navbar {
box-shadow: 0em 0em 0.6em rgba(0, 0, 0, 0.4);
}
.navbar .navbar-brand { .navbar .navbar-brand {
padding-right: 1em; padding-right: 1em;
} }

View File

@ -26,6 +26,10 @@ 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;
},
onclick(event) {
// TODO: row click => check checkbox
} }
}, },

View File

@ -83,6 +83,7 @@ export default {
}, },
toggle() { toggle() {
console.log('tooogle', this.paused, '-', this.$refs.audio.src)
if(this.paused) if(this.paused)
this.play() this.play()
else else

View File

@ -3,7 +3,7 @@
$body-background-color: $light; $body-background-color: $light;
@import "~bulma/bulma"; @import "~bulma";
//-- helpers/modifiers //-- helpers/modifiers
.is-fullwidth { width: 100%; } .is-fullwidth { width: 100%; }
@ -58,6 +58,38 @@ a.navbar-item.is-active {
} }
} }
//-- cards
.card {
.title {
a {
color: $dark;
}
padding: 0.2em;
font-size: $size-5;
font-weight: $weight-medium;
}
&.is-primary {
box-shadow: 0em 0em 0.5em $black
}
}
.card-super-title {
position: absolute;
z-index: 1000;
font-size: 1.2em;
font-weight: $weight-bold;
padding: 0.2em;
top: 1em;
background-color: $white;
.fas {
padding: 0.1em;
font-size: 0.8em;
}
}
//-- filters //-- filters
.filters { .filters {
@ -77,18 +109,7 @@ a.navbar-item.is-active {
} }
/* //-- page
.navbar-brand img {
min-height: 6em;
}
.navbar-menu .navbar-item:not(:last-child) {
border-right: 1px $grey solid;
}
*/
/** page **/
.page { .page {
& > .cover { & > .cover {
float: right; float: right;
@ -109,39 +130,33 @@ a.navbar-item.is-active {
} }
} }
section > .toolbar {
background-color: rgba(0,0,0,0.05);
padding: 1em;
margin-bottom: 1.5em;
}
.media.item .headline {
line-height: 1.2em;
max-height: calc(1.2em * 3);
overflow: hidden;
main { & + .headline-overflow {
.cover { position: relative;
margin: 1em 0em; width: 100%;
border: 0.2em black solid; height: 2em;
margin-top: -2em;
} }
.small-cover { & + .headline-overflow:before {
width: 10em; content:'';
} width:100%;
} height:100%;
position:absolute;
aside { left:0;
.small-cover { bottom:0;
width: 4em; background:linear-gradient(transparent 1em, $body-background-color);
}
.media .subtitle {
font-size: 1em;
}
.media .content {
display: none;
} }
} }
//-- player
.player { .player {
box-shadow: 0em 1.5em 2.5em rgba(0, 0, 0, 0.6); box-shadow: 0em 1.5em 2.5em rgba(0, 0, 0, 0.6);
@ -175,4 +190,57 @@ aside {
} }
//-- media
.media {
.subtitle {
margin-bottom: 0.4em;
}
.media-content .headline {
font-size: 1em;
font-weight: 400;
}
}
//-- general
body {
background-color: $body-background-color;
}
section > .toolbar {
background-color: rgba(0,0,0,0.05);
padding: 1em;
margin-bottom: 1.5em;
}
main {
.cover {
margin: 1em 0em;
border: 0.2em black solid;
}
.small-cover {
width: 10em;
}
}
aside {
& > section {
margin-bottom: 2em;
}
.cover {
margin-bottom: 2em;
}
.small-cover {
width: 4em;
}
.media .subtitle {
font-size: 1em;
}
}

View File

@ -22,9 +22,8 @@ import aircox.urls
try: try:
urlpatterns = [ urlpatterns = aircox.urls.urls + [
path('admin/', admin.site.urls), path('admin/', admin.site.urls),
path('aircox/', include(aircox.urls.urls)),
] ]
if settings.DEBUG: if settings.DEBUG: