From 2537d60ff176839ddc3d1a32aa7278aabca86a91 Mon Sep 17 00:00:00 2001
From: Valentin Deniaud <vdeniaud@entrouvert.com>
Date: Mon, 13 Dec 2021 12:13:01 +0100
Subject: [PATCH 2/2] fix order for subscriptions in recurring events list

---
 chrono/api/views.py         | 17 ++++++++++++--
 tests/api/test_datetimes.py | 46 +++++++++++++++++++++++++++++++++----
 2 files changed, 56 insertions(+), 7 deletions(-)

diff --git a/chrono/api/views.py b/chrono/api/views.py
index dcbda287..79415dac 100644
--- a/chrono/api/views.py
+++ b/chrono/api/views.py
@@ -1081,7 +1081,7 @@ class RecurringEventsList(APIView):
             raise APIErrorBadRequest(N_('invalid payload'), errors=serializer.errors)
         data = serializer.validated_data
 
-        agendas = Agenda.prefetch_recurring_events(data['agendas'])
+        agendas = Agenda.prefetch_recurring_events(data['agendas']).select_related('category')
         events = []
         for agenda in agendas:
             for event in agenda.get_open_recurring_events():
@@ -1094,7 +1094,20 @@ class RecurringEventsList(APIView):
             agenda_querystring_indexes = {
                 agenda_slug: i for i, agenda_slug in enumerate(data['agenda_slugs'])
             }
-            events.sort(key=lambda event: (event.day, agenda_querystring_indexes[event.agenda.slug]))
+            events.sort(
+                key=lambda event: (event.day, agenda_querystring_indexes[event.agenda.slug], event.slug)
+            )
+        elif 'subscribed' in request.query_params:
+            category_querystring_indexes = {category: i for i, category in enumerate(data['subscribed'])}
+            sort_by_category = bool(data['subscribed'] != ['all'])
+            events.sort(
+                key=lambda event: (
+                    event.day,
+                    category_querystring_indexes[event.agenda.category.slug] if sort_by_category else None,
+                    event.agenda.slug,
+                    event.slug,
+                )
+            )
 
         return Response(
             {
diff --git a/tests/api/test_datetimes.py b/tests/api/test_datetimes.py
index f0b2cfa2..4b26ecce 100644
--- a/tests/api/test_datetimes.py
+++ b/tests/api/test_datetimes.py
@@ -1448,18 +1448,30 @@ def test_recurring_events_api_list_multiple_agendas(app):
 
 @pytest.mark.freeze_time('2021-09-06 12:00')
 def test_recurring_events_api_list_multiple_agendas_queries(app):
+    category = Category.objects.create(label='Category A')
     for i in range(20):
-        agenda = Agenda.objects.create(slug=f'{i}', kind='events')
+        agenda = Agenda.objects.create(slug=f'{i}', kind='events', category=category)
         Desk.objects.create(agenda=agenda, slug='_exceptions_holder')
         start, end = now(), now() + datetime.timedelta(days=30)
         Event.objects.create(
             start_datetime=start, places=2, recurrence_end_date=end, recurrence_days=[1, 2], agenda=agenda
         )
+        Subscription.objects.create(
+            agenda=agenda,
+            user_external_id='xxx',
+            date_start=now(),
+            date_end=now() + datetime.timedelta(days=60),
+        )
     with CaptureQueriesContext(connection) as ctx:
         resp = app.get('/api/agendas/recurring-events/?agendas=%s' % ','.join(str(i) for i in range(20)))
         assert len(resp.json['data']) == 40
         assert len(ctx.captured_queries) == 3
 
+    with CaptureQueriesContext(connection) as ctx:
+        resp = app.get('/api/agendas/recurring-events/?subscribed=category-a&user_external_id=xxx')
+        assert len(resp.json['data']) == 40
+        assert len(ctx.captured_queries) == 3
+
 
 @pytest.mark.freeze_time('2021-09-06 12:00')
 def test_recurring_events_api_list_subscribed(app, user):
@@ -1470,18 +1482,16 @@ def test_recurring_events_api_list_subscribed(app, user):
     Event.objects.create(
         slug='event',
         start_datetime=now(),
-        recurrence_days=[0, 1, 3, 4],  # Monday, Tuesday, Thursday, Friday
+        recurrence_days=[0, 1, 3, 6],  # Monday, Tuesday, Thursday, Friday
         places=2,
-        waiting_list_places=1,
         agenda=first_agenda,
         recurrence_end_date=now() + datetime.timedelta(days=364),
     )
     Event.objects.create(
         slug='sunday-event',
         start_datetime=now(),
-        recurrence_days=[6],
+        recurrence_days=[5],
         places=2,
-        waiting_list_places=1,
         agenda=second_agenda,
         recurrence_end_date=now() + datetime.timedelta(days=364),
     )
@@ -1512,6 +1522,15 @@ def test_recurring_events_api_list_subscribed(app, user):
     resp = app.get('/api/agendas/recurring-events/?user_external_id=xxx&subscribed=all')
     assert len(resp.json['data']) == 5
 
+    # events are sorted by day
+    assert [x['id'] for x in resp.json['data']] == [
+        'first-agenda@event:0',
+        'first-agenda@event:1',
+        'first-agenda@event:3',
+        'second-agenda@sunday-event:5',
+        'first-agenda@event:6',
+    ]
+
     resp = app.get('/api/agendas/recurring-events/?user_external_id=xxx&subscribed=category-b')
     assert len(resp.json['data']) == 1
 
@@ -1528,6 +1547,23 @@ def test_recurring_events_api_list_subscribed(app, user):
     resp = app.get('/api/agendas/recurring-events/?user_external_id=yyy&subscribed=all')
     assert len(resp.json['data']) == 1
 
+    # sorting depends on querystring order
+    Event.objects.create(
+        slug='event',
+        start_datetime=now(),
+        recurrence_days=[0],
+        places=2,
+        agenda=second_agenda,
+        recurrence_end_date=now() + datetime.timedelta(days=364),
+    )
+    resp = app.get('/api/agendas/recurring-events/?subscribed=category-a,category-b&user_external_id=xxx')
+    event_ids = [x['id'] for x in resp.json['data']]
+    assert event_ids.index('first-agenda@event:0') < event_ids.index('second-agenda@event:0')
+
+    resp = app.get('/api/agendas/recurring-events/?subscribed=category-b,category-a&user_external_id=xxx')
+    event_ids = [x['id'] for x in resp.json['data']]
+    assert event_ids.index('first-agenda@event:0') > event_ids.index('second-agenda@event:0')
+
 
 @pytest.mark.freeze_time('2021-05-06 14:00')
 def test_datetimes_multiple_agendas(app):
-- 
2.30.2

