pysdmx 1.10.1__py3-none-any.whl → 1.12.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/constraint.py +22 -1
- pysdmx/io/json/fusion/messages/dsd.py +20 -14
- pysdmx/io/json/fusion/messages/msd.py +6 -9
- pysdmx/io/json/fusion/messages/schema.py +20 -1
- 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/schema.py +38 -5
- 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 +11 -2
- pysdmx/toolkit/pd/_data_utils.py +1 -1
- {pysdmx-1.10.1.dist-info → pysdmx-1.12.0.dist-info}/METADATA +7 -1
- {pysdmx-1.10.1.dist-info → pysdmx-1.12.0.dist-info}/RECORD +36 -31
- {pysdmx-1.10.1.dist-info → pysdmx-1.12.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.12.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -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(
|