Skip to content

Commit

Permalink
Add stats of ADS updates
Browse files Browse the repository at this point in the history
  • Loading branch information
brmzkw committed May 10, 2024
1 parent 8634020 commit 9741cf9
Show file tree
Hide file tree
Showing 2 changed files with 196 additions and 60 deletions.
93 changes: 93 additions & 0 deletions mesads/app/views/dashboards.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
from datetime import timedelta
import collections

from django.contrib.contenttypes.models import ContentType
from django.db import connection
from django.db.models import Count, Q, Sum
from django.db.models.functions import Coalesce
from django.utils import timezone
from django.views.generic import TemplateView
from django.views.generic.detail import DetailView

from ..models import (
ADS,
ADSManager,
ADSManagerAdministrator,
)
Expand All @@ -19,8 +22,98 @@ class DashboardsView(TemplateView):
def get_context_data(self, **kwargs):
ctx = super().get_context_data(**kwargs)
ctx["stats"], ctx["stats_total"] = self.get_stats()
ctx["ads_updates_stats"] = self.ads_updates_stats()
return ctx

def ads_updates_stats(self):
"""This function uses a raw SQL query to retrieve the list of ADS ids
that have been updated, grouped by date.
It is using the reversion_version to identify the updates.
The query built is the following (pseudo-code)
1. list all the updates in the ADS table
subq = SELECT object_id, date_created
FROM reversion_version JOIN reversion_revision
WHERE reversion_version.content_type_id = <content type of ADS>
2. for each object with a foreign key to an ADS (ADSLegalFile, ADSUser),
also list the updates and append to the previous list
subq = subq + 'UNION' + SELECT serialized_data->0->'fields'->>'ads', date_created
FROM reversion_version JOIN reversion_revision
WHERE reversion_version.content_type_id = <content type of related model>
3. the list built might contains duplicates (if the model and the
related model have both been updated at the same time). We group by
ads id and by date.
"""
# For each update of an ADS, retrieve the date of the update and the ADS id
subq = f"""
SELECT
reversion_version.object_id AS ads_id,
reversion_revision.date_created AS "when"
FROM
reversion_version
JOIN reversion_revision
ON reversion_version.revision_id = reversion_revision.id
WHERE reversion_version.content_type_id = {ContentType.objects.get_for_model(ADS).id}
"""

# For each object with a foreign key to an ADS, retrieve the date of the
# update and the ADS id, and make a union with the previous query.
for related_object in ADS._meta.related_objects:
subq += f"""
UNION
SELECT
reversion_version.serialized_data::jsonb->0->'fields'->>'{related_object.field.name}' AS ads_id,
reversion_revision.date_created AS "when"
FROM
reversion_version
JOIN reversion_revision
ON reversion_version.revision_id = reversion_revision.id
WHERE reversion_version.content_type_id = {ContentType.objects.get_for_model(related_object.related_model).id}
"""

# Group by day, limit to the last 30 days
query_by_day = f"""
WITH updates_by_date AS ({subq})
SELECT
DATE_TRUNC('day', updates_by_date."when") AS "when",
ARRAY_AGG(updates_by_date.ads_id) AS "ads_list"
FROM updates_by_date
WHERE updates_by_date."when" >= NOW() - INTERVAL '30 day'
GROUP BY
DATE_TRUNC('day', updates_by_date."when")
ORDER BY
DATE_TRUNC('day', updates_by_date."when")
;
"""
query_by_month = f"""
WITH updates_by_date AS ({subq})
SELECT
DATE_TRUNC('month', updates_by_date."when") AS "when",
ARRAY_AGG(updates_by_date.ads_id) AS "ads_list"
FROM updates_by_date
GROUP BY
DATE_TRUNC('month', updates_by_date."when")
ORDER BY
DATE_TRUNC('month', updates_by_date."when")
;
"""
ret = {}
with connection.cursor() as cursor:
cursor.execute(query_by_day)
ret["by_day"] = ((row[0], row[1]) for row in cursor.fetchall())

cursor.execute(query_by_month)
ret["by_month"] = ((row[0], row[1]) for row in cursor.fetchall())
return ret

def get_stats(self):
"""This function returns a tuple of two values:
Expand Down
163 changes: 103 additions & 60 deletions mesads/templates/webpack/pages/ads_register/dashboards_list.html
Original file line number Diff line number Diff line change
Expand Up @@ -16,74 +16,117 @@
<h1>Suivi du déploiement du registre ADS</h1>

<p class="fr-text--lead">
Cette page n'est accessible qu'aux membres de l'équipe <i>Mes ADS</i>. Elle présente des statistiques utiles au déploiement du projet.
Cette page n'est accessible qu'aux membres de l'équipe <i>MesADS</i>. Elle présente des statistiques utiles au déploiement du projet.
</p>

<div class="fr-table fr-table--bordered">

<div class="fr-grid-row">
<div class="fr-col-6 fr-table fr-table--bordered">
<table>
<caption>Nombre de modifications ADS par mois</caption>
<thead>
<tr>
<th>Date</th>
<th>Nombre d'ADS modifiées</th>
</tr>
</thead>
<tbody>
{% for date, ads_list in ads_updates_stats.by_month %}
<tr>
<td>{{ date|date:"m/Y" }}</td>
<td>{{ ads_list|length }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>

<div class="fr-col-6 fr-table fr-table--bordered">
<table>
<caption>Statistiques par préfecture</caption>
<thead>
<tr>
<th>&nbsp;</th>
<th style="text-align:center; border-bottom:1px solid #000" colspan="4">Nombre d'ADS</th>
<th style="text-align:center; border-bottom:1px solid #000" colspan="4">Nombre de gestionnaires</th>
</tr>
<tr>
<th></th>
<caption>Nombre de modifications ADS par jour ces derniers jours</caption>
<thead>
<tr>
<th>Date</th>
<th>Nombre d'ADS modifiées</th>
</tr>
</thead>
<tbody>
{% for date, ads_list in ads_updates_stats.by_day %}
<tr>
<td>{{ date|date:"d/m/Y" }}</td>
<td>{{ ads_list|length }}</td>
</tr>
{% endfor %}
</tbody>
</table>
</div>
</div>

<th>Ajd.</th>
<th>Il y a 3 mois</th>
<th>… 6 mois</th>
<th>… 12 mois</th>
<div class="fr-table fr-table--bordered">
<table>
<caption>Statistiques par préfecture</caption>
<thead>
<tr>
<th>&nbsp;</th>
<th style="text-align:center; border-bottom:1px solid #000" colspan="4">Nombre d'ADS</th>
<th style="text-align:center; border-bottom:1px solid #000" colspan="4">Nombre de gestionnaires</th>
</tr>
<tr>
<th></th>

<th>Ajd.</th>
<th>Il y a 3 mois</th>
<th>… 6 mois</th>
<th>… 12 mois</th>
</tr>
</thead>
<tbody>
{% for row in stats %}
<tr id="prefecture_{{ row.obj.prefecture.numero }}" x-data="{selected: window.location.hash}" @hashchange.window="selected = window.location.hash" :class="selected == '#prefecture_{{ row.obj.prefecture.numero }}' ? 'tr-highlighted' : ''">
<td><a href="{% url 'app.dashboards.detail' ads_manager_administrator_id=row.obj.id %}">{{ row.obj.prefecture }}</a></td>
<th>Ajd.</th>
<th>Il y a 3 mois</th>
<th>… 6 mois</th>
<th>… 12 mois</th>

<td>
<strong>{{ row.ads.now|default:"" }}</strong>
{% if row.ads.with_info_now %}
<br />
<small>{{ row.ads.with_info_now }} avec infos contact</small
{% endif %}
</td>
<td><small>{{ row.ads.3_months|default:"" }}</small></td>
<td><small>{{ row.ads.6_months|default:"" }}</small></td>
<td><small>{{ row.ads.12_months|default:"" }}</small></td>
<th>Ajd.</th>
<th>Il y a 3 mois</th>
<th>… 6 mois</th>
<th>… 12 mois</th>
</tr>
</thead>
<tbody>
{% for row in stats %}
<tr id="prefecture_{{ row.obj.prefecture.numero }}" x-data="{selected: window.location.hash}" @hashchange.window="selected = window.location.hash" :class="selected == '#prefecture_{{ row.obj.prefecture.numero }}' ? 'tr-highlighted' : ''">
<td><a href="{% url 'app.dashboards.detail' ads_manager_administrator_id=row.obj.id %}">{{ row.obj.prefecture }}</a></td>

<td><strong>{{ row.users.now|default:"" }}</strong></td>
<td><small>{{ row.users.3_months|default:"" }}</small></td>
<td><small>{{ row.users.6_months|default:"" }}</small></td>
<td><small>{{ row.users.12_months|default:"" }}</small></td>
</tr>
{% endfor %}
<td>
<strong>{{ row.ads.now|default:"" }}</strong>
{% if row.ads.with_info_now %}
<br />
<small>{{ row.ads.with_info_now }} avec infos contact</small>
{% endif %}
</td>
<td><small>{{ row.ads.3_months|default:"" }}</small></td>
<td><small>{{ row.ads.6_months|default:"" }}</small></td>
<td><small>{{ row.ads.12_months|default:"" }}</small></td>

<tr>
<td><strong>TOTAL</strong></td>
<td>
<strong>{{ stats_total.ads.now|default:"" }}</strong>
{% if stats_total.ads.with_info_now %}
<br />
<small>{{ stats_total.ads.with_info_now }} avec infos contact</small
{% endif %}
</td>
<td><small>{{ stats_total.ads.3_months|default:"" }}</small></td>
<td><small>{{ stats_total.ads.6_months|default:"" }}</small></td>
<td><small>{{ stats_total.ads.12_months|default:"" }}</small></td>
<td><strong>{{ row.users.now|default:"" }}</strong></td>
<td><small>{{ row.users.3_months|default:"" }}</small></td>
<td><small>{{ row.users.6_months|default:"" }}</small></td>
<td><small>{{ row.users.12_months|default:"" }}</small></td>
</tr>
{% endfor %}

<td><strong>{{ stats_total.users.now|default:"" }}</strong></td>
<td><small>{{ stats_total.users.3_months|default:"" }}</small></td>
<td><small>{{ stats_total.users.6_months|default:"" }}</small></td>
<td><small>{{ stats_total.users.12_months|default:"" }}</small></td>
</tr>
</tbody>
</table>
<tr>
<td><strong>TOTAL</strong></td>
<td>
<strong>{{ stats_total.ads.now|default:"" }}</strong>
{% if stats_total.ads.with_info_now %}
<br />
<small>{{ stats_total.ads.with_info_now }} avec infos contact</small>
{% endif %}
</td>
<td><small>{{ stats_total.ads.3_months|default:"" }}</small></td>
<td><small>{{ stats_total.ads.6_months|default:"" }}</small></td>
<td><small>{{ stats_total.ads.12_months|default:"" }}</small></td>

<td><strong>{{ stats_total.users.now|default:"" }}</strong></td>
<td><small>{{ stats_total.users.3_months|default:"" }}</small></td>
<td><small>{{ stats_total.users.6_months|default:"" }}</small></td>
<td><small>{{ stats_total.users.12_months|default:"" }}</small></td>
</tr>
</tbody>
</table>
</div>
{% endblock %}

0 comments on commit 9741cf9

Please sign in to comment.