0001-toulouse_axel-bool-mapping-38464.patch
passerelle/contrib/toulouse_axel/models.py | ||
---|---|---|
43 | 43 |
class AxelSchema(JSONSchemaFromXMLSchema): |
44 | 44 |
type_map = { |
45 | 45 |
'{urn:AllAxelTypes}DATEREQUIREDType': 'date', |
46 |
'{urn:AllAxelTypes}DATEType': 'date', |
|
46 |
'{urn:AllAxelTypes}DATEType': 'date_optional', |
|
47 |
'{urn:AllAxelTypes}OUINONREQUIREDType': 'bool', |
|
48 |
'{urn:AllAxelTypes}OUINONType': 'bool_optional', |
|
47 | 49 |
} |
48 | 50 | |
49 | 51 |
@classmethod |
... | ... | |
56 | 58 |
def encode_date(self, obj): |
57 | 59 |
return datetime.datetime.strptime(obj, '%Y-%m-%d').strftime('%d/%m/%Y') |
58 | 60 | |
61 |
def encode_date_optional(self, obj): |
|
62 |
return self.encode_date(obj) |
|
63 | ||
59 | 64 |
def decode_date(self, data): |
65 |
value = datetime.datetime.strptime(data.text, '%d/%m/%Y').strftime('%Y-%m-%d') |
|
66 |
return xmlschema.ElementData(tag=data.tag, text=value, content=data.content, attributes=data.attributes) |
|
67 | ||
68 |
def decode_date_optional(self, data): |
|
60 | 69 |
if not data.text: |
61 | 70 |
return data |
62 |
value = datetime.datetime.strptime(data.text, '%d/%m/%Y').strftime('%Y-%m-%d') |
|
71 |
return self.decode_date(data) |
|
72 | ||
73 |
@classmethod |
|
74 |
def schema_bool(cls): |
|
75 |
return { |
|
76 |
'oneOf': [ |
|
77 |
{'type': 'boolean'}, |
|
78 |
{ |
|
79 |
'type': 'string', |
|
80 |
'pattern': '(?i)(oui|non|true|false|1|0)', |
|
81 |
} |
|
82 |
] |
|
83 |
} |
|
84 | ||
85 |
def encode_bool(self, obj): |
|
86 |
if obj is True or str(obj).lower() in ['true', 'oui', '1']: |
|
87 |
return 'OUI' |
|
88 |
if obj is False or str(obj).lower() in ['false', 'non', '0']: |
|
89 |
return 'NON' |
|
90 |
return '' |
|
91 | ||
92 |
def decode_bool(self, data): |
|
93 |
value = False |
|
94 |
if data.text.lower() == 'oui': |
|
95 |
value = True |
|
63 | 96 |
return xmlschema.ElementData(tag=data.tag, text=value, content=data.content, attributes=data.attributes) |
64 | 97 | |
98 |
@classmethod |
|
99 |
def schema_bool_optional(cls): |
|
100 |
schema_bool_optional = cls.schema_bool() |
|
101 |
schema_bool_optional['oneOf'].append({'type': 'string', 'enum': ['']}) |
|
102 |
return schema_bool_optional |
|
103 | ||
104 |
def encode_bool_optional(self, obj): |
|
105 |
return self.encode_bool(obj) |
|
106 | ||
107 |
def decode_bool_optional(self, data): |
|
108 |
if not data.text: |
|
109 |
return data |
|
110 |
return self.decode_bool(data) |
|
111 | ||
65 | 112 | |
66 | 113 |
class AxelError(Exception): |
67 | 114 |
pass |
tests/test_toulouse_axel_schema.py | ||
---|---|---|
1 |
# passerelle - uniform access to multiple data sources and services |
|
2 |
# Copyright (C) 2019 Entr'ouvert |
|
3 |
# |
|
4 |
# This program is free software: you can redistribute it and/or modify it |
|
5 |
# under the terms of the GNU Affero General Public License as published |
|
6 |
# by the Free Software Foundation, either version 3 of the License, or |
|
7 |
# (at your option) any later version. |
|
8 |
# |
|
9 |
# This program is distributed in the hope that it will be useful, |
|
10 |
# but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
11 |
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
12 |
# GNU Affero General Public License for more details. |
|
13 |
# |
|
14 |
# You should have received a copy of the GNU Affero General Public License |
|
15 |
# along with this program. If not, see <http://www.gnu.org/licenses/>. |
|
16 | ||
17 |
import os |
|
18 |
import pytest |
|
19 | ||
20 |
from passerelle.contrib.toulouse_axel.models import AxelSchema |
|
21 | ||
22 |
XSD_BASE_DIR = os.path.join( |
|
23 |
os.path.dirname(os.path.abspath(__file__)), |
|
24 |
'../passerelle/contrib/toulouse_axel/xsd') |
|
25 | ||
26 | ||
27 |
@pytest.mark.parametrize('date_type', ['DATEREQUIREDType', 'DATEType']) |
|
28 |
def test_date_mapping(date_type): |
|
29 |
xsd = """<?xml version="1.0" encoding="utf-8" ?> |
|
30 |
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:all="urn:AllAxelTypes"> |
|
31 |
<xsd:import schemaLocation="{path}/AllAxelTypes.xsd" namespace="urn:AllAxelTypes" /> |
|
32 |
<xsd:complexType name="PORTAILType"> |
|
33 |
<xsd:sequence> |
|
34 |
<xsd:element name="DATE" type="all:{date_type}"/> |
|
35 |
</xsd:sequence> |
|
36 |
</xsd:complexType> |
|
37 |
<xsd:element name="PORTAIL" type="PORTAILType"/> |
|
38 |
</xsd:schema>""".format(path=XSD_BASE_DIR, date_type=date_type) |
|
39 | ||
40 |
schema = AxelSchema(xsd, 'PORTAIL') |
|
41 |
xml_data = schema.encode({'PORTAIL': {'DATE': '2019-12-12'}}) |
|
42 |
assert xml_data.find('DATE').text == '12/12/2019' |
|
43 | ||
44 |
json_data = schema.decode(xml_data) |
|
45 |
assert json_data['DATE'] == '2019-12-12' |
|
46 | ||
47 | ||
48 |
@pytest.mark.parametrize('bool_type', ['OUINONREQUIREDType', 'OUINONType']) |
|
49 |
@pytest.mark.parametrize('value, expected, py_expected', [ |
|
50 |
('OUI', 'OUI', True), |
|
51 |
('oui', 'OUI', True), |
|
52 |
('Oui', 'OUI', True), |
|
53 |
('TRUE', 'OUI', True), |
|
54 |
('true', 'OUI', True), |
|
55 |
('True', 'OUI', True), |
|
56 |
(True, 'OUI', True), |
|
57 |
('1', 'OUI', True), |
|
58 |
('NON', 'NON', False), |
|
59 |
('non', 'NON', False), |
|
60 |
('Non', 'NON', False), |
|
61 |
('FALSE', 'NON', False), |
|
62 |
('false', 'NON', False), |
|
63 |
('False', 'NON', False), |
|
64 |
(False, 'NON', False), |
|
65 |
('0', 'NON', False), |
|
66 |
('FOOBAR', '', None), |
|
67 |
('42', '', None), |
|
68 |
]) |
|
69 |
def test_bool_mapping(bool_type, value, expected, py_expected): |
|
70 |
if expected == '' and bool_type == 'OUINONREQUIREDType': |
|
71 |
# required, can't be empty |
|
72 |
return |
|
73 | ||
74 |
xsd = """<?xml version="1.0" encoding="utf-8" ?> |
|
75 |
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:all="urn:AllAxelTypes"> |
|
76 |
<xsd:import schemaLocation="{path}/AllAxelTypes.xsd" namespace="urn:AllAxelTypes" /> |
|
77 |
<xsd:complexType name="PORTAILType"> |
|
78 |
<xsd:sequence> |
|
79 |
<xsd:element name="BOOL" type="all:{bool_type}"/> |
|
80 |
</xsd:sequence> |
|
81 |
</xsd:complexType> |
|
82 |
<xsd:element name="PORTAIL" type="PORTAILType"/> |
|
83 |
</xsd:schema>""".format(path=XSD_BASE_DIR, bool_type=bool_type) |
|
84 | ||
85 |
schema = AxelSchema(xsd, 'PORTAIL') |
|
86 |
xml_data = schema.encode({'PORTAIL': {'BOOL': value}}) |
|
87 |
assert xml_data.find('BOOL').text == expected |
|
88 | ||
89 |
json_data = schema.decode(xml_data) |
|
90 |
assert json_data['BOOL'] is py_expected |
|
0 |
- |