imrpove statistics rendering + filters
This commit is contained in:
		@ -33,6 +33,9 @@ class LogQuerySet(models.QuerySet):
 | 
				
			|||||||
    def after(self, date):
 | 
					    def after(self, date):
 | 
				
			||||||
        return self.filter(date__gte=date) if isinstance(date, tz.datetime) else self.filter(date__date__gte=date)
 | 
					        return self.filter(date__gte=date) if isinstance(date, tz.datetime) else self.filter(date__date__gte=date)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def before(self, date):
 | 
				
			||||||
 | 
					        return self.filter(date__lte=date) if isinstance(date, tz.datetime) else self.filter(date__date__lte=date)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def on_air(self):
 | 
					    def on_air(self):
 | 
				
			||||||
        return self.filter(type=Log.TYPE_ON_AIR)
 | 
					        return self.filter(type=Log.TYPE_ON_AIR)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -6,15 +6,20 @@
 | 
				
			|||||||
{% block content-container %}
 | 
					{% block content-container %}
 | 
				
			||||||
<div class="container">
 | 
					<div class="container">
 | 
				
			||||||
 | 
					
 | 
				
			||||||
{# TODO: date subtitle #}
 | 
					<form method="GET" class="box mt-3 mb-3">
 | 
				
			||||||
{% comment %}
 | 
					    <h3 class="title is-3">{% translate "Filter by date" %}</h3>
 | 
				
			||||||
<nav class="navbar" role="menu">
 | 
					    <div class="flex-row gap-3" style="align-items: flex-end">
 | 
				
			||||||
    {% with "admin:tools-stats" as url_name %}
 | 
					        <div class="field mb-0">
 | 
				
			||||||
    {% include "aircox/widgets/dates_menu.html" %}
 | 
					            <label class="label">{% translate "from" %}</label>
 | 
				
			||||||
    {% endwith %}
 | 
					            <input type="date" class="input" name="min_date" value="{{ min_date|date:"Y-m-d" }}"/>
 | 
				
			||||||
</nav>
 | 
					        </div>
 | 
				
			||||||
{% endcomment %}
 | 
					        <div class="field mb-0">
 | 
				
			||||||
 | 
					            <label class="label">{% translate "... to" %}</label>
 | 
				
			||||||
 | 
					            <input type="date" class="input" name="max_date" value="{{ max_date|date:"Y-m-d" }}"/>
 | 
				
			||||||
 | 
					        </div>
 | 
				
			||||||
 | 
					        <button class="button">{% translate "Apply" %}</button>
 | 
				
			||||||
 | 
					    </div>
 | 
				
			||||||
 | 
					</form>
 | 
				
			||||||
<a-statistics class="column">
 | 
					<a-statistics class="column">
 | 
				
			||||||
<template v-slot="{counts}">
 | 
					<template v-slot="{counts}">
 | 
				
			||||||
    <table class="table is-hoverable is-fullwidth">
 | 
					    <table class="table is-hoverable is-fullwidth">
 | 
				
			||||||
@ -26,7 +31,14 @@
 | 
				
			|||||||
            </tr>
 | 
					            </tr>
 | 
				
			||||||
        </thead>
 | 
					        </thead>
 | 
				
			||||||
        <tbody>
 | 
					        <tbody>
 | 
				
			||||||
        {% for object in object_list %}
 | 
					        {% regroup object_list by date.date as by_date %}
 | 
				
			||||||
 | 
					        {% for date, objects in by_date %}
 | 
				
			||||||
 | 
					            <tr>
 | 
				
			||||||
 | 
					                <th colspan="3">
 | 
				
			||||||
 | 
					                    {{ date|date:"l - d F Y" }}
 | 
				
			||||||
 | 
					                </th>
 | 
				
			||||||
 | 
					            </tr>
 | 
				
			||||||
 | 
					            {% for object in objects %}
 | 
				
			||||||
            {% with object|is_diffusion as is_diff %}
 | 
					            {% with object|is_diffusion as is_diff %}
 | 
				
			||||||
            {% if is_diff %}
 | 
					            {% if is_diff %}
 | 
				
			||||||
            <tr class="bg-main">
 | 
					            <tr class="bg-main">
 | 
				
			||||||
@ -70,6 +82,8 @@
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
            {% endwith %}
 | 
					            {% endwith %}
 | 
				
			||||||
            {% endfor %}
 | 
					            {% endfor %}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        {% endfor %}
 | 
				
			||||||
        </tbody>
 | 
					        </tbody>
 | 
				
			||||||
        <tfoot>
 | 
					        <tfoot>
 | 
				
			||||||
            <tr>
 | 
					            <tr>
 | 
				
			||||||
 | 
				
			|||||||
@ -42,7 +42,7 @@ class DashboardView(DashboardBaseView, TemplateView):
 | 
				
			|||||||
class StatisticsView(DashboardBaseView, LogListView):
 | 
					class StatisticsView(DashboardBaseView, LogListView):
 | 
				
			||||||
    template_name = "aircox/dashboard/statistics.html"
 | 
					    template_name = "aircox/dashboard/statistics.html"
 | 
				
			||||||
    date = None
 | 
					    date = None
 | 
				
			||||||
    redirect_date_url = "dashboard-statistics"
 | 
					    # redirect_date_url = "dashboard-statistics"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    # TOOD: test_func & perms check
 | 
					    # TOOD: test_func & perms check
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
@ -17,35 +17,32 @@ __all__ = ("LogListMixin", "LogListView", "LogListAPIView")
 | 
				
			|||||||
class LogListMixin(GetDateMixin):
 | 
					class LogListMixin(GetDateMixin):
 | 
				
			||||||
    model = Log
 | 
					    model = Log
 | 
				
			||||||
    min_date = None
 | 
					    min_date = None
 | 
				
			||||||
 | 
					    max_date = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_date(self):
 | 
					    def get_date(self, param):
 | 
				
			||||||
        date = super().get_date()
 | 
					        date = super().get_date(param)
 | 
				
			||||||
        if date is not None and not self.request.user.is_staff:
 | 
					        if date is not None and not self.request.user.is_staff:
 | 
				
			||||||
            return min(date, datetime.date.today())
 | 
					            return min(date, datetime.date.today())
 | 
				
			||||||
        return date
 | 
					        return date
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def filter_qs(self, query):
 | 
				
			||||||
 | 
					        if self.min_date:
 | 
				
			||||||
 | 
					            query = query.after(self.min_date)
 | 
				
			||||||
 | 
					        if self.max_date:
 | 
				
			||||||
 | 
					            query = query.before(self.max_date)
 | 
				
			||||||
 | 
					        if not self.min_date and not self.max_date and self.date:
 | 
				
			||||||
 | 
					            return query.date(self.date)
 | 
				
			||||||
 | 
					        return query
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    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).filter(date__lte=tz.now())
 | 
					        query = super().get_queryset().on_air().filter(track__isnull=False).filter(date__lte=tz.now())
 | 
				
			||||||
        return (
 | 
					        return self.filter_qs(query)
 | 
				
			||||||
            qs.date(self.date)
 | 
					 | 
				
			||||||
            if self.date is not None
 | 
					 | 
				
			||||||
            else 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().filter(start__lte=tz.now()).before()
 | 
					        query = Diffusion.objects.station(self.station).on_air().filter(start__lte=tz.now()).before()
 | 
				
			||||||
 | 
					        return self.filter_qs(query)
 | 
				
			||||||
        return (
 | 
					 | 
				
			||||||
            qs.date(self.date)
 | 
					 | 
				
			||||||
            if self.date is not None
 | 
					 | 
				
			||||||
            else qs.after(self.min_date)
 | 
					 | 
				
			||||||
            if self.min_date is not None
 | 
					 | 
				
			||||||
            else qs
 | 
					 | 
				
			||||||
        )
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_object_list(self, logs, full=False):
 | 
					    def get_object_list(self, logs, full=False):
 | 
				
			||||||
        """Return diffusions merged to the provided logs iterable.
 | 
					        """Return diffusions merged to the provided logs iterable.
 | 
				
			||||||
@ -55,7 +52,6 @@ class LogListMixin(GetDateMixin):
 | 
				
			|||||||
        diffs = self.get_diffusions_queryset()
 | 
					        diffs = self.get_diffusions_queryset()
 | 
				
			||||||
        if self.request.user.is_staff and full:
 | 
					        if self.request.user.is_staff and full:
 | 
				
			||||||
            return sorted(list(logs) + list(diffs), key=lambda obj: obj.start)
 | 
					            return sorted(list(logs) + list(diffs), key=lambda obj: obj.start)
 | 
				
			||||||
        print(">>>>", len(logs), len(diffs), Log.merge_diffusions(logs, diffs))
 | 
					 | 
				
			||||||
        return Log.merge_diffusions(logs, diffs)
 | 
					        return Log.merge_diffusions(logs, diffs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@ -64,11 +60,31 @@ class LogListView(AttachedToMixin, BaseView, LogListMixin, ListView):
 | 
				
			|||||||
    `request.GET`, defaults to today)."""
 | 
					    `request.GET`, defaults to today)."""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    redirect_date_url = "log-list"
 | 
					    redirect_date_url = "log-list"
 | 
				
			||||||
 | 
					    date_delta = tz.timedelta(days=7)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_date(self):
 | 
					    def get_date(self, param):
 | 
				
			||||||
        date = super().get_date()
 | 
					        date = super().get_date(param)
 | 
				
			||||||
        return datetime.date.today() if date is None else date
 | 
					        return datetime.date.today() if date is None else date
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    def get(self, request, **kwargs):
 | 
				
			||||||
 | 
					        min_date = self.get_date("min_date")
 | 
				
			||||||
 | 
					        max_date = self.get_date("max_date")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # ensure right values for min and max
 | 
				
			||||||
 | 
					        min_date, max_date = min(min_date, max_date), max(min_date, max_date)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        # limit logs list size using date delta
 | 
				
			||||||
 | 
					        if min_date and max_date:
 | 
				
			||||||
 | 
					            max_date = min(min_date + self.date_delta, max_date)
 | 
				
			||||||
 | 
					        elif min_date:
 | 
				
			||||||
 | 
					            max_date = min_date + self.date_delta
 | 
				
			||||||
 | 
					        elif max_date:
 | 
				
			||||||
 | 
					            min_date = max_date - self.date_delta
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        self.min_date = min_date
 | 
				
			||||||
 | 
					        self.max_date = max_date
 | 
				
			||||||
 | 
					        return super().get(request, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_context_data(self, **kwargs):
 | 
					    def get_context_data(self, **kwargs):
 | 
				
			||||||
        today = datetime.date.today()
 | 
					        today = datetime.date.today()
 | 
				
			||||||
        # `super()...` must be called before updating kwargs, in order
 | 
					        # `super()...` must be called before updating kwargs, in order
 | 
				
			||||||
@ -76,6 +92,9 @@ class LogListView(AttachedToMixin, BaseView, LogListMixin, ListView):
 | 
				
			|||||||
        kwargs = super().get_context_data(**kwargs)
 | 
					        kwargs = super().get_context_data(**kwargs)
 | 
				
			||||||
        kwargs.update(
 | 
					        kwargs.update(
 | 
				
			||||||
            {
 | 
					            {
 | 
				
			||||||
 | 
					                "min_date": self.min_date or today,
 | 
				
			||||||
 | 
					                "max_date": self.max_date or today,
 | 
				
			||||||
 | 
					                "today": datetime.date.today(),
 | 
				
			||||||
                "date": self.date,
 | 
					                "date": self.date,
 | 
				
			||||||
                "dates": (today - datetime.timedelta(days=i) for i in range(0, 7)),
 | 
					                "dates": (today - datetime.timedelta(days=i) for i in range(0, 7)),
 | 
				
			||||||
                "object_list": self.get_object_list(self.object_list),
 | 
					                "object_list": self.get_object_list(self.object_list),
 | 
				
			||||||
@ -101,8 +120,8 @@ class LogListAPIView(LogListMixin, BaseAPIView, ListAPIView):
 | 
				
			|||||||
    def list(self, *args, **kwargs):
 | 
					    def list(self, *args, **kwargs):
 | 
				
			||||||
        return super().list(*args, **kwargs)
 | 
					        return super().list(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_date(self):
 | 
					    def get_date(self, param):
 | 
				
			||||||
        date = super().get_date()
 | 
					        date = super().get_date(param)
 | 
				
			||||||
        if date is None:
 | 
					        if date is None:
 | 
				
			||||||
            self.min_date = tz.now() - tz.timedelta(minutes=30)
 | 
					            self.min_date = tz.now() - tz.timedelta(minutes=30)
 | 
				
			||||||
        return date
 | 
					        return date
 | 
				
			||||||
 | 
				
			|||||||
@ -12,9 +12,9 @@ class GetDateMixin:
 | 
				
			|||||||
    date = None
 | 
					    date = None
 | 
				
			||||||
    redirect_date_url = None
 | 
					    redirect_date_url = None
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    def get_date(self):
 | 
					    def get_date(self, param):
 | 
				
			||||||
        date = self.request.GET.get("date")
 | 
					        date = self.request.GET.get(param)
 | 
				
			||||||
        return str_to_date(date, "-") if date is not None else self.kwargs["date"] if "date" in self.kwargs else None
 | 
					        return str_to_date(date, "-") if date else self.kwargs[param] if param 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"):
 | 
					        if self.redirect_date_url and self.request.GET.get("date"):
 | 
				
			||||||
@ -23,7 +23,7 @@ class GetDateMixin:
 | 
				
			|||||||
                date=self.request.GET["date"].replace("-", "/"),
 | 
					                date=self.request.GET["date"].replace("-", "/"),
 | 
				
			||||||
            )
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        self.date = self.get_date()
 | 
					        self.date = self.get_date("date")
 | 
				
			||||||
        return super().get(*args, **kwargs)
 | 
					        return super().get(*args, **kwargs)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user