maelis: decompose units related to moments of the day (#48480)

This commit is contained in:
Nicolas Roche 2020-12-23 17:11:56 +01:00
parent b6fa710847
commit f9885e2fab
4 changed files with 485 additions and 9 deletions

View File

@ -409,8 +409,9 @@ class Maelis(BaseResource):
'childID': {'description': _('Child ID')},
'start_date': {'description': _('Start date (YYYY-MM-DD format)')},
'end_date': {'description': _('End date (YYYY-MM-DD format)')},
'legacy': {'description': _('Decompose events related to parts of the day if set')},
})
def child_planning(self, request, NameID, childID, start_date=None, end_date=None):
def child_planning(self, request, NameID, childID, start_date=None, end_date=None, legacy=None):
""" Return an events list sorted by id """
link = self.get_link(NameID)
family_data = self.get_family_data(link.family_id)
@ -441,6 +442,10 @@ class Maelis(BaseResource):
for schedule in planning:
utils.book_event(events, schedule, start, end)
if not legacy:
events = {x['id']: x # dictionary is used de remove dupplicated events
for e in events.values()
for x in utils.decompose_event(e)}
return {'data': [s[1] for s in sorted(events.items())]}

View File

@ -17,6 +17,7 @@
from __future__ import unicode_literals
from copy import copy
from datetime import datetime
from dateutil.relativedelta import relativedelta
@ -29,6 +30,40 @@ DATE_FORMAT = '%Y-%m-%d'
TIME_FORMAT = '%H:%M:%S'
DATETIME_FORMAT = DATE_FORMAT + ' ' + TIME_FORMAT
COMPONENTS = {
'PART01': {
'text': 'Matinée',
'time': '08:00:00',
},
'PART02': {
'text': 'Repas',
'time': '12:00:00',
},
'PART03': {
'text': 'Après-midi',
'time': '14:00:00',
}
}
COMPOSED_UNITS = {
'A10003132034': { # JOURNEE
'virtual_unit': 'EO0001',
'components': ['PART01', 'PART02', 'PART03'],
},
'A10003132036': { # MATIN
'virtual_unit': 'EO0001',
'components': ['PART01'],
},
'A10003132038': { # MATIN ET REPAS
'virtual_unit': 'EO0001',
'components': ['PART01', 'PART02'],
},
'A10003132040': { # APRES MIDI
'virtual_unit': 'EO0001',
'components': ['PART03'],
},
}
def normalize_invoice(invoice):
data = {
@ -147,3 +182,25 @@ def book_event(events, schedule, start_date, end_date):
% day['datePlanning'])
event['user_booking_status'] = 'booked'
def decompose_event(event):
""" Break down 'JOURNEE', 'MATIN', 'MATIN ET REPAS' and APRES MIDI' units
into 'Matin', 'Repas' and 'Après-midi' virtual units. """
if event['slot_id'] not in COMPOSED_UNITS.keys():
yield event
return
date_time = datetime.strptime(event['datetime'], DATETIME_FORMAT)
date_string = date_time.strftime(DATE_FORMAT)
composition = COMPOSED_UNITS[event['slot_id']]
for component_id in composition['components']:
component = COMPONENTS[component_id]
new_event = copy(event)
new_event['datetime'] = '%s %s' % (date_string, component['time'])
new_event['slot_id'] = "%s%s" % (composition['virtual_unit'], component_id)
new_event['id'] = '%s-%s-%s' % (
date_string, event['category_id'], new_event['slot_id'])
new_event['text'] = component['text']
yield new_event

View File

@ -0,0 +1,383 @@
{
"data" : [
{
"category" : "MULTI ACCUEIL LES OLIVIERS",
"category_id" : "A10000003840",
"datetime" : "2020-12-14 00:00:00",
"id" : "2020-12-14-A10000003840-A10000003844",
"slot_id" : "A10000003844",
"text" : "MULTI ACCUEIL LES OLIVIERS - Réguliers",
"user_booking_status" : "not-booked"
},
{
"category" : "MULTI ACCUEIL LES OLIVIERS",
"category_id" : "A10000003840",
"datetime" : "2020-12-14 00:00:00",
"id" : "2020-12-14-A10000003840-A10000003845",
"slot_id" : "A10000003845",
"text" : "MULTI ACCUEIL LES OLIVIERS - Occasionnels",
"user_booking_status" : "not-booked"
},
{
"category" : "HALTE GARDERIE LES MAGNOLIAS",
"category_id" : "A10000006100",
"datetime" : "2020-12-14 00:00:00",
"id" : "2020-12-14-A10000006100-A10000006104",
"slot_id" : "A10000006104",
"text" : "HALTE GARDERIE LES MAGNOLIAS - Réguliers",
"user_booking_status" : "not-booked"
},
{
"category" : "HALTE GARDERIE LES MAGNOLIAS",
"category_id" : "A10000006100",
"datetime" : "2020-12-14 00:00:00",
"id" : "2020-12-14-A10000006100-A10000006105",
"slot_id" : "A10000006105",
"text" : "HALTE GARDERIE LES MAGNOLIAS - Occasionnels",
"user_booking_status" : "not-booked"
},
{
"category" : "1 2020-2021 GARDERIE MATIN",
"category_id" : "A10003121692",
"datetime" : "2020-12-14 00:00:00",
"id" : "2020-12-14-A10003121692-A10003121694",
"slot_id" : "A10003121694",
"text" : "1 2020-2021 GARDERIE MATIN",
"user_booking_status" : "booked"
},
{
"category" : "2 2020-2021 RESTAURATION SCOLAIRE",
"category_id" : "A10003123490",
"datetime" : "2020-12-14 00:00:00",
"id" : "2020-12-14-A10003123490-A10003123492",
"slot_id" : "A10003123492",
"text" : "2 2020-2021 RESTAURATION SCOLAIRE",
"user_booking_status" : "booked"
},
{
"category" : "3 2020-2021 GARDERIE SOIR",
"category_id" : "A10003123507",
"datetime" : "2020-12-14 00:00:00",
"id" : "2020-12-14-A10003123507-A10003123509",
"slot_id" : "A10003123509",
"text" : "3 2020-2021 GARDERIE SOIR",
"user_booking_status" : "not-booked"
},
{
"category" : "COURS DE DANSE",
"category_id" : "A10003132090",
"datetime" : "2020-12-14 00:00:00",
"id" : "2020-12-14-A10003132090-A10003132091",
"slot_id" : "A10003132091",
"text" : "COURS DE DANSE",
"user_booking_status" : "not-booked"
},
{
"category" : "ATELIER D'EXPRESSION THEATRALE",
"category_id" : "A10003132293",
"datetime" : "2020-12-14 00:00:00",
"id" : "2020-12-14-A10003132293-A10003132295",
"slot_id" : "A10003132295",
"text" : "ATELIER D'EXPRESSION THEATRALE",
"user_booking_status" : "not-booked"
},
{
"category" : "MULTI ACCUEIL LES OLIVIERS",
"category_id" : "A10000003840",
"datetime" : "2020-12-15 00:00:00",
"id" : "2020-12-15-A10000003840-A10000003844",
"slot_id" : "A10000003844",
"text" : "MULTI ACCUEIL LES OLIVIERS - Réguliers",
"user_booking_status" : "not-booked"
},
{
"category" : "MULTI ACCUEIL LES OLIVIERS",
"category_id" : "A10000003840",
"datetime" : "2020-12-15 00:00:00",
"id" : "2020-12-15-A10000003840-A10000003845",
"slot_id" : "A10000003845",
"text" : "MULTI ACCUEIL LES OLIVIERS - Occasionnels",
"user_booking_status" : "not-booked"
},
{
"category" : "HALTE GARDERIE LES MAGNOLIAS",
"category_id" : "A10000006100",
"datetime" : "2020-12-15 00:00:00",
"id" : "2020-12-15-A10000006100-A10000006104",
"slot_id" : "A10000006104",
"text" : "HALTE GARDERIE LES MAGNOLIAS - Réguliers",
"user_booking_status" : "not-booked"
},
{
"category" : "HALTE GARDERIE LES MAGNOLIAS",
"category_id" : "A10000006100",
"datetime" : "2020-12-15 00:00:00",
"id" : "2020-12-15-A10000006100-A10000006105",
"slot_id" : "A10000006105",
"text" : "HALTE GARDERIE LES MAGNOLIAS - Occasionnels",
"user_booking_status" : "not-booked"
},
{
"category" : "1 2020-2021 GARDERIE MATIN",
"category_id" : "A10003121692",
"datetime" : "2020-12-15 00:00:00",
"id" : "2020-12-15-A10003121692-A10003121694",
"slot_id" : "A10003121694",
"text" : "1 2020-2021 GARDERIE MATIN",
"user_booking_status" : "booked"
},
{
"category" : "2 2020-2021 RESTAURATION SCOLAIRE",
"category_id" : "A10003123490",
"datetime" : "2020-12-15 00:00:00",
"id" : "2020-12-15-A10003123490-A10003123492",
"slot_id" : "A10003123492",
"text" : "2 2020-2021 RESTAURATION SCOLAIRE",
"user_booking_status" : "booked"
},
{
"category" : "3 2020-2021 GARDERIE SOIR",
"category_id" : "A10003123507",
"datetime" : "2020-12-15 00:00:00",
"id" : "2020-12-15-A10003123507-A10003123509",
"slot_id" : "A10003123509",
"text" : "3 2020-2021 GARDERIE SOIR",
"user_booking_status" : "not-booked"
},
{
"category" : "COURS DE DANSE",
"category_id" : "A10003132090",
"datetime" : "2020-12-15 00:00:00",
"id" : "2020-12-15-A10003132090-A10003132091",
"slot_id" : "A10003132091",
"text" : "COURS DE DANSE",
"user_booking_status" : "not-booked"
},
{
"category" : "MULTI ACCUEIL LES OLIVIERS",
"category_id" : "A10000003840",
"datetime" : "2020-12-16 00:00:00",
"id" : "2020-12-16-A10000003840-A10000003844",
"slot_id" : "A10000003844",
"text" : "MULTI ACCUEIL LES OLIVIERS - Réguliers",
"user_booking_status" : "not-booked"
},
{
"category" : "MULTI ACCUEIL LES OLIVIERS",
"category_id" : "A10000003840",
"datetime" : "2020-12-16 00:00:00",
"id" : "2020-12-16-A10000003840-A10000003845",
"slot_id" : "A10000003845",
"text" : "MULTI ACCUEIL LES OLIVIERS - Occasionnels",
"user_booking_status" : "not-booked"
},
{
"category" : "HALTE GARDERIE LES MAGNOLIAS",
"category_id" : "A10000006100",
"datetime" : "2020-12-16 00:00:00",
"id" : "2020-12-16-A10000006100-A10000006104",
"slot_id" : "A10000006104",
"text" : "HALTE GARDERIE LES MAGNOLIAS - Réguliers",
"user_booking_status" : "not-booked"
},
{
"category" : "HALTE GARDERIE LES MAGNOLIAS",
"category_id" : "A10000006100",
"datetime" : "2020-12-16 00:00:00",
"id" : "2020-12-16-A10000006100-A10000006105",
"slot_id" : "A10000006105",
"text" : "HALTE GARDERIE LES MAGNOLIAS - Occasionnels",
"user_booking_status" : "not-booked"
},
{
"category" : "2020-2021 CENTRE DE LOISIRS MERCREDI",
"category_id" : "A10003132030",
"datetime" : "2020-12-16 00:00:00",
"id" : "2020-12-16-A10003132030-A10003132032",
"slot_id" : "A10003132032",
"text" : "2020-2021 CENTRE DE LOISIRS MERCREDI",
"user_booking_status" : "booked"
},
{
"category" : "2020-2021 CENTRE DE LOISIRS MERCREDI",
"category_id" : "A10003132030",
"datetime" : "2020-12-16 08:00:00",
"id" : "2020-12-16-A10003132030-EO0001PART01",
"slot_id" : "EO0001PART01",
"text" : "Matinée",
"user_booking_status" : "not-booked"
},
{
"category" : "2020-2021 CENTRE DE LOISIRS MERCREDI",
"category_id" : "A10003132030",
"datetime" : "2020-12-16 12:00:00",
"id" : "2020-12-16-A10003132030-EO0001PART02",
"slot_id" : "EO0001PART02",
"text" : "Repas",
"user_booking_status" : "not-booked"
},
{
"category" : "2020-2021 CENTRE DE LOISIRS MERCREDI",
"category_id" : "A10003132030",
"datetime" : "2020-12-16 14:00:00",
"id" : "2020-12-16-A10003132030-EO0001PART03",
"slot_id" : "EO0001PART03",
"text" : "Après-midi",
"user_booking_status" : "not-booked"
},
{
"category" : "COURS DE DANSE",
"category_id" : "A10003132090",
"datetime" : "2020-12-16 00:00:00",
"id" : "2020-12-16-A10003132090-A10003132091",
"slot_id" : "A10003132091",
"text" : "COURS DE DANSE",
"user_booking_status" : "not-booked"
},
{
"category" : "MULTI ACCUEIL LES OLIVIERS",
"category_id" : "A10000003840",
"datetime" : "2020-12-17 00:00:00",
"id" : "2020-12-17-A10000003840-A10000003844",
"slot_id" : "A10000003844",
"text" : "MULTI ACCUEIL LES OLIVIERS - Réguliers",
"user_booking_status" : "not-booked"
},
{
"category" : "MULTI ACCUEIL LES OLIVIERS",
"category_id" : "A10000003840",
"datetime" : "2020-12-17 00:00:00",
"id" : "2020-12-17-A10000003840-A10000003845",
"slot_id" : "A10000003845",
"text" : "MULTI ACCUEIL LES OLIVIERS - Occasionnels",
"user_booking_status" : "not-booked"
},
{
"category" : "HALTE GARDERIE LES MAGNOLIAS",
"category_id" : "A10000006100",
"datetime" : "2020-12-17 00:00:00",
"id" : "2020-12-17-A10000006100-A10000006104",
"slot_id" : "A10000006104",
"text" : "HALTE GARDERIE LES MAGNOLIAS - Réguliers",
"user_booking_status" : "not-booked"
},
{
"category" : "HALTE GARDERIE LES MAGNOLIAS",
"category_id" : "A10000006100",
"datetime" : "2020-12-17 00:00:00",
"id" : "2020-12-17-A10000006100-A10000006105",
"slot_id" : "A10000006105",
"text" : "HALTE GARDERIE LES MAGNOLIAS - Occasionnels",
"user_booking_status" : "not-booked"
},
{
"category" : "1 2020-2021 GARDERIE MATIN",
"category_id" : "A10003121692",
"datetime" : "2020-12-17 00:00:00",
"id" : "2020-12-17-A10003121692-A10003121694",
"slot_id" : "A10003121694",
"text" : "1 2020-2021 GARDERIE MATIN",
"user_booking_status" : "booked"
},
{
"category" : "2 2020-2021 RESTAURATION SCOLAIRE",
"category_id" : "A10003123490",
"datetime" : "2020-12-17 00:00:00",
"id" : "2020-12-17-A10003123490-A10003123492",
"slot_id" : "A10003123492",
"text" : "2 2020-2021 RESTAURATION SCOLAIRE",
"user_booking_status" : "booked"
},
{
"category" : "3 2020-2021 GARDERIE SOIR",
"category_id" : "A10003123507",
"datetime" : "2020-12-17 00:00:00",
"id" : "2020-12-17-A10003123507-A10003123509",
"slot_id" : "A10003123509",
"text" : "3 2020-2021 GARDERIE SOIR",
"user_booking_status" : "not-booked"
},
{
"category" : "COURS DE DANSE",
"category_id" : "A10003132090",
"datetime" : "2020-12-17 00:00:00",
"id" : "2020-12-17-A10003132090-A10003132091",
"slot_id" : "A10003132091",
"text" : "COURS DE DANSE",
"user_booking_status" : "not-booked"
},
{
"category" : "MULTI ACCUEIL LES OLIVIERS",
"category_id" : "A10000003840",
"datetime" : "2020-12-18 00:00:00",
"id" : "2020-12-18-A10000003840-A10000003844",
"slot_id" : "A10000003844",
"text" : "MULTI ACCUEIL LES OLIVIERS - Réguliers",
"user_booking_status" : "not-booked"
},
{
"category" : "MULTI ACCUEIL LES OLIVIERS",
"category_id" : "A10000003840",
"datetime" : "2020-12-18 00:00:00",
"id" : "2020-12-18-A10000003840-A10000003845",
"slot_id" : "A10000003845",
"text" : "MULTI ACCUEIL LES OLIVIERS - Occasionnels",
"user_booking_status" : "not-booked"
},
{
"category" : "HALTE GARDERIE LES MAGNOLIAS",
"category_id" : "A10000006100",
"datetime" : "2020-12-18 00:00:00",
"id" : "2020-12-18-A10000006100-A10000006104",
"slot_id" : "A10000006104",
"text" : "HALTE GARDERIE LES MAGNOLIAS - Réguliers",
"user_booking_status" : "not-booked"
},
{
"category" : "HALTE GARDERIE LES MAGNOLIAS",
"category_id" : "A10000006100",
"datetime" : "2020-12-18 00:00:00",
"id" : "2020-12-18-A10000006100-A10000006105",
"slot_id" : "A10000006105",
"text" : "HALTE GARDERIE LES MAGNOLIAS - Occasionnels",
"user_booking_status" : "not-booked"
},
{
"category" : "1 2020-2021 GARDERIE MATIN",
"category_id" : "A10003121692",
"datetime" : "2020-12-18 00:00:00",
"id" : "2020-12-18-A10003121692-A10003121694",
"slot_id" : "A10003121694",
"text" : "1 2020-2021 GARDERIE MATIN",
"user_booking_status" : "booked"
},
{
"category" : "2 2020-2021 RESTAURATION SCOLAIRE",
"category_id" : "A10003123490",
"datetime" : "2020-12-18 00:00:00",
"id" : "2020-12-18-A10003123490-A10003123492",
"slot_id" : "A10003123492",
"text" : "2 2020-2021 RESTAURATION SCOLAIRE",
"user_booking_status" : "booked"
},
{
"category" : "3 2020-2021 GARDERIE SOIR",
"category_id" : "A10003123507",
"datetime" : "2020-12-18 00:00:00",
"id" : "2020-12-18-A10003123507-A10003123509",
"slot_id" : "A10003123509",
"text" : "3 2020-2021 GARDERIE SOIR",
"user_booking_status" : "not-booked"
},
{
"category" : "COURS DE DANSE",
"category_id" : "A10003132090",
"datetime" : "2020-12-18 00:00:00",
"id" : "2020-12-18-A10003132090-A10003132091",
"slot_id" : "A10003132091",
"text" : "COURS DE DANSE",
"user_booking_status" : "not-booked"
}
],
"err" : 0
}

View File

@ -10,7 +10,7 @@ from django.utils.dateparse import parse_date
from passerelle.apps.maelis.models import Maelis, Link
from passerelle.apps.maelis.utils import (
get_school_year, week_boundaries_datetimes, month_range)
get_school_year, week_boundaries_datetimes, month_range, decompose_event)
from passerelle.utils.jsonresponse import APIError
@ -247,10 +247,41 @@ def test_month_range(start, end, items):
assert [x.strftime('%Y-%m-%d') for x in month_range(start, end)] == items
def test_decompose_event():
resp = get_json_file('child_planning_before_decomposition')
data = resp['data']
assert len(data) == 43
assert data[22]['text'] == 'JOURNEE'
assert data[23]['text'] == 'MATIN'
assert data[24]['text'] == 'MATIN ET REPAS'
assert data[25]['text'] == 'APRES MIDI'
assert not [x for x in data if x['text'] == 'REPAS']
# unit is break down into its components
assert [x['text'] for x in decompose_event(data[22])] == [
'Matinée', 'Repas', 'Après-midi']
assert [x['text'] for x in decompose_event(data[23])] == [
'Matinée']
assert [x['text'] for x in decompose_event(data[24])] == [
'Matinée', 'Repas']
assert [x['text'] for x in decompose_event(data[25])] == [
'Après-midi']
# child_planning function use a dict to remove dupplicated components
data = {x['id']: x for e in data for x in decompose_event(e)}.values()
assert len(data) == 42
assert len([x for x in data if x['text'] == 'Repas']) == 1
assert len([x for x in data if 'EO0001' in x['slot_id']]) == 3
@override_settings(TIME_ZONE='Europe/Paris')
@pytest.mark.parametrize('legacy, nb_events, nb_booked, response', [
('please', 43, 9, 'child_planning_before_decomposition'),
('', 42, 9, 'child_planning_after_decomposition'),
])
@mock.patch('passerelle.utils.Request.get')
@mock.patch('passerelle.utils.Request.post')
def test_get_child_planning(mocked_post, mocked_get,
def test_get_child_planning(mocked_post, mocked_get, legacy, nb_events, nb_booked, response,
family_service_wsdl, activity_service_wsdl, app, connector):
mocked_get.side_effect = (
utils.FakedResponse(
@ -281,8 +312,9 @@ def test_get_child_planning(mocked_post, mocked_get,
headers={'Content-Type': 'text/xml'})
)
Link.objects.create(resource=connector, family_id='3264', name_id='local')
resp = app.get(
'/maelis/test/child-planning?NameID=local&childID=21293&start_date=2020-12-19')
url = '/maelis/test/child-planning?NameID=local&childID=21293&start_date=2020-12-19'
url += '&legacy=' + legacy
resp = app.get(url)
data = resp.json['data']
previous_event = {}
for event in data:
@ -291,7 +323,6 @@ def test_get_child_planning(mocked_post, mocked_get,
if previous_event:
assert previous_event['id'] <= event['id']
previous_event = event
assert len(data) == 43
assert len([s for s in data if s['user_booking_status'] == 'not-booked']) == 34
assert len([s for s in data if s['user_booking_status'] == 'booked']) == 9
assert resp.json == get_json_file('child_planning_before_decomposition')
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)