diff --git a/passerelle/contrib/toulouse_maelis/models.py b/passerelle/contrib/toulouse_maelis/models.py index e1b72bf0..b31583d3 100644 --- a/passerelle/contrib/toulouse_maelis/models.py +++ b/passerelle/contrib/toulouse_maelis/models.py @@ -496,6 +496,23 @@ class ToulouseMaelis(BaseResource, HTTPResource): if value is None: dico[key] = '' + def get_person_activity_list_raw( + self, family_id, person_id, nature_id=None, reference_year=None, start_date=None, end_date=None + ): + params = { + 'numDossier': family_id, + 'numPerson': person_id, + 'codeNatureActivity': nature_id, + 'yearSchool': reference_year, + 'dateStartActivity': start_date, + 'dateEndActivity': end_date, + } + response = self.call( + 'Activity', 'getPersonCatalogueActivity', getPersonCatalogueActivityRequestBean=params + ) + data = serialize_object(response) + return data + @endpoint( display_category='Famille', description='Liste des catégories', @@ -2099,6 +2116,177 @@ class ToulouseMaelis(BaseResource, HTTPResource): response = self.call('Family', 'presubscribeSchoolSibling', **post_data) return {'data': serialize_object(response)} + @endpoint( + display_category='Inscriptions', + description="Catalogue des activités d'une personne", + name='get-person-activity-list', + perm='can_access', + parameters={ + 'NameID': {'description': 'Publik NameID'}, + 'family_id': {'description': 'Numéro de DUI'}, + 'person_id': {'description': "Numéro du responsale légal ou de l'enfant"}, + 'nature_id': {'description': "Numéro de la nature des activités"}, + 'start_date': {'description': 'Début de la période'}, + 'end_date': {'description': 'Fin de la période'}, + 'text_template': { + 'description': "template utilisé pour la valeur text (URL encoding)", + 'example_value': '{{ activity.libelle2 }}', + }, + }, + ) + def get_person_activity_list( + self, + request, + person_id, + NameID=None, + family_id=None, + nature_id=None, + start_date=None, + end_date=None, + text_template=None, + ): + family_id = family_id or self.get_link(NameID).family_id + reference_year = None + if start_date and end_date: + start_date, end_date, reference_year = self.get_start_and_end_dates(start_date, end_date) + if not text_template: + text_template = '{{ activity.libelle2|default:activity.libelle1 }}' + + response = self.get_person_activity_list_raw( + family_id, + person_id, + nature_id=nature_id, + reference_year=reference_year, + start_date=start_date and start_date.strftime(utils.json_date_format), + end_date=start_date and end_date.strftime(utils.json_date_format), + ) + for item in response['catalogueActivityList']: + item['id'] = item['activity']['idActivity'] + item['text'] = render_to_string(text_template, item).strip() + return {'data': response['catalogueActivityList'], 'meta': {'person': response['person']}} + + @endpoint( + display_category='Inscriptions', + description="Liste des unités d'une activité pour une personne", + name='get-person-unit-list', + perm='can_access', + parameters={ + 'NameID': {'description': 'Publik NameID'}, + 'family_id': {'description': 'Numéro de DUI'}, + 'person_id': {'description': "Numéro du responsale légal ou de l'enfant"}, + 'activity_id': {'description': "Numéro de l'activités"}, + 'start_date': {'description': 'Début de la période'}, + 'end_date': {'description': 'Fin de la période'}, + 'text_template': { + 'description': 'template utilisée pour la valeur text (URL encoding)', + 'example_value': '{{ libelle }}', + }, + }, + ) + def get_person_unit_list( + self, + request, + person_id, + activity_id, + NameID=None, + family_id=None, + start_date=None, + end_date=None, + text_template=None, + ): + family_id = family_id or self.get_link(NameID).family_id + reference_year = None + if start_date and end_date: + start_date, end_date, reference_year = self.get_start_and_end_dates(start_date, end_date) + if not text_template: + text_template = '{{ libelle }}' + + response = self.get_person_activity_list_raw( + family_id, + person_id, + reference_year=reference_year, + start_date=start_date and start_date.strftime(utils.json_date_format), + end_date=start_date and end_date.strftime(utils.json_date_format), + ) + for activity in response['catalogueActivityList']: + if activity['activity']['idActivity'] == activity_id: + break + else: + raise APIError('No activity %s for person' % activity_id, err_code='no-activity') + data = activity.pop('unitInfoList') + meta = {'person': response['person'], 'activity': activity} + for item in data: + item['id'] = item['idUnit'] + context = dict(item) + context['meta'] = meta + item['text'] = render_to_string(text_template, context).strip() + return {'data': data, 'meta': meta} + + @endpoint( + display_category='Inscriptions', + description="Liste des lieux d'une unité pour une personne", + name='get-person-place-list', + perm='can_access', + parameters={ + 'NameID': {'description': 'Publik NameID'}, + 'family_id': {'description': 'Numéro de DUI'}, + 'person_id': {'description': "Numéro du responsale légal ou de l'enfant"}, + 'activity_id': {'description': "Numéro de l'activités"}, + 'unit_id': {'description': "Numéro de l'unité"}, + 'start_date': {'description': 'Début de la période'}, + 'end_date': {'description': 'Fin de la période'}, + 'text_template': { + 'description': 'template utilisée pour la valeur text (URL encoding)', + 'example_value': '{{ libelle }}', + }, + }, + ) + def get_person_place_list( + self, + request, + person_id, + activity_id, + unit_id, + NameID=None, + family_id=None, + start_date=None, + end_date=None, + text_template=None, + ): + family_id = family_id or self.get_link(NameID).family_id + reference_year = None + if start_date and end_date: + start_date, end_date, reference_year = self.get_start_and_end_dates(start_date, end_date) + if not text_template: + text_template = '{{ place.lib2|default:place.lib1 }}' + + response = self.get_person_activity_list_raw( + family_id, + person_id, + reference_year=reference_year, + start_date=start_date and start_date.strftime(utils.json_date_format), + end_date=start_date and end_date.strftime(utils.json_date_format), + ) + for activity in response['catalogueActivityList']: + if activity['activity']['idActivity'] == activity_id: + break + else: + raise APIError('No activity %s for person' % activity_id, err_code='no-activity') + for unit in activity['unitInfoList']: + if unit['idUnit'] == unit_id: + break + else: + raise APIError('No unit %s for person' % unit_id, err_code='no-unit') + data = unit.pop('placeInfoList') + del activity['unitInfoList'] + meta = {'person': response['person'], 'activity': activity, 'unit': unit} + for item in data: + item['id'] = item['place']['idPlace'] + context = dict(item) + context['meta'] = meta + item['text'] = render_to_string(text_template, context).strip() + return {'data': data, 'meta': meta} + class Link(models.Model): resource = models.ForeignKey(ToulouseMaelis, on_delete=models.CASCADE) diff --git a/tests/data/toulouse_maelis/R_read_person_catalog_activity.xml b/tests/data/toulouse_maelis/R_read_person_catalog_activity.xml new file mode 100644 index 00000000..cc5c53a0 --- /dev/null +++ b/tests/data/toulouse_maelis/R_read_person_catalog_activity.xml @@ -0,0 +1,191 @@ + + + + + + 2014-04-01T00:00:00+02:00 + BART + SIMPSON + 246423 + M + + + + A10051141965 + Vitrail Fusing 1/2 Je Adultes 2022/2023 - Mardi 14h-17h + Activité modèle + + LOI_ADU + Loisirs Adultes + + P + Loisirs + + + I + I + + + A10051141970 + Inscription 2ème semestre + 2023-02-01T00:00:00+01:00 + 2023-06-30T00:00:00+02:00 + + + A10053179226 + Centre Culturel ALBAN MINVILLE + H + + + true + + + + + A10051141990 + Inscription 1er semestre + 2022-09-01T00:00:00+02:00 + 2023-01-31T00:00:00+01:00 + + + A10053179226 + Centre Culturel ALBAN MINVILLE + H + + + true + + + + + A10051141968 + Inscription annuelle + 2022-09-01T00:00:00+02:00 + 2023-06-30T00:00:00+02:00 + + + A10053179226 + Centre Culturel ALBAN MINVILLE + N + + + true + + + + false + + + + A10053187087 + Vacances Ete 2023 + + LOI_VAC + Loisirs - Vacances + + V + Vacances Enfants + + + I + I + + + A10053187241 + Juillet + 2023-07-10T00:00:00+02:00 + 2023-07-31T00:00:00+02:00 + + + A10053179604 + ALEX JANY + H + 2.0 + 1.0 + + + true + + + + + A10053187242 + Aout + 2023-08-01T00:00:00+02:00 + 2023-08-31T00:00:00+02:00 + + + A10053179604 + ALEX JANY + M + 2.0 + 1.0 + + + true + + + + false + + + + A10053187065 + Vacances Hivers 2023 + + LOI_VAC + Loisirs - Vacances + + V + Vacances Enfants + + + I + I + + + A10053187086 + Semaine 2 + 2023-02-27T00:00:00+01:00 + 2023-03-03T00:00:00+01:00 + + + A10053179604 + ALEX JANY + 2011-01-01T00:00:00+01:00 + 2018-12-31T00:00:00+01:00 + H + 2.0 + 1.0 + + + true + + + + + A10053187085 + Semaine 1 + 2023-02-20T00:00:00+01:00 + 2023-02-24T00:00:00+01:00 + + + A10053179604 + ALEX JANY + 2011-01-01T00:00:00+01:00 + 2020-12-31T00:00:00+01:00 + H + 2.0 + 1.0 + + + true + + + + false + + + + + diff --git a/tests/test_toulouse_maelis.py b/tests/test_toulouse_maelis.py index 9adb649f..05b3a31f 100644 --- a/tests/test_toulouse_maelis.py +++ b/tests/test_toulouse_maelis.py @@ -4646,3 +4646,432 @@ def test_create_child_school_pre_registration_with_sibling(family_service, con, assert resp.json['data']['codeWait'] == 'MO_FRATERIE' assert resp.json['data']['derogReason'] == '01PRIO-5' assert resp.json['data']['derogComment'] == 'SERGHEI3 LISA' + + +def test_get_person_activity_list(activity_service, con, app): + def request_check(request): + assert request.yearSchool == 2022 + + activity_service.add_soap_response( + 'getPersonCatalogueActivity', + get_xml_file('R_read_person_catalog_activity.xml'), + request_check=request_check, + ) + url = get_endpoint('get-person-activity-list') + + params = { + 'NameID': '', + 'family_id': '311323', + 'person_id': '246423', + 'nature_id': '', + 'start_date': '2022-09-01', + 'end_date': '2023-08-31', + 'text_template': '', + } + resp = app.get(url, params=params) + assert resp.json['err'] == 0 + Link.objects.create(resource=con, family_id='311323', name_id='local') + + params['NameID'] = 'local' + params['family_id'] = '' + resp = app.get(url, params=params) + assert resp.json['err'] == 0 + assert [(x['id'], x['text']) for x in resp.json['data']] == [ + ('A10051141965', 'Activité modèle'), + ('A10053187087', 'Vacances Ete 2023'), + ('A10053187065', 'Vacances Hivers 2023'), + ] + data = resp.json['data'][1] + del data['unitInfoList'][1] + assert data == { + 'activity': { + 'activityType': { + 'code': 'LOI_VAC', + 'libelle': 'Loisirs - Vacances', + 'natureSpec': {'code': 'V', 'libelle': 'Vacances Enfants'}, + }, + 'idActivity': 'A10053187087', + 'libelle1': 'Vacances Ete 2023', + 'libelle2': None, + 'paiementPortal': 'I', + 'typInsPortal': 'I', + }, + 'id': 'A10053187087', + 'incompleteFamilyFile': False, + 'indicatorBlockSubscribeList': [], + 'text': 'Vacances Ete 2023', + 'unitInfoList': [ + { + 'dateEnd': '2023-07-31T00:00:00+02:00', + 'dateStart': '2023-07-10T00:00:00+02:00', + 'idIns': None, + 'idUnit': 'A10053187241', + 'libelle': 'Juillet', + 'placeInfoList': [ + { + 'capacityInfo': {'controlOK': True, 'message': None}, + 'idIns': None, + 'place': { + 'ageEnd': None, + 'ageStart': None, + 'ctrlPlaces': 'H', + 'etatIns': None, + 'idIns': None, + 'idPlace': 'A10053179604', + 'latitude': 1, + 'lib1': 'ALEX JANY', + 'lib2': None, + 'listBlocNoteBean': [], + 'longitude': 2, + }, + } + ], + }, + ], + } + assert resp.json['meta']['person']['numPerson'] == 246423 + + params['text_template'] = '{{ unitInfoList.0.libelle }}' + resp = app.get(url, params=params) + assert resp.json['err'] == 0 + assert [(x['id'], x['text']) for x in resp.json['data']] == [ + ('A10051141965', 'Inscription 2ème semestre'), + ('A10053187087', 'Juillet'), + ('A10053187065', 'Semaine 2'), + ] + + +def test_get_person_activity_list_not_linked_error(con, app): + url = get_endpoint('get-person-activity-list') + params = { + 'NameID': 'local', + 'family_id': '', + 'person_id': '246423', + 'nature_id': '', + 'start_date': '2022-09-01', + 'end_date': '2023-08-31', + 'text_template': '', + } + resp = app.get(url, params=params) + assert resp.json['err'] == 'not-linked' + assert resp.json['err_desc'] == 'User not linked to family' + + +def test_get_person_activity_list_date_error(con, app): + url = get_endpoint('get-person-activity-list') + + params = { + 'NameID': '', + 'family_id': '311323', + 'person_id': '246423', + 'nature_id': '', + 'start_date': 'bad', + 'end_date': '2023-08-31', + 'text_template': '', + } + resp = app.get(url, params=params, status=400) + assert resp.json['err'] == 'bad-request' + assert resp.json['err_desc'] == 'bad date format, should be YYYY-MM-DD' + + params['start_date'] = '2022-09-01' + params['end_date'] = 'bad' + resp = app.get(url, params=params, status=400) + assert resp.json['err'] == 'bad-request' + assert resp.json['err_desc'] == 'bad date format, should be YYYY-MM-DD' + + params['start_date'] = '2023-09-01' + params['end_date'] = '2023-08-31' + resp = app.get(url, params=params, status=400) + assert resp.json['err'] == 'bad-request' + assert resp.json['err_desc'] == 'start_date should be before end_date' + + params['start_date'] = '2022-09-01' + params['end_date'] = '2024-08-31' + resp = app.get(url, params=params, status=400) + assert resp.json['err'] == 'bad-request' + assert resp.json['err_desc'] == 'start_date and end_date are in different reference year (2022 != 2023)' + + +def test_get_person_unit_list(activity_service, con, app): + def request_check(request): + assert request.yearSchool == 2022 + + activity_service.add_soap_response( + 'getPersonCatalogueActivity', + get_xml_file('R_read_person_catalog_activity.xml'), + request_check=request_check, + ) + url = get_endpoint('get-person-unit-list') + + params = { + 'NameID': '', + 'family_id': '311323', + 'person_id': '246423', + 'activity_id': 'A10053187087', + 'start_date': '2022-09-01', + 'end_date': '2023-08-31', + 'text_template': '', + } + resp = app.get(url, params=params) + assert resp.json['err'] == 0 + Link.objects.create(resource=con, family_id='311323', name_id='local') + + params['NameID'] = 'local' + params['family_id'] = '' + resp = app.get(url, params=params) + assert resp.json['err'] == 0 + assert [(x['id'], x['text']) for x in resp.json['data']] == [ + ('A10053187241', 'Juillet'), + ('A10053187242', 'Aout'), + ] + assert resp.json['data'][0] == { + 'dateEnd': '2023-07-31T00:00:00+02:00', + 'dateStart': '2023-07-10T00:00:00+02:00', + 'id': 'A10053187241', + 'idIns': None, + 'idUnit': 'A10053187241', + 'libelle': 'Juillet', + 'placeInfoList': [ + { + 'capacityInfo': {'controlOK': True, 'message': None}, + 'idIns': None, + 'place': { + 'ageEnd': None, + 'ageStart': None, + 'ctrlPlaces': 'H', + 'etatIns': None, + 'idIns': None, + 'idPlace': 'A10053179604', + 'latitude': 1, + 'lib1': 'ALEX JANY', + 'lib2': None, + 'listBlocNoteBean': [], + 'longitude': 2, + }, + } + ], + 'text': 'Juillet', + } + assert resp.json['meta']['person']['numPerson'] == 246423 + assert resp.json['meta']['activity']['activity']['idActivity'] == 'A10053187087' + + params['text_template'] = '{{ meta.activity.activity.activityType.natureSpec.libelle }} - {{ libelle }}' + resp = app.get(url, params=params) + assert resp.json['err'] == 0 + assert [(x['id'], x['text']) for x in resp.json['data']] == [ + ('A10053187241', 'Vacances Enfants - Juillet'), + ('A10053187242', 'Vacances Enfants - Aout'), + ] + + +def test_get_person_unit_list_not_linked_error(con, app): + url = get_endpoint('get-person-unit-list') + params = { + 'NameID': 'local', + 'family_id': '', + 'person_id': '246423', + 'activity_id': 'A10053187087', + 'start_date': '2022-09-01', + 'end_date': '2023-08-31', + 'text_template': '', + } + resp = app.get(url, params=params) + assert resp.json['err'] == 'not-linked' + assert resp.json['err_desc'] == 'User not linked to family' + + +def test_get_person_unit_list_date_error(con, app): + url = get_endpoint('get-person-unit-list') + + params = { + 'NameID': '', + 'family_id': '311323', + 'person_id': '246423', + 'activity_id': 'A10053187087', + 'start_date': 'bad', + 'end_date': '2023-08-31', + 'text_template': '', + } + resp = app.get(url, params=params, status=400) + assert resp.json['err'] == 'bad-request' + assert resp.json['err_desc'] == 'bad date format, should be YYYY-MM-DD' + + params['start_date'] = '2022-09-01' + params['end_date'] = 'bad' + resp = app.get(url, params=params, status=400) + assert resp.json['err'] == 'bad-request' + assert resp.json['err_desc'] == 'bad date format, should be YYYY-MM-DD' + + params['start_date'] = '2023-09-01' + params['end_date'] = '2023-08-31' + resp = app.get(url, params=params, status=400) + assert resp.json['err'] == 'bad-request' + assert resp.json['err_desc'] == 'start_date should be before end_date' + + params['start_date'] = '2022-09-01' + params['end_date'] = '2024-08-31' + resp = app.get(url, params=params, status=400) + assert resp.json['err'] == 'bad-request' + assert resp.json['err_desc'] == 'start_date and end_date are in different reference year (2022 != 2023)' + + +def test_get_person_unit_list_no_activity_error(activity_service, con, app): + activity_service.add_soap_response( + 'getPersonCatalogueActivity', + get_xml_file('R_read_person_catalog_activity.xml'), + ) + url = get_endpoint('get-person-unit-list') + + params = { + 'NameID': '', + 'family_id': '311323', + 'person_id': '246423', + 'activity_id': 'plop', + 'start_date': '2022-09-01', + 'end_date': '2023-08-31', + 'text_template': '', + } + resp = app.get(url, params=params) + assert resp.json['err'] == 'no-activity' + assert resp.json['err_desc'] == 'No activity plop for person' + + +def test_get_person_place_list(activity_service, con, app): + def request_check(request): + assert request.yearSchool == 2022 + + activity_service.add_soap_response( + 'getPersonCatalogueActivity', + get_xml_file('R_read_person_catalog_activity.xml'), + request_check=request_check, + ) + url = get_endpoint('get-person-place-list') + + params = { + 'NameID': '', + 'family_id': '311323', + 'person_id': '246423', + 'activity_id': 'A10053187087', + 'unit_id': 'A10053187241', + 'start_date': '2022-09-01', + 'end_date': '2023-08-31', + 'text_template': '', + } + resp = app.get(url, params=params) + assert resp.json['err'] == 0 + Link.objects.create(resource=con, family_id='311323', name_id='local') + + params['NameID'] = 'local' + params['family_id'] = '' + resp = app.get(url, params=params) + assert resp.json['err'] == 0 + assert [(x['id'], x['text']) for x in resp.json['data']] == [('A10053179604', 'ALEX JANY')] + assert resp.json['data'] == [ + { + 'capacityInfo': {'controlOK': True, 'message': None}, + 'id': 'A10053179604', + 'idIns': None, + 'place': { + 'ageEnd': None, + 'ageStart': None, + 'ctrlPlaces': 'H', + 'etatIns': None, + 'idIns': None, + 'idPlace': 'A10053179604', + 'latitude': 1.0, + 'lib1': 'ALEX JANY', + 'lib2': None, + 'listBlocNoteBean': [], + 'longitude': 2.0, + }, + 'text': 'ALEX JANY', + } + ] + assert resp.json['meta']['person']['numPerson'] == 246423 + assert resp.json['meta']['activity']['activity']['idActivity'] == 'A10053187087' + assert resp.json['meta']['unit']['idUnit'] == 'A10053187241' + + params['text_template'] = '{{ meta.unit.libelle }} - {{ place.lib1 }}' + resp = app.get(url, params=params) + assert resp.json['err'] == 0 + assert [(x['id'], x['text']) for x in resp.json['data']] == [('A10053179604', 'Juillet - ALEX JANY')] + + +def test_get_person_place_list_not_linked_error(con, app): + url = get_endpoint('get-person-place-list') + params = { + 'NameID': 'local', + 'family_id': '', + 'person_id': '246423', + 'activity_id': 'A10053187087', + 'unit_id': 'A10053187241', + 'start_date': '2022-09-01', + 'end_date': '2023-08-31', + 'text_template': '', + } + resp = app.get(url, params=params) + assert resp.json['err'] == 'not-linked' + assert resp.json['err_desc'] == 'User not linked to family' + + +def test_get_person_place_list_date_error(con, app): + url = get_endpoint('get-person-place-list') + + params = { + 'NameID': '', + 'family_id': '311323', + 'person_id': '246423', + 'activity_id': 'A10053187087', + 'unit_id': 'A10053187241', + 'start_date': 'bad', + 'end_date': '2023-08-31', + 'text_template': '', + } + resp = app.get(url, params=params, status=400) + assert resp.json['err'] == 'bad-request' + assert resp.json['err_desc'] == 'bad date format, should be YYYY-MM-DD' + + params['start_date'] = '2022-09-01' + params['end_date'] = 'bad' + resp = app.get(url, params=params, status=400) + assert resp.json['err'] == 'bad-request' + assert resp.json['err_desc'] == 'bad date format, should be YYYY-MM-DD' + + params['start_date'] = '2023-09-01' + params['end_date'] = '2023-08-31' + resp = app.get(url, params=params, status=400) + assert resp.json['err'] == 'bad-request' + assert resp.json['err_desc'] == 'start_date should be before end_date' + + params['start_date'] = '2022-09-01' + params['end_date'] = '2024-08-31' + resp = app.get(url, params=params, status=400) + assert resp.json['err'] == 'bad-request' + assert resp.json['err_desc'] == 'start_date and end_date are in different reference year (2022 != 2023)' + + +def test_get_person_place_list_no_unit_error(activity_service, con, app): + activity_service.add_soap_response( + 'getPersonCatalogueActivity', + get_xml_file('R_read_person_catalog_activity.xml'), + ) + url = get_endpoint('get-person-place-list') + + params = { + 'NameID': '', + 'family_id': '311323', + 'person_id': '246423', + 'activity_id': 'plop', + 'unit_id': 'plop', + 'start_date': '2022-09-01', + 'end_date': '2023-08-31', + 'text_template': '', + } + resp = app.get(url, params=params) + assert resp.json['err'] == 'no-activity' + assert resp.json['err_desc'] == 'No activity plop for person' + + params['activity_id'] = 'A10053187087' + resp = app.get(url, params=params) + assert resp.json['err'] == 'no-unit' + assert resp.json['err_desc'] == 'No unit plop for person'