Amélioration statistiques

This commit is contained in:
Jean-Marie Favreau 2025-03-15 19:11:27 +01:00
parent ca1015814a
commit 527fa886b7
5 changed files with 338 additions and 274 deletions

File diff suppressed because it is too large Load Diff

View File

@ -42,6 +42,8 @@ from icalendar import Event as icalEvent
from location_field.models.spatial import LocationField from location_field.models.spatial import LocationField
from django.dispatch import receiver from django.dispatch import receiver
from django.db.models.signals import post_save from django.db.models.signals import post_save
from django.db.models.aggregates import StdDev
from django.db.models import Avg, Max, Min
from .calendar import CalendarDay from .calendar import CalendarDay
from .import_tasks.extractor import Extractor from .import_tasks.extractor import Extractor
@ -49,9 +51,24 @@ from .import_tasks.generic_extractors.fbevent import (
CExtractor as FacebookEventExtractor, CExtractor as FacebookEventExtractor,
) )
from django.db.models import Aggregate, FloatField
logger = logging.getLogger(__name__) logger = logging.getLogger(__name__)
class Median(Aggregate):
function = "PERCENTILE_CONT"
name = "median"
output_field = FloatField()
template = "%(function)s(0.5) WITHIN GROUP (ORDER BY %(expressions)s)"
#
#
# Useful for translation
to_be_translated = [_("mean"), _("median"), _("maximum"), _("minimum"), _("stdev")]
class UserProfile(models.Model): class UserProfile(models.Model):
user = models.OneToOneField( user = models.OneToOneField(
User, User,
@ -2728,26 +2745,38 @@ class RecurrentImport(models.Model):
else: else:
return None return None
def get_foresight_quality(self): def _get_foresight_quality_internal(qs):
from statistics import median, mean, stdev limit = datetime.now() + timedelta(days=-30)
values = [ qs = qs.filter(start_day__gte=F("created_date")).annotate(
x["foresight"] foresight=ExtractDay(F("start_day") - F("created_date"))
for x in Event.objects.filter(import_sources__contains=[self.source]) )
.annotate(foresight=ExtractDay(F("start_day") - F("created_date")))
.values("foresight") stats = qs.filter().aggregate(
] minimum=Min("foresight"),
if len(values) == 0: maximum=Max("foresight"),
return [] mean=Avg("foresight"),
result = [ median=Median("foresight"),
[_("minimum"), min(values)], stdev=StdDev("foresight"),
[_("maximum"), max(values)], )
[_("mean"), round(mean(values), 2)],
[_("median"), median(values)], statsm = qs.filter(created_date__gte=limit).aggregate(
] minimum=Min("foresight"),
if len(values) > 2: maximum=Max("foresight"),
result.append([_("standard deviation"), round(stdev(values), 2)]) mean=Avg("foresight"),
return result median=Median("foresight"),
stdev=StdDev("foresight"),
)
return [[_(x), round(stats[x], 2), round(statsm[x], 2)] for x in stats]
def get_global_foresight_quality():
return RecurrentImport._get_foresight_quality_internal(Event.objects)
def get_foresight_quality(self):
return RecurrentImport._get_foresight_quality_internal(
Event.objects.filter(import_sources__contains=[self.source])
)
class BatchImportation(models.Model): class BatchImportation(models.Model):

View File

@ -90,7 +90,9 @@
<h2>Qualité de l'anticipation</h2> <h2>Qualité de l'anticipation</h2>
<p> <p>
On s'intéresse à la différence entre la date de publication d'un événement et la date effective de l'événement. Plus le nombre de jours qui les sépare est élevé, plus On s'intéresse à la différence entre la date de publication d'un événement et la date effective de l'événement. Plus le nombre de jours qui les sépare est élevé, plus
la source anticipe ses événements, et peut être considérée comme une source fiable. la source anticipe ses événements, et peut être considérée comme une source fiable. On ignore les événements ajoutés à l'agenda alors que l'événement a déjà eu lieu.
On affiche cette information pour les imports depuis le début de son
intégration à l'agenda, mais aussi pour le dernier mois.
</p> </p>
<table> <table>
<thead> <thead>
@ -99,9 +101,13 @@
</thead> </thead>
<tbody> <tbody>
<tr> <tr>
<th class="label">Nb jours</th> <th class="label">écart (en jours)</th>
{% for v in stat %}<th>{{ v.1 }}</th>{% endfor %} {% for v in stat %}<th>{{ v.1 }}</th>{% endfor %}
</tr> </tr>
<tr>
<th class="label">écart du dernier mois (en jours)</th>
{% for v in stat %}<th>{{ v.2 }}</th>{% endfor %}
</tr>
</tbody> </tbody>
</table> </table>
{% endif %} {% endif %}

View File

@ -42,6 +42,29 @@
On retrouve une synthèse par mois du tableau précédent, sous forme d'une moyenne du nombre de création d'événements par jour pour chaque mois. On retrouve une synthèse par mois du tableau précédent, sous forme d'une moyenne du nombre de création d'événements par jour pour chaque mois.
</p> </p>
{% include "agenda_culturel/statistics_per_month.html" with data=stats_months_by_creation %} {% include "agenda_culturel/statistics_per_month.html" with data=stats_months_by_creation %}
<h2>Qualité de l'anticipation</h2>
<p>
On s'intéresse à la différence entre la date de publication d'un événement et la date effective de l'événement. Plus le nombre de jours qui les sépare est élevé, plus
les sources anticipent leurs événements. On ignore les événements ajoutés à l'agenda alors que l'événement a déjà eu lieu.
On affiche cette information pour les imports depuis le début de leur
intégration à l'agenda, mais aussi pour le dernier mois.
</p>
<table>
<thead>
<th class="label"></th>
{% for v in stats_foresight %}<th>{{ v.0 }}</th>{% endfor %}
</thead>
<tbody>
<tr>
<th class="label">écart (en jours)</th>
{% for v in stats_foresight %}<th>{{ v.1 }}</th>{% endfor %}
</tr>
<tr>
<th class="label">écart du dernier mois (en jours)</th>
{% for v in stats_foresight %}<th>{{ v.2 }}</th>{% endfor %}
</tr>
</tbody>
</table>
<h2>Par ville</h2> <h2>Par ville</h2>
<p>Nombre d'événements référencés par ville.</p> <p>Nombre d'événements référencés par ville.</p>
<ul> <ul>

View File

@ -2816,6 +2816,8 @@ def statistics(request):
.order_by("-total") .order_by("-total")
) )
stats_foresight = RecurrentImport.get_global_foresight_quality()
context = { context = {
"stats_by_startday": stats["start_day"], "stats_by_startday": stats["start_day"],
"stats_by_creation": stats["created_date__date"], "stats_by_creation": stats["created_date__date"],
@ -2826,6 +2828,7 @@ def statistics(request):
"first_by_creation": first["created_date__date"], "first_by_creation": first["created_date__date"],
"last_by_creation": last["created_date__date"], "last_by_creation": last["created_date__date"],
"nb_by_city": nb_by_city, "nb_by_city": nb_by_city,
"stats_foresight": stats_foresight,
} }
return render(request, "agenda_culturel/statistics.html", context) return render(request, "agenda_culturel/statistics.html", context)