0002-toulouse_axel-endpoint-for-booking-40118.patch
passerelle/contrib/toulouse_axel/models.py | ||
---|---|---|
213 | 213 |
list_dui_factures = Operation('ListeDuiFacturesPayeesRecettees', request_root_element='LISTFACTURE') |
214 | 214 |
enfants_activites = Operation('EnfantsActivites', request_root_element='DUI') |
215 | 215 |
reservation_periode = Operation('ReservationPeriode') |
216 |
reservation_annuelle = Operation('ReservationAnnuelle') |
|
216 | 217 | |
217 | 218 | |
218 | 219 |
class ToulouseAxel(BaseResource): |
... | ... | |
330 | 331 | |
331 | 332 |
UPDATE_FAMILY_SCHEMA['unflatten'] = True |
332 | 333 | |
334 |
BOOKING_SCHEMA = copy.deepcopy( |
|
335 |
reservation_annuelle.request_schema['properties']['PORTAIL']['properties']['DUI']) |
|
336 |
BOOKING_SCHEMA['properties'].pop('IDDUI') |
|
337 |
BOOKING_SCHEMA['properties'].pop('DATEDEMANDE') |
|
338 |
BOOKING_SCHEMA['required'].remove('IDDUI') |
|
339 |
BOOKING_SCHEMA['required'].remove('DATEDEMANDE') |
|
340 |
activite_properties = BOOKING_SCHEMA['properties']['ENFANT']['items']['properties']['ACTIVITE']['items']['properties'] |
|
341 |
activite_properties.pop('ANNEEREFERENCE') |
|
342 |
BOOKING_SCHEMA['properties']['ENFANT']['items']['properties']['ACTIVITE']['items']['required'].remove('ANNEEREFERENCE') |
|
343 |
activite_properties['PERIODE']['items']['properties'].pop('SEMAINETYPE') |
|
344 |
activite_properties['PERIODE']['items']['required'].remove('SEMAINETYPE') |
|
345 |
for day in ['monday', 'tuesday', 'wednesday', 'thursday', 'friday']: |
|
346 |
bool_type = copy.deepcopy(utils.boolean_type) |
|
347 |
activite_properties['PERIODE']['items']['properties'][day] = bool_type |
|
348 |
activite_properties['PERIODE']['items']['required'].append(day) |
|
349 |
BOOKING_SCHEMA['unflatten'] = True |
|
350 | ||
333 | 351 |
@endpoint( |
334 | 352 |
description=_('Lock a resource'), |
335 | 353 |
perm='can_access', |
... | ... | |
1093 | 1111 | |
1094 | 1112 |
return {'data': child_activities} |
1095 | 1113 | |
1114 |
@endpoint( |
|
1115 |
description=_("CLAE/Cantine booking"), |
|
1116 |
perm='can_access', |
|
1117 |
parameters={ |
|
1118 |
'NameID': {'description': _('Publik ID')}, |
|
1119 |
}, |
|
1120 |
post={ |
|
1121 |
'request_body': { |
|
1122 |
'schema': { |
|
1123 |
'application/json': BOOKING_SCHEMA, |
|
1124 |
} |
|
1125 |
} |
|
1126 |
}) |
|
1127 |
def clae_booking(self, request, NameID, post_data): |
|
1128 |
link = self.get_link(NameID) |
|
1129 | ||
1130 |
# prepare data |
|
1131 |
post_data['IDDUI'] = link.dui |
|
1132 |
post_data['DATEDEMANDE'] = datetime.date.today().strftime('%Y-%m-%d') |
|
1133 |
# compute reference years for each activity of each child |
|
1134 |
for child in post_data['ENFANT']: |
|
1135 |
# at least one child (cf definition) |
|
1136 |
for activity in child.get('ACTIVITE', []): |
|
1137 |
# at least one periode (cf definition) |
|
1138 |
start_date = datetime.datetime.strptime( |
|
1139 |
activity['PERIODE'][0]['DATEDEBUT'], |
|
1140 |
utils.json_date_format) |
|
1141 |
reference_year = utils.get_reference_year_from_date(start_date) |
|
1142 |
activity['ANNEEREFERENCE'] = str(reference_year) |
|
1143 |
for periode in activity['PERIODE']: |
|
1144 |
periode['SEMAINETYPE'] = '{}{}{}{}{}'.format( |
|
1145 |
'1' if utils.encode_bool(periode['monday']) == 'OUI' else 0, |
|
1146 |
'1' if utils.encode_bool(periode['tuesday']) == 'OUI' else 0, |
|
1147 |
'1' if utils.encode_bool(periode['wednesday']) == 'OUI' else 0, |
|
1148 |
'1' if utils.encode_bool(periode['thursday']) == 'OUI' else 0, |
|
1149 |
'1' if utils.encode_bool(periode['friday']) == 'OUI' else 0) |
|
1150 |
for day in ['monday', 'tuesday', 'wednesday', 'thursday', 'friday']: |
|
1151 |
periode.pop(day) |
|
1152 | ||
1153 |
try: |
|
1154 |
result = reservation_annuelle(self, {'PORTAIL': {'DUI': post_data}}) |
|
1155 |
except AxelError as e: |
|
1156 |
raise APIError( |
|
1157 |
'Axel error: %s' % e, |
|
1158 |
err_code='error', |
|
1159 |
data={'xml_request': e.xml_request, |
|
1160 |
'xml_response': e.xml_response}) |
|
1161 | ||
1162 |
return { |
|
1163 |
'updated': True, |
|
1164 |
'data': { |
|
1165 |
'xml_request': result.xml_request, |
|
1166 |
'xml_response': result.xml_response, |
|
1167 |
} |
|
1168 |
} |
|
1169 | ||
1096 | 1170 | |
1097 | 1171 |
class Link(models.Model): |
1098 | 1172 |
resource = models.ForeignKey(ToulouseAxel, on_delete=models.CASCADE) |
passerelle/contrib/toulouse_axel/xsd/Dui/Q_ReservationAnnuelle.xsd | ||
---|---|---|
1 |
<?xml version="1.0" encoding="utf-8" ?> |
|
2 |
<xsd:schema xmlns:all="urn:AllAxelTypes" xmlns:xsd="http://www.w3.org/2001/XMLSchema" > |
|
3 |
|
|
4 |
<xsd:import schemaLocation="../AllAxelTypes.xsd" namespace="urn:AllAxelTypes" /> |
|
5 |
|
|
6 |
<xsd:simpleType name="semainetype"> |
|
7 |
<xsd:restriction base="xsd:string"> |
|
8 |
<xsd:pattern value="[0-1][0-1][0-1][0-1][0-1]"/> |
|
9 |
</xsd:restriction> |
|
10 |
</xsd:simpleType> |
|
11 |
|
|
12 |
<xsd:complexType name="periode"> |
|
13 |
<xsd:sequence> |
|
14 |
<xsd:element ref="SEMAINETYPE" /> |
|
15 |
<xsd:element ref="DATEDEBUT" /> |
|
16 |
<xsd:element ref="DATEDFIN" /> |
|
17 |
</xsd:sequence> |
|
18 |
</xsd:complexType> |
|
19 |
|
|
20 |
<xsd:complexType name="activite"> |
|
21 |
<xsd:sequence> |
|
22 |
<xsd:element ref="ANNEEREFERENCE" /> |
|
23 |
<xsd:element ref="IDACTIVITE" /> |
|
24 |
<xsd:element ref="PERIODE" minOccurs="1" maxOccurs="unbounded"/> |
|
25 |
</xsd:sequence> |
|
26 |
</xsd:complexType> |
|
27 |
|
|
28 |
<xsd:complexType name="ENFANTType"> |
|
29 |
<xsd:sequence> |
|
30 |
<xsd:element ref="IDPERSONNE" /> |
|
31 |
<xsd:element ref="REGIME" /> |
|
32 |
<xsd:element ref="ACTIVITE" minOccurs="0" maxOccurs="unbounded"/> |
|
33 |
</xsd:sequence> |
|
34 |
</xsd:complexType> |
|
35 |
|
|
36 |
<xsd:complexType name="DUIType"> |
|
37 |
<xsd:sequence> |
|
38 |
<xsd:element ref="IDDUI" /> |
|
39 |
<xsd:element ref="DATEDEMANDE" /> |
|
40 |
<xsd:element ref="ENFANT" minOccurs="1" maxOccurs="unbounded"/> |
|
41 |
</xsd:sequence> |
|
42 |
</xsd:complexType> |
|
43 |
|
|
44 |
<xsd:complexType name="PORTAILType"> |
|
45 |
<xsd:sequence> |
|
46 |
<xsd:element ref="DUI"/> |
|
47 |
</xsd:sequence> |
|
48 |
</xsd:complexType> |
|
49 |
|
|
50 |
<xsd:simpleType name="regime"> |
|
51 |
<xsd:restriction base="xsd:string"> |
|
52 |
<xsd:maxLength value="4" /> |
|
53 |
</xsd:restriction> |
|
54 |
</xsd:simpleType> |
|
55 |
|
|
56 |
<xsd:element name="IDDUI" type="all:IDENTREQUIREDType"/> |
|
57 |
<xsd:element name="DATEDEMANDE" type="all:DATEREQUIREDType"/> |
|
58 |
<xsd:element name="ENFANT" type="ENFANTType"/> |
|
59 |
|
|
60 |
<xsd:element name="IDPERSONNE" type="all:IDENTREQUIREDType"/> |
|
61 |
<xsd:element name="REGIME" type="regime" /> |
|
62 |
|
|
63 |
<xsd:element name="ANNEEREFERENCE" type="all:ANNEEType"/> |
|
64 |
<xsd:element name="IDACTIVITE" type="all:IDREQUIREDType"/> |
|
65 |
<xsd:element name="SEMAINETYPE" type="semainetype"/> |
|
66 |
<xsd:element name="DATEDEBUT" type="all:DATEREQUIREDType"/> |
|
67 |
<xsd:element name="DATEDFIN" type="all:DATEREQUIREDType"/> |
|
68 |
|
|
69 |
<xsd:element name="ACTIVITE" type="activite"/> |
|
70 |
<xsd:element name="PERIODE" type="periode"/> |
|
71 |
|
|
72 |
<xsd:element name="DUI" type="DUIType"/> |
|
73 |
|
|
74 |
<xsd:element name="PORTAIL" type="PORTAILType"/> |
|
75 |
</xsd:schema> |
passerelle/contrib/toulouse_axel/xsd/Dui/R_ReservationAnnuelle.xsd | ||
---|---|---|
1 |
<?xml version="1.0" encoding="utf-8" ?> |
|
2 |
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema"> |
|
3 |
|
|
4 |
<xsd:redefine schemaLocation="../R_ShemaResultat.xsd"> |
|
5 |
<xsd:simpleType name="TYPEType"> |
|
6 |
<xsd:restriction base="TYPEType"> |
|
7 |
<xsd:enumeration value="ReservationAnnuelle" /> |
|
8 |
</xsd:restriction> |
|
9 |
</xsd:simpleType> |
|
10 |
|
|
11 |
<xsd:complexType name="PORTAILType"> |
|
12 |
<xsd:complexContent> |
|
13 |
<xsd:extension base="PORTAILType"> |
|
14 |
<xsd:sequence> |
|
15 |
<xsd:element name="PORTAIL" type="xsd:string" minOccurs="0"/> |
|
16 |
</xsd:sequence> |
|
17 |
</xsd:extension> |
|
18 |
</xsd:complexContent> |
|
19 |
</xsd:complexType> |
|
20 |
</xsd:redefine> |
|
21 |
</xsd:schema> |
tests/test_toulouse_axel.py | ||
---|---|---|
136 | 136 |
return json.loads(content) |
137 | 137 | |
138 | 138 | |
139 |
@pytest.fixture |
|
140 |
def booking_params(): |
|
141 |
return { |
|
142 |
"ENFANT": [ |
|
143 |
{ |
|
144 |
"IDPERSONNE": "4242", |
|
145 |
"REGIME": "", |
|
146 |
"ACTIVITE": [ |
|
147 |
{ |
|
148 |
"IDACTIVITE": "A19P1M1", |
|
149 |
"PERIODE": [ |
|
150 |
{ |
|
151 |
"DATEDEBUT": "2020-01-01", |
|
152 |
"DATEDFIN": "2020-07-31", |
|
153 |
"monday": "1", |
|
154 |
"tuesday": "1", |
|
155 |
"wednesday": "0", |
|
156 |
"thursday": "1", |
|
157 |
"friday": "1" |
|
158 |
} |
|
159 |
] |
|
160 |
} |
|
161 |
] |
|
162 |
} |
|
163 |
] |
|
164 |
} |
|
165 | ||
166 | ||
139 | 167 |
def test_lock(app, resource): |
140 | 168 |
resp = app.get('/toulouse-axel/test/lock?key=&locker=', status=400) |
141 | 169 |
assert resp.json['err_desc'] == "key is empty" |
... | ... | |
2176 | 2204 |
with mock.patch('passerelle.contrib.toulouse_axel.models.ToulouseAxel.get_children_activities') as get_children_activities: |
2177 | 2205 |
get_children_activities.return_value = activities |
2178 | 2206 |
assert resource.are_children_registered(dui='XXX', reference_year=get_reference_year_from_date(today)) == expected |
2207 | ||
2208 | ||
2209 |
def test_clae_booking_endpoint_axel_error(app, resource, booking_params): |
|
2210 |
Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42') |
|
2211 |
with mock.patch('passerelle.contrib.toulouse_axel.models.reservation_annuelle') as operation: |
|
2212 |
operation.side_effect = AxelError('FooBar') |
|
2213 |
resp = app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=booking_params) |
|
2214 |
assert resp.json['err_desc'] == "Axel error: FooBar" |
|
2215 |
assert resp.json['err'] == 'error' |
|
2216 | ||
2217 | ||
2218 |
def test_clae_booking_endpoint_no_result(app, resource, booking_params): |
|
2219 |
resp = app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=booking_params) |
|
2220 |
assert resp.json['err_desc'] == "Person not found" |
|
2221 |
assert resp.json['err'] == 'not-found' |
|
2222 | ||
2223 | ||
2224 |
def test_clae_booking_endpoint(app, resource, booking_params): |
|
2225 |
Link.objects.create(resource=resource, name_id='yyy', dui='XXX', person_id='42') |
|
2226 |
content = "<PORTAIL/>" |
|
2227 |
with mock_getdata(content, 'ReservationAnnuelle'): |
|
2228 |
resp = app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=booking_params) |
|
2229 |
assert resp.json['err'] == 0 |
|
2230 |
assert resp.json['updated'] is True |
|
2231 |
assert 'data' in resp.json |
|
2232 |
assert 'xml_request' in resp.json['data'] |
|
2233 |
assert 'xml_response' in resp.json['data'] |
|
2234 | ||
2235 |
with mock.patch('passerelle.contrib.toulouse_axel.models.reservation_annuelle') as operation: |
|
2236 |
operation.return_value = OperationResult(json_response={}, xml_request='', xml_response='') |
|
2237 |
resp = app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=booking_params) |
|
2238 |
payload = operation.call_args_list[0][0][1]['PORTAIL']['DUI'] |
|
2239 |
assert payload['ENFANT'][0]['ACTIVITE'][0]['PERIODE'][0]['SEMAINETYPE'] == '11011' |
|
2240 | ||
2241 |
new_booking_params = copy.deepcopy(booking_params) |
|
2242 |
new_booking_params['ENFANT'][0]['ACTIVITE'][0]['PERIODE'][0]['monday'] = '0' |
|
2243 |
with mock.patch('passerelle.contrib.toulouse_axel.models.reservation_annuelle') as operation: |
|
2244 |
operation.return_value = OperationResult(json_response={}, xml_request='', xml_response='') |
|
2245 |
resp = app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=new_booking_params) |
|
2246 |
payload = operation.call_args_list[0][0][1]['PORTAIL']['DUI'] |
|
2247 |
assert payload['ENFANT'][0]['ACTIVITE'][0]['PERIODE'][0]['SEMAINETYPE'] == '01011' |
|
2248 | ||
2249 |
new_booking_params['ENFANT'][0]['ACTIVITE'][0]['PERIODE'][0]['monday'] = 'OUI' |
|
2250 |
with mock.patch('passerelle.contrib.toulouse_axel.models.reservation_annuelle') as operation: |
|
2251 |
operation.return_value = OperationResult(json_response={}, xml_request='', xml_response='') |
|
2252 |
resp = app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=new_booking_params) |
|
2253 |
payload = operation.call_args_list[0][0][1]['PORTAIL']['DUI'] |
|
2254 |
assert payload['ENFANT'][0]['ACTIVITE'][0]['PERIODE'][0]['SEMAINETYPE'] == '11011' |
|
2255 | ||
2256 |
new_booking_params['ENFANT'][0]['ACTIVITE'][0]['PERIODE'][0]['monday'] = False |
|
2257 |
with mock.patch('passerelle.contrib.toulouse_axel.models.reservation_annuelle') as operation: |
|
2258 |
operation.return_value = OperationResult(json_response={}, xml_request='', xml_response='') |
|
2259 |
resp = app.post_json('/toulouse-axel/test/clae_booking?NameID=yyy', params=new_booking_params) |
|
2260 |
payload = operation.call_args_list[0][0][1]['PORTAIL']['DUI'] |
|
2261 |
assert payload['ENFANT'][0]['ACTIVITE'][0]['PERIODE'][0]['SEMAINETYPE'] == '01011' |
|
2179 |
- |