imrpove statistics rendering + filters
This commit is contained in:
parent
0ee72f30c5
commit
8a6f72ca83
|
@ -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,49 +31,58 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
{% for object in object_list %}
|
{% regroup object_list by date.date as by_date %}
|
||||||
{% with object|is_diffusion as is_diff %}
|
{% for date, objects in by_date %}
|
||||||
{% if is_diff %}
|
<tr>
|
||||||
<tr class="bg-main">
|
<th colspan="3">
|
||||||
<td>{{ object.start|time:"H:i" }} - {{ object.end|time:"H:i" }}</td>
|
{{ date|date:"l - d F Y" }}
|
||||||
<td colspan="2">
|
</th>
|
||||||
<a href="{% url "episode-detail" slug=object.episode.slug %}" target="new">{{ object.episode|default:"" }}</a>
|
|
||||||
</td>
|
|
||||||
</tr>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
{% with object|get_tracks as tracks %}
|
|
||||||
{% for track in tracks %}
|
|
||||||
<tr {% if is_diff %}class="bg-main-light"{% endif %}>
|
|
||||||
{% if forloop.first %}
|
|
||||||
<td rowspan="{{ tracks|length }}">{{ object.start|time:"H:i" }} {% if object|is_diffusion %} - {{ object.end|time:"H:i" }}{% endif %}</td>
|
|
||||||
{% endif %}
|
|
||||||
|
|
||||||
<td>
|
|
||||||
{% if object.source %}{{ object.source }} / {% endif %}
|
|
||||||
{% include "aircox/widgets/track_item.html" with object=track %}
|
|
||||||
</td>
|
|
||||||
{% with track.tags.all|join:', ' as tags %}
|
|
||||||
<td>
|
|
||||||
{% if tags and tags.strip %}
|
|
||||||
<label class="checkbox">
|
|
||||||
<input type="checkbox" checked value="{{ tags|escape }}" name="data">
|
|
||||||
{{ tags }}
|
|
||||||
</label>
|
|
||||||
{% endif %}
|
|
||||||
</td>
|
|
||||||
{% endwith %}
|
|
||||||
</tr>
|
</tr>
|
||||||
{% empty %}
|
{% for object in objects %}
|
||||||
|
{% with object|is_diffusion as is_diff %}
|
||||||
{% if is_diff %}
|
{% if is_diff %}
|
||||||
<tr class="bg-main-light">
|
<tr class="bg-main">
|
||||||
<td colspan="3">{% translate "No tracks" %}</td>
|
<td>{{ object.start|time:"H:i" }} - {{ object.end|time:"H:i" }}</td>
|
||||||
|
<td colspan="2">
|
||||||
|
<a href="{% url "episode-detail" slug=object.episode.slug %}" target="new">{{ object.episode|default:"" }}</a>
|
||||||
|
</td>
|
||||||
</tr>
|
</tr>
|
||||||
{% endif %}
|
{% endif %}
|
||||||
{% endfor %}
|
|
||||||
{% endwith %}
|
|
||||||
|
|
||||||
{% endwith %}
|
{% with object|get_tracks as tracks %}
|
||||||
|
{% for track in tracks %}
|
||||||
|
<tr {% if is_diff %}class="bg-main-light"{% endif %}>
|
||||||
|
{% if forloop.first %}
|
||||||
|
<td rowspan="{{ tracks|length }}">{{ object.start|time:"H:i" }} {% if object|is_diffusion %} - {{ object.end|time:"H:i" }}{% endif %}</td>
|
||||||
|
{% endif %}
|
||||||
|
|
||||||
|
<td>
|
||||||
|
{% if object.source %}{{ object.source }} / {% endif %}
|
||||||
|
{% include "aircox/widgets/track_item.html" with object=track %}
|
||||||
|
</td>
|
||||||
|
{% with track.tags.all|join:', ' as tags %}
|
||||||
|
<td>
|
||||||
|
{% if tags and tags.strip %}
|
||||||
|
<label class="checkbox">
|
||||||
|
<input type="checkbox" checked value="{{ tags|escape }}" name="data">
|
||||||
|
{{ tags }}
|
||||||
|
</label>
|
||||||
|
{% endif %}
|
||||||
|
</td>
|
||||||
|
{% endwith %}
|
||||||
|
</tr>
|
||||||
|
{% empty %}
|
||||||
|
{% if is_diff %}
|
||||||
|
<tr class="bg-main-light">
|
||||||
|
<td colspan="3">{% translate "No tracks" %}</td>
|
||||||
|
</tr>
|
||||||
|
{% endif %}
|
||||||
|
{% endfor %}
|
||||||
|
{% endwith %}
|
||||||
|
|
||||||
|
{% endwith %}
|
||||||
|
{% endfor %}
|
||||||
|
|
||||||
{% endfor %}
|
{% endfor %}
|
||||||
</tbody>
|
</tbody>
|
||||||
<tfoot>
|
<tfoot>
|
||||||
|
|
|
@ -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)
|
||||||
|
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue
Block a user