diff --git a/functests/caluire_axel/test_caluire_axel.py b/functests/caluire_axel/test_caluire_axel.py
index 20c881f8..8c0f91d2 100644
--- a/functests/caluire_axel/test_caluire_axel.py
+++ b/functests/caluire_axel/test_caluire_axel.py
@@ -1,3 +1,4 @@
+import datetime
import pprint
import requests
@@ -48,6 +49,19 @@ def test_link(conn, user):
assert res['err'] == 0
print('\n')
+ print("and GET school info")
+ url = conn + '/child_schooling_info?NameID=%s&idpersonne=%s&booking_date=%s' % (
+ name_id,
+ child['IDENT'],
+ datetime.date.today().strftime('%Y-%m-%d'),
+ )
+ resp = requests.get(url)
+ resp.raise_for_status()
+ res = resp.json()
+ pprint.pprint(res)
+ assert res['err'] == 0
+ print('\n')
+
print("Deleting link")
url = conn + '/unlink?NameID=%s' % name_id
resp = requests.post(url)
diff --git a/passerelle/contrib/caluire_axel/models.py b/passerelle/contrib/caluire_axel/models.py
index de3b0b1a..22ede3b4 100644
--- a/passerelle/contrib/caluire_axel/models.py
+++ b/passerelle/contrib/caluire_axel/models.py
@@ -14,6 +14,8 @@
# You should have received a copy of the GNU Affero General Public License
# along with this program. If not, see .
+import datetime
+
from django.db import models
from django.utils.translation import ugettext_lazy as _
@@ -22,7 +24,7 @@ from passerelle.contrib.utils import axel
from passerelle.utils.api import endpoint
from passerelle.utils.jsonresponse import APIError
-from . import schemas
+from . import schemas, utils
class CaluireAxel(BaseResource):
@@ -32,7 +34,7 @@ class CaluireAxel(BaseResource):
)
category = _('Business Process Connectors')
- _category_ordering = [_('Family account')]
+ _category_ordering = [_('Family account'), _('Schooling')]
class Meta:
verbose_name = _('Caluire Axel')
@@ -150,6 +152,13 @@ class CaluireAxel(BaseResource):
return family_data
+ def get_child_data(self, family_id, child_id):
+ family_data = self.get_family_data(family_id)
+ for child in family_data.get('MEMBRE', []):
+ if child['IDENT'] == child_id:
+ return child
+ return None
+
@endpoint(
display_category=_('Family account'),
display_order=3,
@@ -188,15 +197,51 @@ class CaluireAxel(BaseResource):
'idpersonne': {'description': _('Child ID')},
},
)
- def child_info(self, request, idpersonne, NameID):
+ def child_info(self, request, NameID, idpersonne):
link = self.get_link(NameID)
- family_data = self.get_family_data(link.family_id)
+ child_data = self.get_child_data(link.family_id, idpersonne)
+ if child_data is None:
+ raise APIError('Child not found', err_code='not-found')
+ return {'data': child_data}
- for child in family_data.get('MEMBRE', []):
- if child['IDENT'] == idpersonne:
- return {'data': child}
+ @endpoint(
+ display_category=_('Schooling'),
+ display_order=1,
+ description=_("Get information about schooling of a child"),
+ perm='can_access',
+ parameters={
+ 'NameID': {'description': _('Publik ID')},
+ 'idpersonne': {'description': _('Child ID')},
+ 'booking_date': {'description': _('Booking date (to get reference year)')},
+ },
+ )
+ def child_schooling_info(self, request, NameID, idpersonne, booking_date):
+ link = self.get_link(NameID)
+ try:
+ booking_date = datetime.datetime.strptime(booking_date, axel.json_date_format)
+ except ValueError:
+ raise APIError('bad date format, should be YYYY-MM-DD', err_code='bad-request', http_status=400)
- raise APIError('Child not found', err_code='not-found')
+ child_data = self.get_child_data(link.family_id, idpersonne)
+ if child_data is None:
+ raise APIError('Child not found', err_code='not-found')
+
+ reference_year = utils.get_reference_year_from_date(booking_date)
+ try:
+ result = schemas.get_individu(
+ self,
+ {'PORTAIL': {'GETINDIVIDU': {'IDENTINDIVIDU': idpersonne, 'ANNEE': str(reference_year)}}},
+ )
+ except axel.AxelError as e:
+ raise APIError(
+ 'Axel error: %s' % e,
+ err_code='error',
+ data={'xml_request': e.xml_request, 'xml_response': e.xml_response},
+ )
+
+ schooling_data = result.json_response['DATA']['PORTAIL']['GETINDIVIDU']
+
+ return {'data': schooling_data}
class Link(models.Model):
diff --git a/passerelle/contrib/caluire_axel/schemas.py b/passerelle/contrib/caluire_axel/schemas.py
index f1d579d8..eba5b858 100644
--- a/passerelle/contrib/caluire_axel/schemas.py
+++ b/passerelle/contrib/caluire_axel/schemas.py
@@ -74,6 +74,7 @@ class Operation(axel.Operation):
find_individus = Operation('FindIndividus')
get_famille_individus = Operation('GetFamilleIndividus')
+get_individu = Operation('GetIndividu')
LINK_SCHEMA = copy.deepcopy(
diff --git a/passerelle/contrib/caluire_axel/utils.py b/passerelle/contrib/caluire_axel/utils.py
new file mode 100644
index 00000000..49a3377d
--- /dev/null
+++ b/passerelle/contrib/caluire_axel/utils.py
@@ -0,0 +1,23 @@
+# -*- coding: utf-8 -*-
+# passerelle - uniform access to multiple data sources and services
+# Copyright (C) 2021 Entr'ouvert
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+
+def get_reference_year_from_date(booking_date):
+ if booking_date.month <= 8:
+ # between january and august, reference year is the year just before
+ return booking_date.year - 1
+ return booking_date.year
diff --git a/passerelle/contrib/caluire_axel/xsd/Q_GetIndividu.xsd b/passerelle/contrib/caluire_axel/xsd/Q_GetIndividu.xsd
new file mode 100644
index 00000000..e4248a49
--- /dev/null
+++ b/passerelle/contrib/caluire_axel/xsd/Q_GetIndividu.xsd
@@ -0,0 +1,26 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/passerelle/contrib/caluire_axel/xsd/R_GetIndividu.xsd b/passerelle/contrib/caluire_axel/xsd/R_GetIndividu.xsd
new file mode 100644
index 00000000..8768ae75
--- /dev/null
+++ b/passerelle/contrib/caluire_axel/xsd/R_GetIndividu.xsd
@@ -0,0 +1,63 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/tests/data/caluire_axel/schooling_info.xml b/tests/data/caluire_axel/schooling_info.xml
new file mode 100644
index 00000000..b46d872a
--- /dev/null
+++ b/tests/data/caluire_axel/schooling_info.xml
@@ -0,0 +1,36 @@
+
+
+ 0
+
+ ECOLE1
+ Ecole Elémentaire
+ CE2
+ Cours élémentaire 2ème année
+
+
+ 50632
+
+ CALUIRE TEST
+ Enfant 1
+ 10/10/2013
+ M
+
+
+
+
+ N
+ O
+
+
+
+ 30
+ RUE PASTEUR
+
+ 69300
+ CALUIRE ET CUIRE
+
+
+
+
+
+
diff --git a/tests/test_caluire_axel.py b/tests/test_caluire_axel.py
index 637558e3..49d80aa7 100644
--- a/tests/test_caluire_axel.py
+++ b/tests/test_caluire_axel.py
@@ -16,6 +16,7 @@
# along with this program. If not, see .
import os
+import xml.etree.ElementTree as ET
from contextlib import contextmanager
import mock
@@ -45,6 +46,33 @@ def link_params():
}
+@pytest.fixture
+def family_data():
+ filepath = os.path.join(os.path.dirname(__file__), 'data/caluire_axel/family_info.xml')
+ with open(filepath) as xml:
+ content = xml.read()
+ resp = (
+ '''
+
+
+
+ GetFamilleIndividus
+ OK
+ 10/10/2010 10:10:01
+
+
+
+ %s
+
+
+ '''.strip()
+ % content
+ )
+ return schemas.get_famille_individus.response_converter.decode(ET.fromstring(resp))['DATA']['PORTAIL'][
+ 'GETFAMILLE'
+ ]
+
+
@contextmanager
def mock_getdata(content, operation):
with mock.patch('passerelle.contrib.caluire_axel.models.CaluireAxel.soap_client') as client:
@@ -131,6 +159,48 @@ def test_operation_find_individus(resource, content):
)
+@pytest.mark.parametrize(
+ 'content',
+ [
+ '',
+ ],
+)
+def test_operation_get_famille_individus(resource, content):
+ with mock_getdata(content, 'GetFamilleIndividus'):
+ with pytest.raises(AxelError):
+ schemas.get_famille_individus(
+ resource,
+ {
+ 'PORTAIL': {
+ 'GETFAMILLE': {
+ 'IDENTFAMILLE': 'XXX',
+ }
+ }
+ },
+ )
+
+
+@pytest.mark.parametrize(
+ 'content',
+ [
+ '',
+ ],
+)
+def test_operation_get_individu(resource, content):
+ with mock_getdata(content, 'GetIndividu'):
+ with pytest.raises(AxelError):
+ schemas.get_individu(
+ resource,
+ {
+ 'PORTAIL': {
+ 'GETINDIVIDU': {
+ 'IDENTINDIVIDU': 'XXX',
+ }
+ }
+ },
+ )
+
+
def test_link_endpoint_nameid_empty(app, resource, link_params):
resp = app.post_json('/caluire-axel/test/link?NameID=', params=link_params, status=400)
assert resp.json['err_desc'] == "NameID is empty"
@@ -440,13 +510,13 @@ def test_child_info_endpoint_axel_error(app, resource):
Link.objects.create(resource=resource, name_id='yyy', family_id='XXX', person_id='42')
with mock.patch('passerelle.contrib.caluire_axel.schemas.get_famille_individus') as operation:
operation.side_effect = AxelError('FooBar')
- resp = app.get('/caluire-axel/test/child_info?NameID=yyy&idpersonne=zzz')
+ resp = app.get('/caluire-axel/test/child_info?NameID=yyy&idpersonne=50632')
assert resp.json['err_desc'] == "Axel error: FooBar"
assert resp.json['err'] == 'error'
def test_child_info_endpoint_no_result(app, resource):
- resp = app.get('/caluire-axel/test/child_info?NameID=yyy&idpersonne=zzz')
+ resp = app.get('/caluire-axel/test/child_info?NameID=yyy&idpersonne=50632')
assert resp.json['err_desc'] == "Person not found"
assert resp.json['err'] == 'not-found'
@@ -490,3 +560,73 @@ def test_child_info_endpoint(app, resource):
)
assert resp.json['data']['id'] == '50632'
assert resp.json['data']['text'] == 'Enfant 1 CALUIRE TEST'
+
+
+def test_child_schooling_info_endpoint_axel_error(app, resource):
+ Link.objects.create(resource=resource, name_id='yyy', family_id='XXX', person_id='42')
+ with mock.patch('passerelle.contrib.caluire_axel.schemas.get_famille_individus') as operation:
+ operation.side_effect = AxelError('FooBar')
+ resp = app.get(
+ '/caluire-axel/test/child_schooling_info?NameID=yyy&idpersonne=50632&booking_date=2021-05-10'
+ )
+ assert resp.json['err_desc'] == "Axel error: FooBar"
+ assert resp.json['err'] == 'error'
+
+ filepath = os.path.join(os.path.dirname(__file__), 'data/caluire_axel/family_info.xml')
+ with open(filepath) as xml:
+ content = xml.read()
+ with mock_getdata(content, 'GetFamilleIndividus'):
+ with mock.patch('passerelle.contrib.caluire_axel.schemas.get_individu') as operation:
+ operation.side_effect = AxelError('FooBar')
+ resp = app.get(
+ '/caluire-axel/test/child_schooling_info?NameID=yyy&idpersonne=50632&booking_date=2021-05-10'
+ )
+ assert resp.json['err_desc'] == "Axel error: FooBar"
+ assert resp.json['err'] == 'error'
+
+
+@pytest.mark.parametrize('value', ['foo', '20/01/2020', '2020'])
+def test_child_schooling_info_endpoint_bad_date_format(app, resource, value):
+ Link.objects.create(resource=resource, name_id='yyy', family_id='XXX', person_id='42')
+ resp = app.get(
+ '/caluire-axel/test/child_schooling_info?NameID=yyy&idpersonne=50632&booking_date=%s' % value,
+ status=400,
+ )
+ assert resp.json['err_desc'] == "bad date format, should be YYYY-MM-DD"
+ assert resp.json['err'] == 'bad-request'
+
+
+def test_child_schooling_info_endpoint_no_result(app, resource):
+ resp = app.get(
+ '/caluire-axel/test/child_schooling_info?NameID=yyy&idpersonne=50632&booking_date=2021-05-10'
+ )
+ assert resp.json['err_desc'] == "Person not found"
+ assert resp.json['err'] == 'not-found'
+
+ Link.objects.create(resource=resource, name_id='yyy', family_id='XXX', person_id='42')
+ filepath = os.path.join(os.path.dirname(__file__), 'data/caluire_axel/family_info.xml')
+ with open(filepath) as xml:
+ content = xml.read()
+ with mock_getdata(content, 'GetFamilleIndividus'):
+ resp = app.get(
+ '/caluire-axel/test/child_schooling_info?NameID=yyy&idpersonne=zzz&booking_date=2021-05-10'
+ )
+ assert resp.json['err_desc'] == "Child not found"
+ assert resp.json['err'] == 'not-found'
+
+
+def test_child_schooling_info(app, resource, family_data):
+ Link.objects.create(resource=resource, name_id='yyy', family_id='XXX', person_id='42')
+ filepath = os.path.join(os.path.dirname(__file__), 'data/caluire_axel/schooling_info.xml')
+ with open(filepath) as xml:
+ content = xml.read()
+ with mock_getdata(content, 'GetIndividu'):
+ with mock.patch(
+ 'passerelle.contrib.caluire_axel.models.CaluireAxel.get_family_data',
+ return_value=family_data,
+ ):
+ resp = app.get(
+ '/caluire-axel/test/child_schooling_info?NameID=yyy&idpersonne=50632&booking_date=2021-05-10'
+ )
+ assert resp.json['err'] == 0
+ assert set(resp.json['data'].keys()) == set(['CODE', 'INDIVIDU', 'SCOLAIRE'])
diff --git a/tests/test_caluire_axel_utils.py b/tests/test_caluire_axel_utils.py
new file mode 100644
index 00000000..d4dc5207
--- /dev/null
+++ b/tests/test_caluire_axel_utils.py
@@ -0,0 +1,36 @@
+# passerelle - uniform access to multiple data sources and services
+# Copyright (C) 2021 Entr'ouvert
+#
+# This program is free software: you can redistribute it and/or modify it
+# under the terms of the GNU Affero General Public License as published
+# by the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU Affero General Public License for more details.
+#
+# You should have received a copy of the GNU Affero General Public License
+# along with this program. If not, see .
+
+import datetime
+
+import pytest
+
+from passerelle.contrib.caluire_axel.utils import get_reference_year_from_date
+from passerelle.contrib.utils.axel import json_date_format
+
+
+@pytest.mark.parametrize(
+ 'value, expected',
+ [
+ ('2021-01-01', 2020),
+ ('2021-08-31', 2020),
+ ('2021-09-01', 2021),
+ ('2021-12-31', 2021),
+ ],
+)
+def test_get_reference_year_from_date(value, expected):
+ in_date = datetime.datetime.strptime(value, json_date_format)
+ assert get_reference_year_from_date(in_date) == expected