From c4215219395840fbe90dcebee66ddbe6c3e78e62 Mon Sep 17 00:00:00 2001
From: Pierre Ducroquet <pducroquet@entrouvert.com>
Date: Tue, 27 Sep 2022 13:10:56 +0200
Subject: [PATCH] performance: use a distinct query for statistics X axis
 (#69606)

---
 src/authentic2/apps/journal/models.py |  8 +++++++-
 src/authentic2/apps/journal/utils.py  | 15 +++++++++------
 src/authentic2/journal_event_types.py |  4 ++--
 3 files changed, 18 insertions(+), 9 deletions(-)

diff --git a/src/authentic2/apps/journal/models.py b/src/authentic2/apps/journal/models.py
index 1ab7fd93..7c566afa 100644
--- a/src/authentic2/apps/journal/models.py
+++ b/src/authentic2/apps/journal/models.py
@@ -136,6 +136,7 @@ class EventTypeDefinition(metaclass=EventTypeDefinitionMeta):
             qs = qs.filter(timestamp__gte=start)
         if end:
             qs = qs.filter(timestamp__lte=end)
+        x_interval_qs = qs
 
         values = [group_by_time]
         if group_by_time != 'timestamp':
@@ -156,7 +157,9 @@ class EventTypeDefinition(metaclass=EventTypeDefinitionMeta):
 
         qs = qs.values(*values)
         qs = qs.annotate(count=Count('id'))
-        return qs.order_by(group_by_time)
+        x_interval = x_interval_qs.aggregate(min=Trunc(Min("timestamp"), kind=group_by_time, output_field=models.DateField()), max=Trunc(Max("timestamp"), kind=group_by_time, output_field=models.DateField()))
+
+        return (qs.order_by(group_by_time), x_interval)
 
     def __repr__(self):
         return '<EventTypeDefinition %r %s>' % (self.name, self.label)
@@ -431,6 +434,9 @@ class Event(models.Model):
         verbose_name = _('event')
         verbose_name_plural = _('events')
         ordering = ('timestamp', 'id')
+        index_together = [
+            ("type", "timestamp"),
+        ]
 
 
 class EventCursor(str):
diff --git a/src/authentic2/apps/journal/utils.py b/src/authentic2/apps/journal/utils.py
index ad759130..9af3a729 100644
--- a/src/authentic2/apps/journal/utils.py
+++ b/src/authentic2/apps/journal/utils.py
@@ -48,9 +48,9 @@ class Statistics:
     }
     default_y_label = _('None')
 
-    def __init__(self, qs, time_interval):
+    def __init__(self, qs, time_interval, x_interval = None):
         self.time_interval = time_interval
-        self.x_labels = self.build_x_labels(qs)
+        self.x_labels = self.build_x_labels(qs, interval_qs)
         self._x_labels_indexes = {label: i for i, label in enumerate(self.x_labels)}
         self.series = {}
         self.y_labels = []
@@ -58,13 +58,16 @@ class Statistics:
     def set_y_labels(self, y_labels):
         self.y_labels[:] = y_labels
 
-    def build_x_labels(self, qs):
+    def build_x_labels(self, qs, x_interval = None):
         if self.time_interval == 'timestamp':
             return list(qs.distinct().values_list(self.time_interval, flat=True))
 
-        aggregate = qs.aggregate(min=Min(self.time_interval), max=Max(self.time_interval))
-        if not aggregate['min']:
-            return []
+        if x_interval is not None:
+            aggregate = x_interval
+        else:
+            aggregate = qs.aggregate(min=Min(self.time_interval), max=Max(self.time_interval))
+            if not aggregate['min']:
+                return []
 
         min_date, max_date = aggregate['min'].date(), aggregate['max'].date()
         if self.time_interval == 'day':
diff --git a/src/authentic2/journal_event_types.py b/src/authentic2/journal_event_types.py
index 11b4813d..5e77a57f 100644
--- a/src/authentic2/journal_event_types.py
+++ b/src/authentic2/journal_event_types.py
@@ -68,7 +68,7 @@ class EventTypeWithHow(EventTypeWithService):
         elif service:
             which_references = service
 
-        qs = cls.get_statistics(
+        qs, x_interval = cls.get_statistics(
             group_by_time=group_by_time,
             group_by_field='how',
             which_references=which_references,
@@ -76,7 +76,7 @@ class EventTypeWithHow(EventTypeWithService):
             start=start,
             end=end,
         )
-        stats = Statistics(qs, time_interval=group_by_time)
+        stats = Statistics(qs, time_interval=group_by_time, x_interval=x_interval)
 
         for stat in qs:
             stats.add(x_label=stat[group_by_time], y_label=stat['how'], value=stat['count'])
-- 
2.35.1

