start to work on stats
This commit is contained in:
parent
3ab373097b
commit
76055c742a
|
@ -19,7 +19,7 @@ class Streamer:
|
||||||
"""
|
"""
|
||||||
Related station
|
Related station
|
||||||
"""
|
"""
|
||||||
template_name = 'aircox/controllers/liquidsoap.liq'
|
template_name = 'aircox/config/liquidsoap.liq'
|
||||||
"""
|
"""
|
||||||
If set, use this template in order to generated the configuration
|
If set, use this template in order to generated the configuration
|
||||||
file in self.path file
|
file in self.path file
|
||||||
|
|
|
@ -137,7 +137,7 @@ class Command (BaseCommand):
|
||||||
group.add_argument(
|
group.add_argument(
|
||||||
'--check', action='store_true',
|
'--check', action='store_true',
|
||||||
help='check unconfirmed later diffusions from the given '
|
help='check unconfirmed later diffusions from the given '
|
||||||
'date again'\'t schedule. If no schedule is found, remove '
|
'date agains\'t schedule. If no schedule is found, remove '
|
||||||
'it.'
|
'it.'
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -105,7 +105,8 @@ class Monitor:
|
||||||
source = current_source.id,
|
source = current_source.id,
|
||||||
date = tz.now(),
|
date = tz.now(),
|
||||||
related = sound[0] if sound else None,
|
related = sound[0] if sound else None,
|
||||||
comment = None if sound else current_sound,
|
# keep sound path (if sound is removed, we keep that info)
|
||||||
|
comment = current_sound,
|
||||||
)
|
)
|
||||||
|
|
||||||
def trace_sound_tracks(self, log):
|
def trace_sound_tracks(self, log):
|
||||||
|
|
|
@ -377,52 +377,6 @@ class Program(Nameable):
|
||||||
return qs[0] if qs else None
|
return qs[0] if qs else None
|
||||||
|
|
||||||
|
|
||||||
class DiffusionManager(models.Manager):
|
|
||||||
def get_at(self, date = None, next = False):
|
|
||||||
"""
|
|
||||||
Return a queryset of diffusions that have the given date
|
|
||||||
in their range.
|
|
||||||
|
|
||||||
If date is a datetime.date object, check only against the
|
|
||||||
date.
|
|
||||||
"""
|
|
||||||
date = date or tz.now()
|
|
||||||
if not issubclass(type(date), datetime.datetime):
|
|
||||||
return self.filter(
|
|
||||||
models.Q(start__contains = date) | \
|
|
||||||
models.Q(end__contains = date)
|
|
||||||
)
|
|
||||||
|
|
||||||
if not next:
|
|
||||||
return self.filter(start__lte = date, end__gte = date) \
|
|
||||||
.order_by('start')
|
|
||||||
|
|
||||||
return self.filter(
|
|
||||||
models.Q(start__lte = date, end__gte = date) |
|
|
||||||
models.Q(start__gte = date),
|
|
||||||
).order_by('start')
|
|
||||||
|
|
||||||
def get_after(self, date = None):
|
|
||||||
"""
|
|
||||||
Return a queryset of diffusions that happen after the given
|
|
||||||
date.
|
|
||||||
"""
|
|
||||||
date = date_or_default(date)
|
|
||||||
return self.filter(
|
|
||||||
start__gte = date,
|
|
||||||
).order_by('start')
|
|
||||||
|
|
||||||
def get_before(self, date):
|
|
||||||
"""
|
|
||||||
Return a queryset of diffusions that finish before the given
|
|
||||||
date.
|
|
||||||
"""
|
|
||||||
date = date_or_default(date)
|
|
||||||
return self.filter(
|
|
||||||
end__lte = date,
|
|
||||||
).order_by('start')
|
|
||||||
|
|
||||||
|
|
||||||
class Stream(models.Model):
|
class Stream(models.Model):
|
||||||
"""
|
"""
|
||||||
When there are no program scheduled, it is possible to play sounds
|
When there are no program scheduled, it is possible to play sounds
|
||||||
|
@ -671,6 +625,52 @@ class Schedule(models.Model):
|
||||||
verbose_name_plural = _('Schedules')
|
verbose_name_plural = _('Schedules')
|
||||||
|
|
||||||
|
|
||||||
|
class DiffusionManager(models.Manager):
|
||||||
|
def get_at(self, date = None, next = False):
|
||||||
|
"""
|
||||||
|
Return a queryset of diffusions that have the given date
|
||||||
|
in their range.
|
||||||
|
|
||||||
|
If date is a datetime.date object, check only against the
|
||||||
|
date.
|
||||||
|
"""
|
||||||
|
date = date or tz.now()
|
||||||
|
if not issubclass(type(date), datetime.datetime):
|
||||||
|
return self.filter(
|
||||||
|
models.Q(start__contains = date) | \
|
||||||
|
models.Q(end__contains = date)
|
||||||
|
)
|
||||||
|
|
||||||
|
if not next:
|
||||||
|
return self.filter(start__lte = date, end__gte = date) \
|
||||||
|
.order_by('start')
|
||||||
|
|
||||||
|
return self.filter(
|
||||||
|
models.Q(start__lte = date, end__gte = date) |
|
||||||
|
models.Q(start__gte = date),
|
||||||
|
).order_by('start')
|
||||||
|
|
||||||
|
def get_after(self, date = None):
|
||||||
|
"""
|
||||||
|
Return a queryset of diffusions that happen after the given
|
||||||
|
date.
|
||||||
|
"""
|
||||||
|
date = date_or_default(date)
|
||||||
|
return self.filter(
|
||||||
|
start__gte = date,
|
||||||
|
).order_by('start')
|
||||||
|
|
||||||
|
def get_before(self, date):
|
||||||
|
"""
|
||||||
|
Return a queryset of diffusions that finish before the given
|
||||||
|
date.
|
||||||
|
"""
|
||||||
|
date = date_or_default(date)
|
||||||
|
return self.filter(
|
||||||
|
end__lte = date,
|
||||||
|
).order_by('start')
|
||||||
|
|
||||||
|
|
||||||
class Diffusion(models.Model):
|
class Diffusion(models.Model):
|
||||||
"""
|
"""
|
||||||
A Diffusion is an occurrence of a Program that is scheduled on the
|
A Diffusion is an occurrence of a Program that is scheduled on the
|
||||||
|
@ -1036,7 +1036,7 @@ class Log(Related):
|
||||||
Log sounds and diffusions that are played on the station.
|
Log sounds and diffusions that are played on the station.
|
||||||
|
|
||||||
This only remember what has been played on the outputs, not on each
|
This only remember what has been played on the outputs, not on each
|
||||||
track; Source designate here which source is responsible of that.
|
source; Source designate here which source is responsible of that.
|
||||||
"""
|
"""
|
||||||
class Type(IntEnum):
|
class Type(IntEnum):
|
||||||
stop = 0x00
|
stop = 0x00
|
||||||
|
|
46
aircox/templates/aircox/controllers/stats.html
Normal file
46
aircox/templates/aircox/controllers/stats.html
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
{% load i18n %}
|
||||||
|
|
||||||
|
<div id='stats'>
|
||||||
|
{% for stats in statistics %}
|
||||||
|
<section class="station">
|
||||||
|
<header>
|
||||||
|
<h1>{{ stats.station.name }}</h1>
|
||||||
|
<h2>- {{ stats.date|date:'l d F Y' }}</h2>
|
||||||
|
</header>
|
||||||
|
|
||||||
|
<table border=1>
|
||||||
|
<tr>
|
||||||
|
<th>{% trans "Date" %}</th>
|
||||||
|
{# Translators "Header for statistics view" #}
|
||||||
|
<th>{% trans "Diffusion or sound played" %}
|
||||||
|
<th colspan="100"></th>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
{% for item in stats.items %}
|
||||||
|
<tr>
|
||||||
|
<td>{{ item.date|date:"H:i" }}</td>
|
||||||
|
{# TODO: logs #}
|
||||||
|
<td>{{ item.program.name }}</td>
|
||||||
|
{% for tag,count in item.tags %}
|
||||||
|
<td>{{ tag }}: {{ count }}</td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
|
<tr>
|
||||||
|
<td>{{ stats.date|date:'d/m/Y' }}</td>
|
||||||
|
<td>{% trans "Total and average" %} ({{ stats.tracks_count }})</td>
|
||||||
|
{% for tag, count, average in stats.tags %}
|
||||||
|
<td>{{ tag }}: <b>{{ count }} / {{ average|floatformat }}%</b></td>
|
||||||
|
{% endfor %}
|
||||||
|
</tr>
|
||||||
|
</table>
|
||||||
|
</section>
|
||||||
|
{% endfor %}
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import aircox.views as views
|
||||||
|
|
||||||
urls = [
|
urls = [
|
||||||
url(r'^on_air', views.on_air, name='aircox.on_air'),
|
url(r'^on_air', views.on_air, name='aircox.on_air'),
|
||||||
url(r'^monitor', views.Monitor.as_view(), name='aircox.monitor')
|
url(r'^monitor', views.Monitor.as_view(), name='aircox.monitor'),
|
||||||
|
url(r'^stats', views.StatisticsView.as_view(), name='aircox.stats'),
|
||||||
]
|
]
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,7 @@
|
||||||
import json
|
import json
|
||||||
|
import datetime
|
||||||
|
|
||||||
|
from django.db.models import Count
|
||||||
from django.views.generic.base import View, TemplateResponseMixin
|
from django.views.generic.base import View, TemplateResponseMixin
|
||||||
from django.contrib.auth.mixins import LoginRequiredMixin
|
from django.contrib.auth.mixins import LoginRequiredMixin
|
||||||
from django.http import HttpResponse, Http404
|
from django.http import HttpResponse, Http404
|
||||||
|
@ -82,7 +84,7 @@ class Monitor(View,TemplateResponseMixin,LoginRequiredMixin):
|
||||||
stations.fetch()
|
stations.fetch()
|
||||||
return { 'stations': stations.stations }
|
return { 'stations': stations.stations }
|
||||||
|
|
||||||
def get (self, request = None, **kwargs):
|
def get(self, request = None, **kwargs):
|
||||||
if not request.user.is_active:
|
if not request.user.is_active:
|
||||||
return Http404()
|
return Http404()
|
||||||
|
|
||||||
|
@ -90,7 +92,7 @@ class Monitor(View,TemplateResponseMixin,LoginRequiredMixin):
|
||||||
context = self.get_context_data(**kwargs)
|
context = self.get_context_data(**kwargs)
|
||||||
return render(request, self.template_name, context)
|
return render(request, self.template_name, context)
|
||||||
|
|
||||||
def post (self, request = None, **kwargs):
|
def post(self, request = None, **kwargs):
|
||||||
if not request.user.is_active:
|
if not request.user.is_active:
|
||||||
return Http404()
|
return Http404()
|
||||||
|
|
||||||
|
@ -121,6 +123,92 @@ class Monitor(View,TemplateResponseMixin,LoginRequiredMixin):
|
||||||
return HttpResponse('')
|
return HttpResponse('')
|
||||||
|
|
||||||
|
|
||||||
|
class StatisticsView(View,TemplateResponseMixin,LoginRequiredMixin):
|
||||||
|
template_name = 'aircox/controllers/stats.html'
|
||||||
|
|
||||||
|
class Stats:
|
||||||
|
station = None
|
||||||
|
date = None
|
||||||
|
items = None
|
||||||
|
"""
|
||||||
|
Log or Diffusion object that has been diffused by date. These
|
||||||
|
objects have extra fields:
|
||||||
|
- tags: [ (tag_name, tag_count), ...]
|
||||||
|
- tracks_count: total count of tracks
|
||||||
|
"""
|
||||||
|
tags = None
|
||||||
|
"""
|
||||||
|
Total of played track's tags: [(tag_name, tag_count, tag_average), ...]
|
||||||
|
on the station for the given date. Note: tag_average is in %
|
||||||
|
"""
|
||||||
|
tracks_count = 0
|
||||||
|
|
||||||
|
def __init__(self, **kwargs):
|
||||||
|
self.items = []
|
||||||
|
self.tags = []
|
||||||
|
self.__dict__.update(kwargs)
|
||||||
|
|
||||||
|
def get_stats(self, station, date):
|
||||||
|
"""
|
||||||
|
Return statistics for the given station and date.
|
||||||
|
"""
|
||||||
|
items = station.on_air(date)
|
||||||
|
stats = self.Stats(station = station, items = items, date = date)
|
||||||
|
|
||||||
|
sums = {}
|
||||||
|
total = 0
|
||||||
|
|
||||||
|
for item in items:
|
||||||
|
qs = models.Track.objects.get_for(item)
|
||||||
|
item.tracks = qs
|
||||||
|
item.tracks_count = qs.count()
|
||||||
|
|
||||||
|
qs = qs.values('tags__name').annotate(count = Count('tags__name')) \
|
||||||
|
.order_by('tags__name')
|
||||||
|
item.tags = [
|
||||||
|
(q['tags__name'], q['count'])
|
||||||
|
for q in qs if q['tags__name']
|
||||||
|
]
|
||||||
|
for name, count in item.tags:
|
||||||
|
sums[name] = (sums.get(name) or 0) + count
|
||||||
|
|
||||||
|
total += item.tracks_count
|
||||||
|
|
||||||
|
stats.tracks_count = total
|
||||||
|
stats.tags = [
|
||||||
|
(name, count, count / total * 100)
|
||||||
|
for name, count in sums.items()
|
||||||
|
]
|
||||||
|
stats.tags.sort(key=lambda s: s[0])
|
||||||
|
return stats
|
||||||
|
|
||||||
|
def get_context_data(self, **kwargs):
|
||||||
|
context = {}
|
||||||
|
date = datetime.date.today()
|
||||||
|
|
||||||
|
try:
|
||||||
|
GET = self.request.GET
|
||||||
|
year = int(GET["year"]) if 'year' in GET else date.year
|
||||||
|
month = int(GET["month"]) if 'month' in GET else date.month
|
||||||
|
day = int(GET["day"]) if 'day' in GET else date.day
|
||||||
|
date = datetime.date(year, month, day)
|
||||||
|
except:
|
||||||
|
pass
|
||||||
|
|
||||||
|
context["statistics"] = [
|
||||||
|
self.get_stats(station, date)
|
||||||
|
for station in models.Station.objects.all()
|
||||||
|
]
|
||||||
|
|
||||||
|
return context
|
||||||
|
|
||||||
|
def get(self, request = None, **kwargs):
|
||||||
|
if not request.user.is_active:
|
||||||
|
return Http404()
|
||||||
|
|
||||||
|
self.request = request
|
||||||
|
context = self.get_context_data(**kwargs)
|
||||||
|
return render(request, self.template_name, context)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user