start to work on stats
This commit is contained in:
		@ -19,7 +19,7 @@ class Streamer:
 | 
			
		||||
    """
 | 
			
		||||
    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
 | 
			
		||||
    file in self.path file
 | 
			
		||||
 | 
			
		||||
@ -137,7 +137,7 @@ class Command (BaseCommand):
 | 
			
		||||
        group.add_argument(
 | 
			
		||||
            '--check', action='store_true',
 | 
			
		||||
            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.'
 | 
			
		||||
        )
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@ -105,7 +105,8 @@ class Monitor:
 | 
			
		||||
            source = current_source.id,
 | 
			
		||||
            date = tz.now(),
 | 
			
		||||
            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):
 | 
			
		||||
 | 
			
		||||
@ -377,52 +377,6 @@ class Program(Nameable):
 | 
			
		||||
        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):
 | 
			
		||||
    """
 | 
			
		||||
    When there are no program scheduled, it is possible to play sounds
 | 
			
		||||
@ -671,6 +625,52 @@ class Schedule(models.Model):
 | 
			
		||||
        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):
 | 
			
		||||
    """
 | 
			
		||||
    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.
 | 
			
		||||
 | 
			
		||||
    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):
 | 
			
		||||
        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 = [
 | 
			
		||||
    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 datetime
 | 
			
		||||
 | 
			
		||||
from django.db.models import Count
 | 
			
		||||
from django.views.generic.base import View, TemplateResponseMixin
 | 
			
		||||
from django.contrib.auth.mixins import LoginRequiredMixin
 | 
			
		||||
from django.http import HttpResponse, Http404
 | 
			
		||||
@ -82,7 +84,7 @@ class Monitor(View,TemplateResponseMixin,LoginRequiredMixin):
 | 
			
		||||
        stations.fetch()
 | 
			
		||||
        return { 'stations': stations.stations }
 | 
			
		||||
 | 
			
		||||
    def get (self, request = None, **kwargs):
 | 
			
		||||
    def get(self, request = None, **kwargs):
 | 
			
		||||
        if not request.user.is_active:
 | 
			
		||||
            return Http404()
 | 
			
		||||
 | 
			
		||||
@ -90,7 +92,7 @@ class Monitor(View,TemplateResponseMixin,LoginRequiredMixin):
 | 
			
		||||
        context = self.get_context_data(**kwargs)
 | 
			
		||||
        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:
 | 
			
		||||
            return Http404()
 | 
			
		||||
 | 
			
		||||
@ -121,6 +123,92 @@ class Monitor(View,TemplateResponseMixin,LoginRequiredMixin):
 | 
			
		||||
        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)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
		Reference in New Issue
	
	Block a user