pysdmx 1.10.1__py3-none-any.whl → 1.11.0__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pysdmx/__init__.py +1 -1
- pysdmx/api/fmr/maintenance.py +10 -5
- pysdmx/io/input_processor.py +4 -0
- pysdmx/io/json/fusion/messages/dsd.py +19 -14
- pysdmx/io/json/fusion/messages/msd.py +6 -9
- pysdmx/io/json/sdmxjson2/messages/core.py +12 -5
- pysdmx/io/json/sdmxjson2/messages/dsd.py +11 -17
- pysdmx/io/json/sdmxjson2/messages/msd.py +2 -5
- pysdmx/io/json/sdmxjson2/messages/report.py +7 -3
- pysdmx/io/json/sdmxjson2/messages/structure.py +7 -3
- pysdmx/io/json/sdmxjson2/reader/metadata.py +3 -3
- pysdmx/io/json/sdmxjson2/reader/structure.py +3 -3
- pysdmx/io/json/sdmxjson2/writer/_helper.py +118 -0
- pysdmx/io/json/sdmxjson2/writer/v2_0/__init__.py +1 -0
- pysdmx/io/json/sdmxjson2/writer/v2_0/metadata.py +33 -0
- pysdmx/io/json/sdmxjson2/writer/v2_0/structure.py +33 -0
- pysdmx/io/json/sdmxjson2/writer/v2_1/__init__.py +1 -0
- pysdmx/io/json/sdmxjson2/writer/v2_1/metadata.py +31 -0
- pysdmx/io/json/sdmxjson2/writer/v2_1/structure.py +33 -0
- pysdmx/io/reader.py +12 -3
- pysdmx/io/writer.py +13 -3
- pysdmx/io/xml/__ss_aux_reader.py +39 -17
- pysdmx/io/xml/__structure_aux_reader.py +221 -33
- pysdmx/io/xml/__structure_aux_writer.py +304 -5
- pysdmx/io/xml/__tokens.py +12 -0
- pysdmx/io/xml/__write_aux.py +9 -0
- pysdmx/io/xml/sdmx21/writer/generic.py +2 -2
- pysdmx/model/dataflow.py +2 -2
- pysdmx/toolkit/pd/_data_utils.py +1 -1
- {pysdmx-1.10.1.dist-info → pysdmx-1.11.0.dist-info}/METADATA +7 -1
- {pysdmx-1.10.1.dist-info → pysdmx-1.11.0.dist-info}/RECORD +33 -28
- {pysdmx-1.10.1.dist-info → pysdmx-1.11.0.dist-info}/WHEEL +1 -1
- pysdmx/io/json/sdmxjson2/writer/metadata.py +0 -60
- pysdmx/io/json/sdmxjson2/writer/structure.py +0 -61
- {pysdmx-1.10.1.dist-info → pysdmx-1.11.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
"""Writer interface for SDMX-JSON 2.1.0 Structure messages."""
|
|
2
|
+
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from typing import Optional, Sequence, Union
|
|
5
|
+
|
|
6
|
+
from pysdmx.io.json.sdmxjson2.writer._helper import write_structure_msg
|
|
7
|
+
from pysdmx.model.__base import MaintainableArtefact
|
|
8
|
+
from pysdmx.model.message import Header
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
def write(
|
|
12
|
+
structures: Sequence[MaintainableArtefact],
|
|
13
|
+
output_path: Optional[Union[str, Path]] = None,
|
|
14
|
+
prettyprint: bool = True,
|
|
15
|
+
header: Optional[Header] = None,
|
|
16
|
+
) -> Optional[str]:
|
|
17
|
+
"""Write maintainable SDMX artefacts in SDMX-JSON 2.1.0.
|
|
18
|
+
|
|
19
|
+
Args:
|
|
20
|
+
structures: The maintainable SDMX artefacts to be serialized.
|
|
21
|
+
output_path: The path to save the JSON file. If None or empty, the
|
|
22
|
+
serialized content is returned as a string instead.
|
|
23
|
+
prettyprint: Whether to format the JSON output with indentation (True)
|
|
24
|
+
or output compact JSON without extra whitespace (False).
|
|
25
|
+
header: The header to be used in the SDMX-JSON message
|
|
26
|
+
(will be generated if no header is supplied).
|
|
27
|
+
|
|
28
|
+
Returns:
|
|
29
|
+
The JSON string if output_path is None or empty, None otherwise.
|
|
30
|
+
"""
|
|
31
|
+
return write_structure_msg(
|
|
32
|
+
structures, output_path, prettyprint, header, "2.1"
|
|
33
|
+
)
|
pysdmx/io/reader.py
CHANGED
|
@@ -80,7 +80,10 @@ def read_sdmx( # noqa: C901
|
|
|
80
80
|
header = read_header(input_str, validate=validate)
|
|
81
81
|
# SDMX-ML 3.1 Structure
|
|
82
82
|
result_structures = read_structure(input_str, validate=validate)
|
|
83
|
-
elif read_format
|
|
83
|
+
elif read_format in (
|
|
84
|
+
Format.STRUCTURE_SDMX_JSON_2_0_0,
|
|
85
|
+
Format.STRUCTURE_SDMX_JSON_2_1_0,
|
|
86
|
+
):
|
|
84
87
|
from pysdmx.io.json.sdmxjson2.reader.structure import (
|
|
85
88
|
read as read_struct,
|
|
86
89
|
)
|
|
@@ -90,7 +93,10 @@ def read_sdmx( # noqa: C901
|
|
|
90
93
|
result_structures = (
|
|
91
94
|
struct_msg.structures if struct_msg.structures else []
|
|
92
95
|
)
|
|
93
|
-
elif read_format
|
|
96
|
+
elif read_format in (
|
|
97
|
+
Format.REFMETA_SDMX_JSON_2_0_0,
|
|
98
|
+
Format.REFMETA_SDMX_JSON_2_1_0,
|
|
99
|
+
):
|
|
94
100
|
from pysdmx.io.json.sdmxjson2.reader.metadata import (
|
|
95
101
|
read as read_refmeta,
|
|
96
102
|
)
|
|
@@ -174,7 +180,10 @@ def read_sdmx( # noqa: C901
|
|
|
174
180
|
return Message(header=header, data=result_data)
|
|
175
181
|
elif read_format == Format.REGISTRY_SDMX_ML_2_1:
|
|
176
182
|
return Message(header=header, submission=result_submission)
|
|
177
|
-
elif read_format
|
|
183
|
+
elif read_format in (
|
|
184
|
+
Format.REFMETA_SDMX_JSON_2_0_0,
|
|
185
|
+
Format.REFMETA_SDMX_JSON_2_1_0,
|
|
186
|
+
):
|
|
178
187
|
return Message(header=header, reports=reports)
|
|
179
188
|
# TODO: Ensure we have changed the signature of the structure readers
|
|
180
189
|
return Message(header=header, structures=result_structures)
|
pysdmx/io/writer.py
CHANGED
|
@@ -24,10 +24,16 @@ WRITERS = {
|
|
|
24
24
|
Format.DATA_SDMX_ML_3_1: "pysdmx.io.xml.sdmx31.writer.structure_specific",
|
|
25
25
|
Format.STRUCTURE_SDMX_ML_3_1: "pysdmx.io.xml.sdmx31.writer.structure",
|
|
26
26
|
Format.STRUCTURE_SDMX_JSON_2_0_0: (
|
|
27
|
-
"pysdmx.io.json.sdmxjson2.writer.structure"
|
|
27
|
+
"pysdmx.io.json.sdmxjson2.writer.v2_0.structure"
|
|
28
|
+
),
|
|
29
|
+
Format.STRUCTURE_SDMX_JSON_2_1_0: (
|
|
30
|
+
"pysdmx.io.json.sdmxjson2.writer.v2_1.structure"
|
|
28
31
|
),
|
|
29
32
|
Format.REFMETA_SDMX_JSON_2_0_0: (
|
|
30
|
-
"pysdmx.io.json.sdmxjson2.writer.metadata"
|
|
33
|
+
"pysdmx.io.json.sdmxjson2.writer.v2_0.metadata"
|
|
34
|
+
),
|
|
35
|
+
Format.REFMETA_SDMX_JSON_2_1_0: (
|
|
36
|
+
"pysdmx.io.json.sdmxjson2.writer.v2_1.metadata"
|
|
31
37
|
),
|
|
32
38
|
}
|
|
33
39
|
|
|
@@ -36,9 +42,13 @@ STRUCTURE_WRITERS = (
|
|
|
36
42
|
Format.STRUCTURE_SDMX_ML_3_0,
|
|
37
43
|
Format.STRUCTURE_SDMX_ML_3_1,
|
|
38
44
|
Format.STRUCTURE_SDMX_JSON_2_0_0,
|
|
45
|
+
Format.STRUCTURE_SDMX_JSON_2_1_0,
|
|
39
46
|
)
|
|
40
47
|
|
|
41
|
-
REFMETA_WRITERS = (
|
|
48
|
+
REFMETA_WRITERS = (
|
|
49
|
+
Format.REFMETA_SDMX_JSON_2_0_0,
|
|
50
|
+
Format.REFMETA_SDMX_JSON_2_1_0,
|
|
51
|
+
)
|
|
42
52
|
|
|
43
53
|
|
|
44
54
|
def write_sdmx(
|
pysdmx/io/xml/__ss_aux_reader.py
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
"""SDMX XML StructureSpecificData reader aux module."""
|
|
2
2
|
|
|
3
3
|
import itertools
|
|
4
|
-
from typing import Any, Dict
|
|
4
|
+
from typing import Any, Dict, List
|
|
5
5
|
|
|
6
6
|
import numpy as np
|
|
7
7
|
import pandas as pd
|
|
@@ -40,23 +40,22 @@ def _reading_str_series(dataset: Dict[str, Any]) -> pd.DataFrame:
|
|
|
40
40
|
return df
|
|
41
41
|
|
|
42
42
|
|
|
43
|
-
def _reading_group_data(dataset: Dict[str, Any]) -> pd.DataFrame:
|
|
43
|
+
def _reading_group_data(dataset: Dict[str, Any]) -> List[pd.DataFrame]:
|
|
44
44
|
# Structure Specific Group Data
|
|
45
|
-
|
|
46
|
-
df = None
|
|
45
|
+
group_dfs = []
|
|
47
46
|
dataset[GROUP] = add_list(dataset[GROUP])
|
|
48
47
|
for data in dataset[GROUP]:
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
test_list, df = __process_df(test_list, df, is_end=True)
|
|
48
|
+
group_dict = dict(data.items())
|
|
49
|
+
group_df = pd.DataFrame([group_dict])
|
|
52
50
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
51
|
+
# Remove :type columns
|
|
52
|
+
cols_to_delete = [x for x in group_df.columns if ":type" in x]
|
|
53
|
+
for x in cols_to_delete:
|
|
54
|
+
del group_df[x]
|
|
56
55
|
|
|
57
|
-
|
|
56
|
+
group_dfs.append(group_df)
|
|
58
57
|
|
|
59
|
-
return
|
|
58
|
+
return group_dfs
|
|
60
59
|
|
|
61
60
|
|
|
62
61
|
def _get_at_att_str(dataset: Dict[str, Any]) -> Dict[str, Any]:
|
|
@@ -76,11 +75,34 @@ def _parse_structure_specific_data(
|
|
|
76
75
|
# Structure Specific Series
|
|
77
76
|
df = _reading_str_series(dataset)
|
|
78
77
|
if GROUP in dataset:
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
78
|
+
group_dfs = _reading_group_data(dataset)
|
|
79
|
+
original_columns = df.columns.tolist()
|
|
80
|
+
for group_df in group_dfs:
|
|
81
|
+
# Find non-NaN columns in this group
|
|
82
|
+
non_nan_cols = [
|
|
83
|
+
col
|
|
84
|
+
for col in group_df.columns
|
|
85
|
+
if not group_df[col].isna().all()
|
|
86
|
+
]
|
|
87
|
+
|
|
88
|
+
# Merge keys are intersection of original and non-NaN cols
|
|
89
|
+
merge_cols = list(
|
|
90
|
+
set(original_columns).intersection(set(non_nan_cols))
|
|
91
|
+
)
|
|
92
|
+
|
|
93
|
+
group_df = group_df.drop_duplicates(merge_cols, keep="first")
|
|
94
|
+
df = pd.merge(
|
|
95
|
+
df,
|
|
96
|
+
group_df,
|
|
97
|
+
on=merge_cols,
|
|
98
|
+
how="left",
|
|
99
|
+
suffixes=("", "_drop"),
|
|
100
|
+
)
|
|
101
|
+
for col in list(df.columns):
|
|
102
|
+
if col.endswith("_drop"):
|
|
103
|
+
original = col[:-5]
|
|
104
|
+
df[original] = df[original].fillna(df[col])
|
|
105
|
+
df.drop(col, axis=1, inplace=True)
|
|
84
106
|
elif OBS in dataset:
|
|
85
107
|
dataset[OBS] = add_list(dataset[OBS])
|
|
86
108
|
# Structure Specific All dimensions
|
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
"""Parsers for reading metadata."""
|
|
2
2
|
|
|
3
3
|
from datetime import datetime
|
|
4
|
-
from typing import Any, Callable, Dict, List, Optional, Sequence, Union
|
|
4
|
+
from typing import Any, Callable, Dict, List, Optional, Sequence, Type, Union
|
|
5
5
|
|
|
6
6
|
from msgspec import Struct
|
|
7
|
+
from msgspec.structs import asdict
|
|
7
8
|
|
|
8
9
|
from pysdmx.io.xml.__tokens import (
|
|
9
10
|
AGENCIES,
|
|
@@ -29,6 +30,8 @@ from pysdmx.io.xml.__tokens import (
|
|
|
29
30
|
CLS,
|
|
30
31
|
CODE,
|
|
31
32
|
CODES_LOW,
|
|
33
|
+
COMPONENT_MAP,
|
|
34
|
+
COMPONENT_MAPS,
|
|
32
35
|
COMPS,
|
|
33
36
|
CON,
|
|
34
37
|
CON_ID,
|
|
@@ -44,6 +47,7 @@ from pysdmx.io.xml.__tokens import (
|
|
|
44
47
|
CUSTOM_TYPE_SCHEMES,
|
|
45
48
|
CUSTOM_TYPES,
|
|
46
49
|
DATA_PROV,
|
|
50
|
+
DATE_PATTERN_MAP,
|
|
47
51
|
DEPARTMENT,
|
|
48
52
|
DESC,
|
|
49
53
|
DFW,
|
|
@@ -64,6 +68,8 @@ from pysdmx.io.xml.__tokens import (
|
|
|
64
68
|
FACETS,
|
|
65
69
|
FAX,
|
|
66
70
|
FAXES,
|
|
71
|
+
FIXED_VALUE_MAP,
|
|
72
|
+
FIXED_VALUE_MAPS,
|
|
67
73
|
GROUP,
|
|
68
74
|
GROUP_DIM,
|
|
69
75
|
GROUPS_LOW,
|
|
@@ -98,6 +104,8 @@ from pysdmx.io.xml.__tokens import (
|
|
|
98
104
|
PROV_AGREEMENT,
|
|
99
105
|
PROV_AGREEMENTS,
|
|
100
106
|
REF,
|
|
107
|
+
REPRESENTATION_MAP,
|
|
108
|
+
REPRESENTATION_MAPS,
|
|
101
109
|
REQUIRED,
|
|
102
110
|
ROLE,
|
|
103
111
|
RULE,
|
|
@@ -110,6 +118,8 @@ from pysdmx.io.xml.__tokens import (
|
|
|
110
118
|
STR_URL_LOW,
|
|
111
119
|
STR_USAGE,
|
|
112
120
|
STRUCTURE,
|
|
121
|
+
STRUCTURE_MAP,
|
|
122
|
+
STRUCTURE_MAPS,
|
|
113
123
|
TELEPHONE,
|
|
114
124
|
TELEPHONES,
|
|
115
125
|
TEXT,
|
|
@@ -152,10 +162,19 @@ from pysdmx.model import (
|
|
|
152
162
|
AgencyScheme,
|
|
153
163
|
Code,
|
|
154
164
|
Codelist,
|
|
165
|
+
ComponentMap,
|
|
155
166
|
Concept,
|
|
156
167
|
ConceptScheme,
|
|
157
168
|
DataType,
|
|
169
|
+
DatePatternMap,
|
|
158
170
|
Facets,
|
|
171
|
+
FixedValueMap,
|
|
172
|
+
ImplicitComponentMap,
|
|
173
|
+
MultiComponentMap,
|
|
174
|
+
MultiValueMap,
|
|
175
|
+
RepresentationMap,
|
|
176
|
+
StructureMap,
|
|
177
|
+
ValueMap,
|
|
159
178
|
VtlCodelistMapping,
|
|
160
179
|
VtlConceptMapping,
|
|
161
180
|
)
|
|
@@ -207,6 +226,11 @@ STRUCTURES_MAPPING = {
|
|
|
207
226
|
UDO_SCHEME: UserDefinedOperatorScheme,
|
|
208
227
|
TRANS_SCHEME: TransformationScheme,
|
|
209
228
|
VTL_MAPPING_SCHEME: VtlMappingScheme,
|
|
229
|
+
STRUCTURE_MAP: StructureMap,
|
|
230
|
+
COMPONENT_MAP: ComponentMap,
|
|
231
|
+
FIXED_VALUE_MAP: FixedValueMap,
|
|
232
|
+
DATE_PATTERN_MAP: DatePatternMap,
|
|
233
|
+
REPRESENTATION_MAP: RepresentationMap,
|
|
210
234
|
NAME_PER_SCHEME: NamePersonalisationScheme,
|
|
211
235
|
CUSTOM_TYPE_SCHEME: CustomTypeScheme,
|
|
212
236
|
PROV_AGREEMENTS: ProvisionAgreement,
|
|
@@ -308,6 +332,10 @@ class StructureParser(Struct):
|
|
|
308
332
|
rulesets: Dict[str, RulesetScheme] = {}
|
|
309
333
|
udos: Dict[str, UserDefinedOperatorScheme] = {}
|
|
310
334
|
vtl_mappings: Dict[str, VtlMappingScheme] = {}
|
|
335
|
+
structure_maps: Dict[str, StructureMap] = {}
|
|
336
|
+
component_maps: Dict[str, ComponentMap] = {}
|
|
337
|
+
fixed_value_maps: Dict[str, FixedValueMap] = {}
|
|
338
|
+
representation_maps: Dict[str, RepresentationMap] = {}
|
|
311
339
|
name_personalisations: Dict[str, NamePersonalisationScheme] = {}
|
|
312
340
|
custom_types: Dict[str, CustomTypeScheme] = {}
|
|
313
341
|
transformations: Dict[str, TransformationScheme] = {}
|
|
@@ -529,16 +557,11 @@ class StructureParser(Struct):
|
|
|
529
557
|
if FACETS.lower() in rep:
|
|
530
558
|
representation_info[LOCAL_FACETS_LOW] = rep.pop(FACETS.lower())
|
|
531
559
|
|
|
532
|
-
def __format_con_id(
|
|
533
|
-
|
|
560
|
+
def __format_con_id(
|
|
561
|
+
self, concept_ref: Union[str, Dict[str, Any]]
|
|
562
|
+
) -> Dict[str, Any]:
|
|
534
563
|
if isinstance(concept_ref, str):
|
|
535
564
|
item_reference = parse_urn(concept_ref)
|
|
536
|
-
scheme_reference = Reference(
|
|
537
|
-
sdmx_type=CS,
|
|
538
|
-
agency=item_reference.agency,
|
|
539
|
-
id=item_reference.id,
|
|
540
|
-
version=item_reference.version,
|
|
541
|
-
)
|
|
542
565
|
else:
|
|
543
566
|
item_reference = ItemReference(
|
|
544
567
|
sdmx_type=concept_ref[CLASS],
|
|
@@ -547,27 +570,44 @@ class StructureParser(Struct):
|
|
|
547
570
|
version=concept_ref[PAR_VER],
|
|
548
571
|
item_id=concept_ref[ID],
|
|
549
572
|
)
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
573
|
+
scheme_reference = Reference(
|
|
574
|
+
sdmx_type=CS,
|
|
575
|
+
agency=item_reference.agency,
|
|
576
|
+
id=item_reference.id,
|
|
577
|
+
version=item_reference.version,
|
|
578
|
+
)
|
|
556
579
|
|
|
557
580
|
concept_scheme = self.concepts.get(str(scheme_reference))
|
|
581
|
+
|
|
558
582
|
if concept_scheme is None:
|
|
559
583
|
return {CON: item_reference}
|
|
584
|
+
|
|
585
|
+
short_urn = str(item_reference)
|
|
560
586
|
for con in concept_scheme.concepts:
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
587
|
+
con_short = str(
|
|
588
|
+
ItemReference(
|
|
589
|
+
sdmx_type=item_reference.sdmx_type,
|
|
590
|
+
agency=item_reference.agency,
|
|
591
|
+
id=item_reference.id,
|
|
592
|
+
version=item_reference.version,
|
|
593
|
+
item_id=con.id,
|
|
594
|
+
)
|
|
595
|
+
)
|
|
596
|
+
|
|
597
|
+
if con_short == short_urn:
|
|
598
|
+
if con.urn is None:
|
|
599
|
+
con = Concept(
|
|
600
|
+
**{
|
|
601
|
+
**asdict(con),
|
|
602
|
+
"urn": (
|
|
603
|
+
"urn:sdmx:org.sdmx.infomodel.conceptscheme."
|
|
604
|
+
f"{short_urn}"
|
|
605
|
+
),
|
|
606
|
+
}
|
|
607
|
+
)
|
|
608
|
+
return {CON: con}
|
|
609
|
+
|
|
610
|
+
return {CON: item_reference}
|
|
571
611
|
|
|
572
612
|
@staticmethod
|
|
573
613
|
def __get_attachment_level( # noqa: C901
|
|
@@ -751,6 +791,11 @@ class StructureParser(Struct):
|
|
|
751
791
|
del comp[ATT_REL]
|
|
752
792
|
|
|
753
793
|
if ME_REL in comp:
|
|
794
|
+
measures = add_list(comp[ME_REL][MSR])
|
|
795
|
+
if len(measures) == 1 and measures[0] == "OBS_VALUE":
|
|
796
|
+
comp[ATT_LVL] = "O"
|
|
797
|
+
else:
|
|
798
|
+
comp[ATT_LVL] = ",".join(measures)
|
|
754
799
|
del comp[ME_REL]
|
|
755
800
|
|
|
756
801
|
if AS_STATUS in comp or USAGE in comp:
|
|
@@ -762,14 +807,10 @@ class StructureParser(Struct):
|
|
|
762
807
|
comp[REQUIRED] = False
|
|
763
808
|
del comp[status_key]
|
|
764
809
|
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
del comp[ANNOTATIONS]
|
|
770
|
-
|
|
771
|
-
if CON_ROLE in comp:
|
|
772
|
-
del comp[CON_ROLE]
|
|
810
|
+
unwanted_keys = ["position", ANNOTATIONS, CON_ROLE]
|
|
811
|
+
for key in unwanted_keys:
|
|
812
|
+
if key in comp:
|
|
813
|
+
del comp[key]
|
|
773
814
|
|
|
774
815
|
return Component(**comp)
|
|
775
816
|
|
|
@@ -1035,6 +1076,124 @@ class StructureParser(Struct):
|
|
|
1035
1076
|
)
|
|
1036
1077
|
return json_elem
|
|
1037
1078
|
|
|
1079
|
+
def __build_component_map(
|
|
1080
|
+
self, child_dict: Dict[str, Any]
|
|
1081
|
+
) -> Union[ComponentMap, MultiComponentMap, ImplicitComponentMap]:
|
|
1082
|
+
if "values" not in child_dict:
|
|
1083
|
+
return ImplicitComponentMap(
|
|
1084
|
+
source=child_dict["source"],
|
|
1085
|
+
target=child_dict["target"],
|
|
1086
|
+
)
|
|
1087
|
+
|
|
1088
|
+
src_list = add_list(child_dict.get("source"))
|
|
1089
|
+
tgt_list = add_list(child_dict.get("target"))
|
|
1090
|
+
|
|
1091
|
+
if len(src_list) != 1 or len(tgt_list) != 1:
|
|
1092
|
+
return MultiComponentMap(
|
|
1093
|
+
source=src_list,
|
|
1094
|
+
target=tgt_list,
|
|
1095
|
+
values=child_dict["values"],
|
|
1096
|
+
)
|
|
1097
|
+
|
|
1098
|
+
return ComponentMap(
|
|
1099
|
+
source=src_list[0],
|
|
1100
|
+
target=tgt_list[0],
|
|
1101
|
+
values=child_dict["values"],
|
|
1102
|
+
)
|
|
1103
|
+
|
|
1104
|
+
def __build_representation_mapping(
|
|
1105
|
+
self, child_dict: Dict[str, Any]
|
|
1106
|
+
) -> Union[ValueMap, MultiValueMap]:
|
|
1107
|
+
src = child_dict.get("source")
|
|
1108
|
+
tgt = child_dict.get("target")
|
|
1109
|
+
|
|
1110
|
+
src_list = add_list(src) if src is not None else []
|
|
1111
|
+
tgt_list = add_list(tgt) if tgt is not None else []
|
|
1112
|
+
|
|
1113
|
+
if len(src_list) != 1 or len(tgt_list) != 1:
|
|
1114
|
+
return MultiValueMap(
|
|
1115
|
+
source=src_list,
|
|
1116
|
+
target=tgt_list,
|
|
1117
|
+
valid_from=child_dict.get("valid_from"),
|
|
1118
|
+
valid_to=child_dict.get("valid_to"),
|
|
1119
|
+
)
|
|
1120
|
+
|
|
1121
|
+
return ValueMap(
|
|
1122
|
+
source=src_list[0],
|
|
1123
|
+
target=tgt_list[0],
|
|
1124
|
+
valid_from=child_dict.get("valid_from"),
|
|
1125
|
+
valid_to=child_dict.get("valid_to"),
|
|
1126
|
+
)
|
|
1127
|
+
|
|
1128
|
+
def __format_maps(self, element: Dict[str, Any]) -> Dict[str, Any]:
|
|
1129
|
+
if "sourcePattern" in element:
|
|
1130
|
+
element["pattern_type"] = (
|
|
1131
|
+
# DatePatternMap.pattern_type defaults value is fixed
|
|
1132
|
+
"variable" if "FrequencyDimension" in element else "fixed"
|
|
1133
|
+
)
|
|
1134
|
+
|
|
1135
|
+
renames = {
|
|
1136
|
+
"Source": "source",
|
|
1137
|
+
"Target": "target",
|
|
1138
|
+
"Value": "value",
|
|
1139
|
+
"SourceCodelist": "source",
|
|
1140
|
+
"TargetCodelist": "target",
|
|
1141
|
+
"SourceValue": "source",
|
|
1142
|
+
"TargetValue": "target",
|
|
1143
|
+
"RepresentationMap": "values",
|
|
1144
|
+
"sourcePattern": "pattern",
|
|
1145
|
+
"resolvePeriod": "resolve_period",
|
|
1146
|
+
"TargetFrequencyID": "frequency",
|
|
1147
|
+
"FrequencyDimension": "frequency",
|
|
1148
|
+
"validFrom": "valid_from",
|
|
1149
|
+
"validTo": "valid_to",
|
|
1150
|
+
}
|
|
1151
|
+
|
|
1152
|
+
for xml_key, py_key in renames.items():
|
|
1153
|
+
if xml_key in element:
|
|
1154
|
+
element[py_key] = element.pop(xml_key)
|
|
1155
|
+
|
|
1156
|
+
child_class_mapping: Dict[str, Type[Any]] = {
|
|
1157
|
+
"ComponentMap": ComponentMap,
|
|
1158
|
+
"FixedValueMap": FixedValueMap,
|
|
1159
|
+
"DatePatternMap": DatePatternMap,
|
|
1160
|
+
"RepresentationMapping": ValueMap,
|
|
1161
|
+
}
|
|
1162
|
+
|
|
1163
|
+
MapChild = Union[
|
|
1164
|
+
ComponentMap,
|
|
1165
|
+
MultiComponentMap,
|
|
1166
|
+
ImplicitComponentMap,
|
|
1167
|
+
FixedValueMap,
|
|
1168
|
+
DatePatternMap,
|
|
1169
|
+
ValueMap,
|
|
1170
|
+
MultiValueMap,
|
|
1171
|
+
]
|
|
1172
|
+
consolidated_children: List[MapChild] = []
|
|
1173
|
+
|
|
1174
|
+
for xml_tag, target_class in child_class_mapping.items():
|
|
1175
|
+
if xml_tag not in element:
|
|
1176
|
+
continue
|
|
1177
|
+
|
|
1178
|
+
for child_dict in add_list(element.pop(xml_tag)):
|
|
1179
|
+
self.__format_maps(child_dict)
|
|
1180
|
+
|
|
1181
|
+
if xml_tag == "ComponentMap":
|
|
1182
|
+
consolidated_children.append(
|
|
1183
|
+
self.__build_component_map(child_dict)
|
|
1184
|
+
)
|
|
1185
|
+
elif xml_tag == "RepresentationMapping":
|
|
1186
|
+
consolidated_children.append(
|
|
1187
|
+
self.__build_representation_mapping(child_dict)
|
|
1188
|
+
)
|
|
1189
|
+
else:
|
|
1190
|
+
consolidated_children.append(target_class(**child_dict))
|
|
1191
|
+
|
|
1192
|
+
if consolidated_children:
|
|
1193
|
+
element["maps"] = consolidated_children
|
|
1194
|
+
|
|
1195
|
+
return element
|
|
1196
|
+
|
|
1038
1197
|
def __format_scheme(
|
|
1039
1198
|
self, json_elem: Dict[str, Any], scheme: str, item: str
|
|
1040
1199
|
) -> Dict[str, ItemScheme]:
|
|
@@ -1116,6 +1275,7 @@ class StructureParser(Struct):
|
|
|
1116
1275
|
element = self.__format_validity(element)
|
|
1117
1276
|
element = self.__format_groups(element)
|
|
1118
1277
|
element = self.__format_components(element)
|
|
1278
|
+
element = self.__format_maps(element)
|
|
1119
1279
|
if item == PROV_AGREEMENT:
|
|
1120
1280
|
element = self.__format_prov_agreement(element)
|
|
1121
1281
|
|
|
@@ -1300,6 +1460,34 @@ class StructureParser(Struct):
|
|
|
1300
1460
|
),
|
|
1301
1461
|
"transformations",
|
|
1302
1462
|
),
|
|
1463
|
+
STRUCTURE_MAPS: process_structure(
|
|
1464
|
+
STRUCTURE_MAPS,
|
|
1465
|
+
lambda data: self.__format_schema(
|
|
1466
|
+
data, STRUCTURE_MAP, STRUCTURE_MAP
|
|
1467
|
+
),
|
|
1468
|
+
"structure_maps",
|
|
1469
|
+
),
|
|
1470
|
+
COMPONENT_MAPS: process_structure(
|
|
1471
|
+
COMPONENT_MAPS,
|
|
1472
|
+
lambda data: self.__format_schema(
|
|
1473
|
+
data, COMPONENT_MAP, COMPONENT_MAP
|
|
1474
|
+
),
|
|
1475
|
+
"component_maps",
|
|
1476
|
+
),
|
|
1477
|
+
FIXED_VALUE_MAPS: process_structure(
|
|
1478
|
+
FIXED_VALUE_MAPS,
|
|
1479
|
+
lambda data: self.__format_schema(
|
|
1480
|
+
data, FIXED_VALUE_MAP, FIXED_VALUE_MAP
|
|
1481
|
+
),
|
|
1482
|
+
"fixed_value_maps",
|
|
1483
|
+
),
|
|
1484
|
+
REPRESENTATION_MAPS: process_structure(
|
|
1485
|
+
REPRESENTATION_MAPS,
|
|
1486
|
+
lambda data: self.__format_schema(
|
|
1487
|
+
data, REPRESENTATION_MAP, REPRESENTATION_MAP
|
|
1488
|
+
),
|
|
1489
|
+
"representation_maps",
|
|
1490
|
+
),
|
|
1303
1491
|
TRANS_SCHEMES: process_structure(
|
|
1304
1492
|
TRANS_SCHEMES,
|
|
1305
1493
|
lambda data: self.__format_scheme(
|