maelis: add child-activities endpoint (#49616)

This commit is contained in:
Nicolas Roche 2021-01-05 18:45:10 +01:00
parent ece9f1e17a
commit 7098b10e0b
5 changed files with 3097 additions and 20 deletions

View File

@ -400,6 +400,51 @@ class Maelis(BaseResource):
activities = serialize_object(r)
return {'data': [utils.normalize_activity(a) for a in activities]}
@endpoint(
display_category=_('Activities'),
perm='can_access',
display_order=2,
description=_('Get child activities'),
name='child-activities',
parameters={
'NameID': {'description': _('Publik ID')},
'childID': {'description': _('Child ID')},
'subscribingStatus': {
'description': _('subscribed, not-subscribed or None')},
'queryDate': {'description': _('Optional querying date (YYYY-MM-DD)')}
})
def child_activities(self, request, NameID, childID,
subscribingStatus=None, queryDate=None):
if subscribingStatus:
if subscribingStatus != 'subscribed' and subscribingStatus != 'not-subscribed':
raise APIError('wrong value for subscribingStatus: %s' % subscribingStatus)
if queryDate:
try:
start_date = parse_date(queryDate)
except ValueError as exc:
raise APIError('input is well formatted but not a valid date: %s' % exc)
if not start_date:
raise APIError("input isn't well formatted, YYYY-MM-DD expected")
if not queryDate:
start_date = timezone.now().date()
if start_date.strftime('%m-%d') >= '05-01':
# start displaying next year activities on may
school_year = start_date.year
else:
school_year = start_date.year - 1
end_date = utils.get_datetime('%s-07-31' % (school_year + 1))
child_info = self.get_child_info(NameID, childID)
r = self.call('ActivityService?wsdl', 'readActivityList',
schoolyear=school_year,
numPerson=childID,
dateStartCalend=start_date,
dateEndCalend=end_date)
flatted_activities = utils.flatten_activities(serialize_object(r))
utils.mark_subscribed_flatted_activities(flatted_activities, child_info)
data = utils.flatted_activities_as_list(flatted_activities, subscribingStatus)
return {'data': data}
@endpoint(
display_category=_('Activities'),

View File

@ -17,7 +17,7 @@
from __future__ import unicode_literals
from copy import copy
from copy import copy, deepcopy
from datetime import datetime
from dateutil.relativedelta import relativedelta
@ -204,3 +204,74 @@ def decompose_event(event):
date_string, event['category_id'], new_event['slot_id'])
new_event['text'] = component['text']
yield new_event
def flatten_activities(activities):
data = {}
for activity in activities:
if activity.get('openDayList'):
del activity['openDayList']
activity_id = activity['activityPortail']['idAct']
activity_text = activity['activityPortail']['label']
activity_obj = deepcopy(activity)
del activity_obj['unitPortailList']
if not activity_obj['activityPortail']['activityType']:
activity_obj['activityPortail']['activityType'] = {
"code" : "?",
"libelle" : "Inconnu",
"natureSpec" : {
"code" : "?",
"libelle" : "Inconnu"
}
}
units = {}
for unit in activity['unitPortailList']:
unit_id = unit['idUnit']
unit_text = unit['label']
unit_obj = deepcopy(unit)
del unit_obj['placeList']
places = {}
for place in unit['placeList']:
place_id = place['id']
place_text = place['lib']
places[place_text] = {
'id': '%s-%s-%s' % (activity_id, unit_id, place_id),
'text': '%s / %s / %s' % (activity_text, unit_text, place_text),
'activity_id': activity_id,
'unit_id': unit_id,
'place_id': place_id,
'activity_text': activity_text,
'unit_text': unit_text,
'place_text': place_text,
'activity_object': activity_obj,
'unit_object': unit_obj,
'place_object': place,
}
units[unit_id] = places
data[activity_id] = units
return data
def mark_subscribed_flatted_activities(flatted_activities, child_info):
for child_activity in child_info['subscribeActivityList']:
activity = flatted_activities[child_activity['idActivity']]
for child_unit in child_activity['subscribesUnit']:
unit = activity[child_unit['idUnit']]
place = unit[child_activity['place']]
place['user_subscribing_status'] = 'subscribed'
def flatted_activities_as_list(flatted_activities, filtering_status):
data = []
for activity in [a[1] for a in sorted(flatted_activities.items())]:
for unit in [u[1] for u in sorted(activity.items())]:
for place in sorted(unit.values(), key=lambda p: p['place_id']):
status = place.get('user_subscribing_status')
if not status:
status = place['user_subscribing_status'] = 'not-subscribed'
if not filtering_status or status == filtering_status:
data.append(place)
return data

File diff suppressed because it is too large Load Diff

View File

@ -46,6 +46,30 @@ def activity_service_wsdl():
return get_xml_file('ActivityService.wsdl')
@pytest.fixture
def catalog_mocked_get(activity_service_wsdl, family_service_wsdl):
return (
utils.FakedResponse(content=family_service_wsdl,
status_code=200,
headers={'Content-Type': 'text/xml'}),
utils.FakedResponse(content=activity_service_wsdl,
status_code=200,
headers={'Content-Type': 'text/xml'}),
)
@pytest.fixture
def catalog_mocked_post():
return (
utils.FakedResponse(content=get_xml_file('readFamily.xml'),
status_code=200,
headers={'Content-Type': 'text/xml'}),
utils.FakedResponse(content=get_xml_file('readActivityListResponse.xml'),
status_code=200,
headers={'Content-Type': 'text/xml'}),
)
@pytest.fixture
def connector(db):
return utils.setup_access_rights(Maelis.objects.create(
@ -126,24 +150,10 @@ def test_family_info(mocked_post, mocked_get, family_service_wsdl,
@mock.patch('passerelle.utils.Request.get')
@mock.patch('passerelle.utils.Request.post')
def test_activity_list(mocked_post, mocked_get, family_service_wsdl,
activity_service_wsdl, connector, app):
mocked_get.side_effect = (
utils.FakedResponse(content=family_service_wsdl,
status_code=200,
headers={'Content-Type': 'text/xml'}),
utils.FakedResponse(content=activity_service_wsdl,
status_code=200,
headers={'Content-Type': 'text/xml'})
)
mocked_post.side_effect = (
utils.FakedResponse(content=get_xml_file('readFamily.xml'),
status_code=200,
headers={'Content-Type': 'text/xml'}),
utils.FakedResponse(content=get_xml_file('readActivityListResponse.xml'),
status_code=200,
headers={'Content-Type': 'text/xml'})
)
def test_activity_list(mocked_post, mocked_get,
catalog_mocked_get, catalog_mocked_post, connector, app):
mocked_get.side_effect = catalog_mocked_get
mocked_post.side_effect = catalog_mocked_post
Link.objects.create(resource=connector, family_id='3264', name_id='local')
resp = app.get('/maelis/test/activity-list?NameID=local&personID=21293')
assert resp.json['data']
@ -326,3 +336,44 @@ def test_get_child_planning(mocked_post, mocked_get, legacy, nb_events, nb_booke
assert len(data) == nb_events
assert len([s for s in data if s['user_booking_status'] == 'booked']) == nb_booked
assert resp.json == get_json_file(response)
@pytest.mark.parametrize('parameters, nb_subscribed, nb_not_subscribed', [
('&subscribingStatus=', 2, 27),
('&subscribingStatus=subscribed', 2, 0),
('&subscribingStatus=not-subscribed', 0, 27),
])
@mock.patch('passerelle.utils.Request.get')
@mock.patch('passerelle.utils.Request.post')
def test_child_activities(mocked_post, mocked_get, parameters, nb_subscribed, nb_not_subscribed,
catalog_mocked_get, catalog_mocked_post, connector, app):
mocked_get.side_effect = catalog_mocked_get
mocked_post.side_effect = catalog_mocked_post
Link.objects.create(resource=connector, family_id='3264', name_id='local')
url = '/maelis/test/child-activities?NameID=local&childID=21293'
url += parameters
resp = app.get(url)
if parameters == '&subscribingStatus=':
assert resp.json == get_json_file('child_activities')
status = [x['user_subscribing_status'] for x in resp.json['data']]
assert len([x for x in status if x == 'subscribed']) == nb_subscribed
assert len([x for x in status if x == 'not-subscribed']) == nb_not_subscribed
@pytest.mark.parametrize('parameters, err_desc', [
('&childID=99999', 'Child not found'),
('&childID=21293&subscribingStatus=not-a-status', 'wrong value for subscribingStatus'),
("&childID=21293&queryDate=2020-02-31", 'not a valid date'),
("&childID=21293&queryDate=not-a-date", 'YYYY-MM-DD expected'),
])
@mock.patch('passerelle.utils.Request.get')
@mock.patch('passerelle.utils.Request.post')
def test_child_activities_errors(mocked_post, mocked_get, parameters, err_desc,
catalog_mocked_get, catalog_mocked_post, connector, app):
mocked_get.side_effect = catalog_mocked_get
mocked_post.side_effect = catalog_mocked_post
Link.objects.create(resource=connector, family_id='3264', name_id='local')
url = '/maelis/test/child-activities?NameID=local'
url += parameters
resp = app.get(url)
assert resp.json['err']
assert err_desc in resp.json['err_desc']

View File

@ -26,7 +26,7 @@ deps =
pylint<1.8
pylint-django<0.8.1
django-webtest<1.9.3
feedparser<6
feedparser
lxml
mohawk
pytest-freezegun