From 6b84dd741fb450543da38f995586406ca460fc74 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Laur=C3=A9line=20Gu=C3=A9rin?= <zebuline@entrouvert.com>
Date: Tue, 31 Mar 2020 11:21:27 +0200
Subject: [PATCH 2/2] toulouse_axel: endpoint to get possible days for an
 activity (#41168)

---
 passerelle/contrib/toulouse_axel/models.py |  52 +++++++--
 tests/test_toulouse_axel.py                | 121 +++++++++++++++++++++
 2 files changed, 163 insertions(+), 10 deletions(-)

diff --git a/passerelle/contrib/toulouse_axel/models.py b/passerelle/contrib/toulouse_axel/models.py
index 095e2390..c8fcfbb6 100644
--- a/passerelle/contrib/toulouse_axel/models.py
+++ b/passerelle/contrib/toulouse_axel/models.py
@@ -26,6 +26,7 @@ from collections import namedtuple
 from django.core.cache import cache
 from django.db import models
 from django.http import HttpResponse
+from django.utils.dates import WEEKDAYS
 from django.utils.encoding import force_text
 from django.utils.translation import ugettext_lazy as _
 
@@ -1009,13 +1010,14 @@ class ToulouseAxel(BaseResource):
                       'xml_response': e.xml_response})
         return {'data': True}
 
-    def get_children_activities(self, dui, reference_year):
+    def get_children_activities(self, dui, reference_year, activity_types=None):
+        activity_types = activity_types or ['MAT', 'MIDI', 'SOIR', 'GARD']
         try:
             result = enfants_activites(self, {
                 'DUI': {
                     'IDDUI': dui,
                     'ANNEEREFERENCE': str(reference_year),
-                    'TYPESACTIVITES': 'MAT,MIDI,SOIR,GARD',
+                    'TYPESACTIVITES': ','.join(activity_types),
                 }
             })
         except AxelError as e:
@@ -1028,8 +1030,9 @@ class ToulouseAxel(BaseResource):
         children_activities = result.json_response['DATA']['PORTAIL']['DUI'].get('ENFANT', [])
         return {child['IDPERSONNE']: child for child in children_activities}
 
-    def get_child_activities(self, dui, reference_year, child_id):
-        children_activities = self.get_children_activities(dui=dui, reference_year=reference_year)
+    def get_child_activities(self, dui, reference_year, child_id, activity_types=None):
+        children_activities = self.get_children_activities(
+            dui=dui, reference_year=reference_year, activity_types=activity_types)
 
         if child_id not in children_activities:
             raise APIError('Child not found', err_code='not-found')
@@ -1056,14 +1059,15 @@ class ToulouseAxel(BaseResource):
             return {}
         return {child_id: bool(child.get('ACTIVITE', [])) for child_id, child in children_activities.items()}
 
-    def get_booking_data(self, dui, child_id, booking_date):
+    def get_booking_data(self, dui, child_id, booking_date, activity_types=None):
         reference_year = utils.get_reference_year_from_date(booking_date)
 
         # first get activities information for the child
         child_activities = self.get_child_activities(
             dui=dui,
             reference_year=reference_year,
-            child_id=child_id)
+            child_id=child_id,
+            activity_types=activity_types)
 
         # then get booking of the requested week for the child
         activity_ids = [act['IDACTIVITE'] for act in child_activities.get('ACTIVITE', [])]
@@ -1159,14 +1163,14 @@ class ToulouseAxel(BaseResource):
             'idpersonne': {'description': _('Child ID')},
             'booking_date': {'description': _('Booking date')},
         })
-    def clae_booking_info(self, request, NameID, idpersonne, booking_date):
+    def clae_booking_info(self, request, NameID, idpersonne, booking_date, activity_types=None):
         link = self.get_link(NameID)
         try:
             booking_date = datetime.datetime.strptime(booking_date, utils.json_date_format)
         except ValueError:
             raise APIError('bad date format, should be YYYY-MM-DD', err_code='bad-request', http_status=400)
 
-        booking_data = self.get_booking_data(dui=link.dui, child_id=idpersonne, booking_date=booking_date)
+        booking_data = self.get_booking_data(dui=link.dui, child_id=idpersonne, booking_date=booking_date, activity_types=activity_types)
 
         return {'data': booking_data}
 
@@ -1178,8 +1182,36 @@ class ToulouseAxel(BaseResource):
             'idpersonne': {'description': _('Child ID')},
             'booking_date': {'description': _('Booking date')},
         })
-    def clae_booking_activities_info(self, request, NameID, idpersonne, booking_date):
-        return {'data': self.clae_booking_info(request, NameID, idpersonne, booking_date)['data'].get('ACTIVITE', [])}
+    def clae_booking_activities_info(self, request, NameID, idpersonne, booking_date, activity_types=None):
+        return {'data': self.clae_booking_info(
+            request, NameID, idpersonne, booking_date, activity_types=activity_types)['data'].get('ACTIVITE', [])}
+
+    @endpoint(
+        description=_("Get list of possible days for an activity"),
+        perm='can_access',
+        parameters={
+            'NameID': {'description': _('Publik ID')},
+            'idpersonne': {'description': _('Child ID')},
+            'activity_type': {'description': _('Activity type (MAT, MIDI, SOIR, GARD)')},
+            'booking_date': {'description': _('Booking date')},
+        })
+    def clae_booking_activity_possible_days(self, request, NameID, idpersonne, activity_type, booking_date):
+        if activity_type not in ['MAT', 'MIDI', 'SOIR', 'GARD']:
+            raise APIError('bad activity_type, should be MAT, MIDI, SOIR or GARD', err_code='bad-request', http_status=400)
+        activities = self.clae_booking_activities_info(request, NameID, idpersonne, booking_date, activity_types=[activity_type])['data']
+        if not activities:
+            return {'data': []}
+        activity = activities[0]
+        activity_days = []
+        for i, day in enumerate(['monday', 'tuesday', 'wednesday', 'thursday', 'friday']):
+            activity_days.append({
+                'id': '{}-{}-{}-{}'.format(idpersonne, activity_type, activity['id'], day),
+                'text': WEEKDAYS[i],
+                'disabled': activity['booking']['days'][day] is None,
+                'prefill': activity['booking']['days'][day],
+                'details': activity,
+            })
+        return {'data': activity_days}
 
     @endpoint(
         description=_("CLAE/Cantine booking"),
diff --git a/tests/test_toulouse_axel.py b/tests/test_toulouse_axel.py
index 033930e1..49d722dd 100644
--- a/tests/test_toulouse_axel.py
+++ b/tests/test_toulouse_axel.py
@@ -2506,6 +2506,127 @@ def test_clae_booking_activities_info_endpoint(app, resource, child_activities_d
     ]
 
 
+def test_clae_booking_activity_possible_days_endpoint_axel_error(app, resource):
+    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
+    with mock.patch('passerelle.contrib.toulouse_axel.models.enfants_activites') as operation:
+        operation.side_effect = AxelError('FooBar')
+        resp = app.get('/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=3535&booking_date=2020-01-20&activity_type=MAT')
+    assert resp.json['err_desc'] == "Axel error: FooBar"
+    assert resp.json['err'] == 'error'
+
+    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/child_activities.xml')
+    with open(filepath) as xml:
+        content = xml.read()
+    with mock_getdata(content, 'EnfantsActivites'):
+        with mock.patch('passerelle.contrib.toulouse_axel.models.reservation_periode') as operation:
+            operation.side_effect = AxelError('FooBar')
+            resp = app.get('/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=3535&booking_date=2020-01-20&activity_type=MAT')
+    assert resp.json['err_desc'] == "Axel error: FooBar"
+    assert resp.json['err'] == 'error'
+
+
+@pytest.mark.parametrize('value', ['foo', '20/01/2020', '2020'])
+def test_clae_booking_activity_possible_days_endpoint_bad_date_format(app, resource, value):
+    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
+    resp = app.get('/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=3535&booking_date=%s&activity_type=MAT' % value, status=400)
+    assert resp.json['err_desc'] == "bad date format, should be YYYY-MM-DD"
+    assert resp.json['err'] == 'bad-request'
+
+
+def test_clae_booking_activity_possible_days_endpoint_activity_type(app, resource):
+    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
+    resp = app.get('/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=3535&booking_date=2020-01-20&activity_type=FOO', status=400)
+    assert resp.json['err_desc'] == "bad activity_type, should be MAT, MIDI, SOIR or GARD"
+    assert resp.json['err'] == 'bad-request'
+
+
+def test_clae_booking_activity_possible_days_endpoint_no_result(app, resource, child_activities_data):
+    resp = app.get('/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=3535&booking_date=2020-01-20&activity_type=MAT')
+    assert resp.json['err_desc'] == "Person not found"
+    assert resp.json['err'] == 'not-found'
+
+    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
+    filepath = os.path.join(os.path.dirname(__file__), 'data/toulouse_axel/child_activities.xml')
+    with open(filepath) as xml:
+        content = xml.read()
+    with mock_getdata(content, 'EnfantsActivites'):
+        with mock.patch('passerelle.contrib.toulouse_axel.models.reservation_periode') as operation:
+            operation.side_effect = AxelError('FooBar')
+            resp = app.get('/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=4242&booking_date=2020-01-20&activity_type=MAT')
+    assert resp.json['err_desc'] == "Child not found"
+    assert resp.json['err'] == 'not-found'
+
+    content = """<PORTAIL>
+  <DUI>
+    <IDDUI>XXX</IDDUI>
+    <ENFANT>
+      <IDPERSONNE>4242</IDPERSONNE>
+      <ACTIVITE>
+        <ANNEEREFERENCE>2019</ANNEEREFERENCE>
+        <IDACTIVITE>A19P1M1</IDACTIVITE>
+        <JOUR>00000</JOUR>
+      </ACTIVITE>
+    </ENFANT>
+  </DUI>
+</PORTAIL>"""
+    activities = child_activities_data['ENFANT'][0]
+    with mock_getdata(content, 'ReservationPeriode'):
+        with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities', return_value=activities):
+            resp = app.get('/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=3535&booking_date=2020-01-20&activity_type=MAT')
+    assert resp.json['err_desc'] == "Child not found"
+    assert resp.json['err'] == 'not-found'
+
+    with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.clae_booking_activities_info') as activities_info:
+        activities_info.return_value = {'data': []}
+        resp = app.get('/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=3535&booking_date=2020-01-20&activity_type=MAT')
+    assert resp.json['err'] == 0
+    assert resp.json['data'] == []
+
+
+def test_clae_booking_activity_possible_days_endpoint(app, resource, child_activities_data):
+    Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42')
+    content = """<PORTAIL>
+  <DUI>
+    <IDDUI>XXX</IDDUI>
+    <ENFANT>
+      <IDPERSONNE>3535</IDPERSONNE>
+      <ACTIVITE>
+        <ANNEEREFERENCE>2019</ANNEEREFERENCE>
+        <IDACTIVITE>A19P1M1</IDACTIVITE>
+        <JOUR>10201</JOUR>
+      </ACTIVITE>
+    </ENFANT>
+  </DUI>
+</PORTAIL>"""
+    activities = child_activities_data['ENFANT'][0]
+    with mock_getdata(content, 'ReservationPeriode'):
+        with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_child_activities', return_value=activities):
+            resp = app.get('/toulouse-axel/test/clae_booking_activity_possible_days?NameID=yyy&idpersonne=3535&booking_date=2020-01-20&activity_type=MAT')
+    assert resp.json['err'] == 0
+    assert len(resp.json['data']) == 5
+    assert set(resp.json['data'][0].keys()) == set(['id', 'text', 'disabled', 'prefill', 'details'])
+    assert resp.json['data'][0]['id'] == '3535-MAT-A19P1M1-monday'
+    assert resp.json['data'][1]['id'] == '3535-MAT-A19P1M1-tuesday'
+    assert resp.json['data'][2]['id'] == '3535-MAT-A19P1M1-wednesday'
+    assert resp.json['data'][3]['id'] == '3535-MAT-A19P1M1-thursday'
+    assert resp.json['data'][4]['id'] == '3535-MAT-A19P1M1-friday'
+    assert resp.json['data'][0]['text'] == 'Monday'
+    assert resp.json['data'][1]['text'] == 'Tuesday'
+    assert resp.json['data'][2]['text'] == 'Wednesday'
+    assert resp.json['data'][3]['text'] == 'Thursday'
+    assert resp.json['data'][4]['text'] == 'Friday'
+    assert resp.json['data'][0]['disabled'] is False
+    assert resp.json['data'][1]['disabled'] is False
+    assert resp.json['data'][2]['disabled'] is True
+    assert resp.json['data'][3]['disabled'] is False
+    assert resp.json['data'][4]['disabled'] is False
+    assert resp.json['data'][0]['prefill'] is True
+    assert resp.json['data'][1]['prefill'] is False
+    assert resp.json['data'][2]['prefill'] is None
+    assert resp.json['data'][3]['prefill'] is False
+    assert resp.json['data'][4]['prefill'] is True
+
+
 def test_are_children_registered_axel_error(resource):
     with mock.patch('passerelle.contrib.toulouse_axel.models.enfants_activites') as operation:
         operation.side_effect = AxelError('FooBar')
-- 
2.20.1

