pysdmx 1.10.0rc1__py3-none-any.whl → 1.10.0rc2__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/__init__.py +3 -2
- pysdmx/io/_pd_utils.py +83 -0
- pysdmx/io/csv/__csv_aux_writer.py +23 -0
- pysdmx/io/csv/sdmx10/reader/__init__.py +1 -1
- pysdmx/io/csv/sdmx10/writer/__init__.py +15 -9
- pysdmx/io/csv/sdmx20/reader/__init__.py +1 -1
- pysdmx/io/csv/sdmx20/writer/__init__.py +1 -1
- pysdmx/io/csv/sdmx21/reader/__init__.py +1 -1
- pysdmx/io/csv/sdmx21/writer/__init__.py +1 -1
- pysdmx/io/json/sdmxjson2/messages/__init__.py +4 -0
- pysdmx/io/json/sdmxjson2/messages/code.py +16 -6
- pysdmx/io/json/sdmxjson2/messages/constraint.py +235 -16
- pysdmx/io/json/sdmxjson2/messages/dsd.py +35 -7
- pysdmx/io/json/sdmxjson2/messages/map.py +5 -4
- pysdmx/io/json/sdmxjson2/messages/metadataflow.py +1 -0
- pysdmx/io/json/sdmxjson2/messages/msd.py +18 -10
- pysdmx/io/json/sdmxjson2/messages/schema.py +2 -2
- pysdmx/io/json/sdmxjson2/messages/structure.py +81 -44
- pysdmx/io/json/sdmxjson2/messages/vtl.py +13 -9
- pysdmx/io/xml/__write_data_aux.py +20 -7
- pysdmx/io/xml/__write_structure_specific_aux.py +71 -54
- pysdmx/io/xml/sdmx21/writer/generic.py +31 -19
- pysdmx/model/__base.py +46 -1
- pysdmx/model/__init__.py +18 -0
- pysdmx/model/category.py +17 -0
- pysdmx/model/concept.py +16 -0
- pysdmx/model/constraint.py +69 -0
- pysdmx/model/message.py +80 -71
- {pysdmx-1.10.0rc1.dist-info → pysdmx-1.10.0rc2.dist-info}/METADATA +1 -1
- {pysdmx-1.10.0rc1.dist-info → pysdmx-1.10.0rc2.dist-info}/RECORD +33 -31
- {pysdmx-1.10.0rc1.dist-info → pysdmx-1.10.0rc2.dist-info}/WHEEL +0 -0
- {pysdmx-1.10.0rc1.dist-info → pysdmx-1.10.0rc2.dist-info}/licenses/LICENSE +0 -0
|
@@ -265,12 +265,17 @@ class JsonAttribute(Struct, frozen=True, omit_defaults=True):
|
|
|
265
265
|
attribute.attachment_level # type: ignore[arg-type]
|
|
266
266
|
)
|
|
267
267
|
repr = _get_json_representation(attribute)
|
|
268
|
+
# The line below will need to be changed when we work on
|
|
269
|
+
# Measure Relationship (cf. issue #467)
|
|
270
|
+
mr = ["OBS_VALUE"] if attribute.attachment_level == "O" else None
|
|
271
|
+
|
|
268
272
|
return JsonAttribute(
|
|
269
273
|
id=attribute.id,
|
|
270
274
|
conceptIdentity=concept,
|
|
271
275
|
attributeRelationship=level,
|
|
272
276
|
usage=usage,
|
|
273
277
|
localRepresentation=repr,
|
|
278
|
+
measureRelationship=mr,
|
|
274
279
|
)
|
|
275
280
|
|
|
276
281
|
|
|
@@ -447,19 +452,42 @@ class JsonComponents(Struct, frozen=True, omit_defaults=True):
|
|
|
447
452
|
enums = [cl.to_model() for cl in cls]
|
|
448
453
|
enums.extend([vl.to_model() for vl in vls])
|
|
449
454
|
comps = []
|
|
450
|
-
if constraints
|
|
451
|
-
|
|
455
|
+
if constraints:
|
|
456
|
+
incl_cubes = []
|
|
457
|
+
for const in constraints:
|
|
458
|
+
incl_cubes.extend(
|
|
459
|
+
[cr for cr in (const.cubeRegions or []) if cr.include]
|
|
460
|
+
)
|
|
461
|
+
if len(incl_cubes) == 1:
|
|
462
|
+
cons = {
|
|
463
|
+
kv.id: [v.value for v in kv.values]
|
|
464
|
+
for kv in incl_cubes[0].keyValues
|
|
465
|
+
}
|
|
466
|
+
else:
|
|
467
|
+
cons = {}
|
|
452
468
|
else:
|
|
453
469
|
cons = {}
|
|
454
|
-
comps.extend(
|
|
470
|
+
comps.extend(
|
|
471
|
+
self.dimensionList.to_model(
|
|
472
|
+
cs,
|
|
473
|
+
enums,
|
|
474
|
+
cons, # type: ignore[arg-type]
|
|
475
|
+
)
|
|
476
|
+
)
|
|
455
477
|
if self.measureList:
|
|
456
|
-
comps.extend(
|
|
478
|
+
comps.extend(
|
|
479
|
+
self.measureList.to_model(
|
|
480
|
+
cs,
|
|
481
|
+
enums,
|
|
482
|
+
cons, # type: ignore[arg-type]
|
|
483
|
+
)
|
|
484
|
+
)
|
|
457
485
|
if self.attributeList:
|
|
458
486
|
comps.extend(
|
|
459
487
|
self.attributeList.to_model(
|
|
460
488
|
cs,
|
|
461
489
|
enums,
|
|
462
|
-
cons,
|
|
490
|
+
cons, # type: ignore[arg-type]
|
|
463
491
|
self.groups,
|
|
464
492
|
)
|
|
465
493
|
)
|
|
@@ -556,7 +584,7 @@ class JsonDataStructures(Struct, frozen=True, omit_defaults=True):
|
|
|
556
584
|
conceptSchemes: Sequence[JsonConceptScheme] = ()
|
|
557
585
|
valuelists: Sequence[JsonValuelist] = ()
|
|
558
586
|
codelists: Sequence[JsonCodelist] = ()
|
|
559
|
-
|
|
587
|
+
dataConstraints: Sequence[JsonDataConstraint] = ()
|
|
560
588
|
|
|
561
589
|
def to_model(self) -> Sequence[DataStructureDefinition]:
|
|
562
590
|
"""Returns the requested dsds."""
|
|
@@ -565,7 +593,7 @@ class JsonDataStructures(Struct, frozen=True, omit_defaults=True):
|
|
|
565
593
|
self.conceptSchemes,
|
|
566
594
|
self.codelists,
|
|
567
595
|
self.valuelists,
|
|
568
|
-
self.
|
|
596
|
+
self.dataConstraints,
|
|
569
597
|
)
|
|
570
598
|
for dsd in self.dataStructures
|
|
571
599
|
]
|
|
@@ -87,19 +87,20 @@ class JsonRepresentationMapping(Struct, frozen=True, omit_defaults=True):
|
|
|
87
87
|
self, vm: Union[MultiValueMap, ValueMap]
|
|
88
88
|
) -> "JsonRepresentationMapping":
|
|
89
89
|
"""Converts a value map to an SDMX-JSON JsonRepresentationMapping."""
|
|
90
|
+
fmt = r"%Y-%m-%dT%H:%M:%S"
|
|
90
91
|
if isinstance(vm, ValueMap):
|
|
91
92
|
return JsonRepresentationMapping(
|
|
92
93
|
[JsonSourceValue.from_model(vm.source)],
|
|
93
94
|
[vm.target],
|
|
94
|
-
vm.valid_from.strftime(
|
|
95
|
-
vm.valid_to.strftime(
|
|
95
|
+
vm.valid_from.strftime(fmt) if vm.valid_from else None,
|
|
96
|
+
vm.valid_to.strftime(fmt) if vm.valid_to else None,
|
|
96
97
|
)
|
|
97
98
|
else:
|
|
98
99
|
return JsonRepresentationMapping(
|
|
99
100
|
[JsonSourceValue.from_model(s) for s in vm.source],
|
|
100
101
|
vm.target,
|
|
101
|
-
vm.valid_from.strftime(
|
|
102
|
-
vm.valid_to.strftime(
|
|
102
|
+
vm.valid_from.strftime(fmt) if vm.valid_from else None,
|
|
103
|
+
vm.valid_to.strftime(fmt) if vm.valid_to else None,
|
|
103
104
|
)
|
|
104
105
|
|
|
105
106
|
|
|
@@ -15,7 +15,6 @@ from pysdmx.io.json.sdmxjson2.messages.core import (
|
|
|
15
15
|
from pysdmx.io.json.sdmxjson2.messages.dsd import (
|
|
16
16
|
_find_concept,
|
|
17
17
|
_get_concept_reference,
|
|
18
|
-
_get_json_representation,
|
|
19
18
|
_get_representation,
|
|
20
19
|
)
|
|
21
20
|
from pysdmx.model import (
|
|
@@ -28,6 +27,13 @@ from pysdmx.model import (
|
|
|
28
27
|
from pysdmx.util import parse_item_urn
|
|
29
28
|
|
|
30
29
|
|
|
30
|
+
def _get_attr_repr(comp: MetadataComponent) -> Optional[JsonRepresentation]:
|
|
31
|
+
enum = comp.local_enum_ref if comp.local_enum_ref else None
|
|
32
|
+
return JsonRepresentation.from_model(
|
|
33
|
+
comp.local_dtype, enum, comp.local_facets, None
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
31
37
|
class JsonMetadataAttribute(Struct, frozen=True, omit_defaults=True):
|
|
32
38
|
"""SDMX-JSON payload for an attribute."""
|
|
33
39
|
|
|
@@ -80,11 +86,13 @@ class JsonMetadataAttribute(Struct, frozen=True, omit_defaults=True):
|
|
|
80
86
|
def from_model(self, cmp: MetadataComponent) -> "JsonMetadataAttribute":
|
|
81
87
|
"""Converts a pysdmx metadata attribute to an SDMX-JSON one."""
|
|
82
88
|
concept = _get_concept_reference(cmp)
|
|
83
|
-
repr =
|
|
89
|
+
repr = _get_attr_repr(cmp)
|
|
84
90
|
|
|
85
91
|
min_occurs = cmp.array_def.min_size if cmp.array_def else 0
|
|
86
|
-
if cmp.array_def is None
|
|
87
|
-
max_occurs: Union[int, Literal["unbounded"]] =
|
|
92
|
+
if cmp.array_def is None:
|
|
93
|
+
max_occurs: Union[int, Literal["unbounded"]] = 1
|
|
94
|
+
elif cmp.array_def.max_size is None:
|
|
95
|
+
max_occurs = "unbounded"
|
|
88
96
|
else:
|
|
89
97
|
max_occurs = cmp.array_def.max_size
|
|
90
98
|
|
|
@@ -95,9 +103,9 @@ class JsonMetadataAttribute(Struct, frozen=True, omit_defaults=True):
|
|
|
95
103
|
minOccurs=min_occurs,
|
|
96
104
|
maxOccurs=max_occurs,
|
|
97
105
|
isPresentational=cmp.is_presentational,
|
|
98
|
-
metadataAttributes=
|
|
99
|
-
JsonMetadataAttribute.from_model(c) for c in cmp.components
|
|
100
|
-
|
|
106
|
+
metadataAttributes=tuple(
|
|
107
|
+
[JsonMetadataAttribute.from_model(c) for c in cmp.components]
|
|
108
|
+
),
|
|
101
109
|
)
|
|
102
110
|
|
|
103
111
|
|
|
@@ -119,9 +127,9 @@ class JsonMetadataAttributes(Struct, frozen=True, omit_defaults=True):
|
|
|
119
127
|
) -> "JsonMetadataAttributes":
|
|
120
128
|
"""Converts a pysdmx list of metadata attributes to SDMX-JSON."""
|
|
121
129
|
return JsonMetadataAttributes(
|
|
122
|
-
metadataAttributes=
|
|
123
|
-
JsonMetadataAttribute.from_model(a) for a in attributes
|
|
124
|
-
|
|
130
|
+
metadataAttributes=tuple(
|
|
131
|
+
[JsonMetadataAttribute.from_model(a) for a in attributes]
|
|
132
|
+
)
|
|
125
133
|
)
|
|
126
134
|
|
|
127
135
|
|
|
@@ -21,7 +21,7 @@ class JsonSchemas(msgspec.Struct, frozen=True, omit_defaults=True):
|
|
|
21
21
|
dataStructures: Sequence[JsonDataStructure]
|
|
22
22
|
valuelists: Sequence[JsonValuelist] = ()
|
|
23
23
|
codelists: Sequence[JsonCodelist] = ()
|
|
24
|
-
|
|
24
|
+
dataConstraints: Sequence[JsonDataConstraint] = ()
|
|
25
25
|
|
|
26
26
|
def to_model(
|
|
27
27
|
self,
|
|
@@ -32,7 +32,7 @@ class JsonSchemas(msgspec.Struct, frozen=True, omit_defaults=True):
|
|
|
32
32
|
self.conceptSchemes,
|
|
33
33
|
self.codelists,
|
|
34
34
|
self.valuelists,
|
|
35
|
-
self.
|
|
35
|
+
self.dataConstraints,
|
|
36
36
|
)
|
|
37
37
|
return comps, grps # type: ignore[return-value]
|
|
38
38
|
|
|
@@ -17,6 +17,7 @@ from pysdmx.io.json.sdmxjson2.messages.code import (
|
|
|
17
17
|
JsonValuelist,
|
|
18
18
|
)
|
|
19
19
|
from pysdmx.io.json.sdmxjson2.messages.concept import JsonConceptScheme
|
|
20
|
+
from pysdmx.io.json.sdmxjson2.messages.constraint import JsonDataConstraint
|
|
20
21
|
from pysdmx.io.json.sdmxjson2.messages.core import JsonHeader
|
|
21
22
|
from pysdmx.io.json.sdmxjson2.messages.dataflow import JsonDataflow
|
|
22
23
|
from pysdmx.io.json.sdmxjson2.messages.dsd import JsonDataStructure
|
|
@@ -49,90 +50,95 @@ from pysdmx.model.message import StructureMessage
|
|
|
49
50
|
class JsonStructures(Struct, frozen=True, omit_defaults=True):
|
|
50
51
|
"""The allowed strutures."""
|
|
51
52
|
|
|
52
|
-
|
|
53
|
+
agencySchemes: Sequence[JsonAgencyScheme] = ()
|
|
54
|
+
categorisations: Sequence[JsonCategorisation] = ()
|
|
53
55
|
categorySchemes: Sequence[JsonCategoryScheme] = ()
|
|
54
|
-
conceptSchemes: Sequence[JsonConceptScheme] = ()
|
|
55
56
|
codelists: Sequence[JsonCodelist] = ()
|
|
56
|
-
|
|
57
|
+
conceptSchemes: Sequence[JsonConceptScheme] = ()
|
|
58
|
+
customTypeSchemes: Sequence[JsonCustomTypeScheme] = ()
|
|
59
|
+
dataConstraints: Sequence[JsonDataConstraint] = ()
|
|
60
|
+
dataflows: Sequence[JsonDataflow] = ()
|
|
61
|
+
dataProviderSchemes: Sequence[JsonDataProviderScheme] = ()
|
|
62
|
+
dataStructures: Sequence[JsonDataStructure] = ()
|
|
57
63
|
hierarchies: Sequence[JsonHierarchy] = ()
|
|
58
64
|
hierarchyAssociations: Sequence[JsonHierarchyAssociation] = ()
|
|
59
|
-
agencySchemes: Sequence[JsonAgencyScheme] = ()
|
|
60
|
-
dataProviderSchemes: Sequence[JsonDataProviderScheme] = ()
|
|
61
|
-
metadataProviderSchemes: Sequence[JsonMetadataProviderScheme] = ()
|
|
62
|
-
dataflows: Sequence[JsonDataflow] = ()
|
|
63
|
-
provisionAgreements: Sequence[JsonProvisionAgreement] = ()
|
|
64
65
|
metadataflows: Sequence[JsonMetadataflow] = ()
|
|
66
|
+
metadataProviderSchemes: Sequence[JsonMetadataProviderScheme] = ()
|
|
65
67
|
metadataProvisionAgreements: Sequence[JsonMetadataProvisionAgreement] = ()
|
|
66
68
|
metadataStructures: Sequence[JsonMetadataStructure] = ()
|
|
67
|
-
structureMaps: Sequence[JsonStructureMap] = ()
|
|
68
|
-
representationMaps: Sequence[JsonRepresentationMap] = ()
|
|
69
|
-
categorisations: Sequence[JsonCategorisation] = ()
|
|
70
|
-
customTypeSchemes: Sequence[JsonCustomTypeScheme] = ()
|
|
71
|
-
vtlMappingSchemes: Sequence[JsonVtlMappingScheme] = ()
|
|
72
69
|
namePersonalisationSchemes: Sequence[JsonNamePersonalisationScheme] = ()
|
|
70
|
+
provisionAgreements: Sequence[JsonProvisionAgreement] = ()
|
|
71
|
+
representationMaps: Sequence[JsonRepresentationMap] = ()
|
|
73
72
|
rulesetSchemes: Sequence[JsonRulesetScheme] = ()
|
|
73
|
+
structureMaps: Sequence[JsonStructureMap] = ()
|
|
74
74
|
transformationSchemes: Sequence[JsonTransformationScheme] = ()
|
|
75
75
|
userDefinedOperatorSchemes: Sequence[JsonUserDefinedOperatorScheme] = ()
|
|
76
|
+
valueLists: Sequence[JsonValuelist] = ()
|
|
77
|
+
vtlMappingSchemes: Sequence[JsonVtlMappingScheme] = ()
|
|
76
78
|
|
|
77
79
|
def to_model(self) -> Sequence[MaintainableArtefact]:
|
|
78
80
|
"""Map to pysdmx artefacts."""
|
|
79
81
|
structures = [] # type: ignore[var-annotated]
|
|
80
82
|
structures.extend(
|
|
81
|
-
i.to_model(
|
|
82
|
-
self.conceptSchemes, self.codelists, self.valueLists, ()
|
|
83
|
-
)
|
|
84
|
-
for i in self.dataStructures
|
|
83
|
+
i.to_model(self.dataflows) for i in self.agencySchemes
|
|
85
84
|
)
|
|
85
|
+
structures.extend(i.to_model() for i in self.categorisations)
|
|
86
86
|
structures.extend(i.to_model() for i in self.categorySchemes)
|
|
87
|
-
structures.extend(
|
|
88
|
-
i.to_model(self.codelists) for i in self.conceptSchemes
|
|
89
|
-
)
|
|
90
87
|
structures.extend(i.to_model() for i in self.codelists)
|
|
91
|
-
structures.extend(i.to_model() for i in self.valueLists)
|
|
92
|
-
structures.extend(i.to_model(self.codelists) for i in self.hierarchies)
|
|
93
88
|
structures.extend(
|
|
94
|
-
i.to_model(self.
|
|
95
|
-
for i in self.hierarchyAssociations
|
|
89
|
+
i.to_model(self.codelists) for i in self.conceptSchemes
|
|
96
90
|
)
|
|
91
|
+
structures.extend(i.to_model() for i in self.customTypeSchemes)
|
|
92
|
+
structures.extend(i.to_model() for i in self.dataConstraints)
|
|
97
93
|
structures.extend(
|
|
98
|
-
i.to_model(
|
|
94
|
+
i.to_model(
|
|
95
|
+
self.dataStructures,
|
|
96
|
+
self.conceptSchemes,
|
|
97
|
+
self.valueLists,
|
|
98
|
+
self.codelists,
|
|
99
|
+
)
|
|
100
|
+
for i in self.dataflows
|
|
99
101
|
)
|
|
100
102
|
structures.extend(
|
|
101
103
|
i.to_model(self.provisionAgreements)
|
|
102
104
|
for i in self.dataProviderSchemes
|
|
103
105
|
)
|
|
106
|
+
structures.extend(
|
|
107
|
+
i.to_model(
|
|
108
|
+
self.conceptSchemes, self.codelists, self.valueLists, ()
|
|
109
|
+
)
|
|
110
|
+
for i in self.dataStructures
|
|
111
|
+
)
|
|
112
|
+
structures.extend(i.to_model(self.codelists) for i in self.hierarchies)
|
|
113
|
+
structures.extend(
|
|
114
|
+
i.to_model(self.hierarchies, self.codelists)
|
|
115
|
+
for i in self.hierarchyAssociations
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
structures.extend(i.to_model() for i in self.metadataflows)
|
|
104
119
|
structures.extend(
|
|
105
120
|
i.to_model(self.metadataProvisionAgreements)
|
|
106
121
|
for i in self.metadataProviderSchemes
|
|
107
122
|
)
|
|
123
|
+
structures.extend(
|
|
124
|
+
i.to_model() for i in self.metadataProvisionAgreements
|
|
125
|
+
)
|
|
108
126
|
structures.extend(
|
|
109
127
|
i.to_model(self.conceptSchemes, self.codelists, self.valueLists)
|
|
110
128
|
for i in self.metadataStructures
|
|
111
129
|
)
|
|
112
130
|
structures.extend(
|
|
113
|
-
i.to_model(
|
|
114
|
-
self.dataStructures,
|
|
115
|
-
self.conceptSchemes,
|
|
116
|
-
self.valueLists,
|
|
117
|
-
self.codelists,
|
|
118
|
-
)
|
|
119
|
-
for i in self.dataflows
|
|
131
|
+
i.to_model() for i in self.namePersonalisationSchemes
|
|
120
132
|
)
|
|
121
133
|
structures.extend(i.to_model() for i in self.provisionAgreements)
|
|
122
|
-
structures.extend(i.to_model() for i in self.metadataflows)
|
|
123
134
|
structures.extend(
|
|
124
|
-
i.to_model()
|
|
135
|
+
i.to_model(bool(len(i.source) > 1 or len(i.target) > 1))
|
|
136
|
+
for i in self.representationMaps
|
|
125
137
|
)
|
|
138
|
+
structures.extend(i.to_model() for i in self.rulesetSchemes)
|
|
126
139
|
structures.extend(
|
|
127
140
|
i.to_model(self.representationMaps) for i in self.structureMaps
|
|
128
141
|
)
|
|
129
|
-
structures.extend(i.to_model() for i in self.categorisations)
|
|
130
|
-
structures.extend(i.to_model() for i in self.customTypeSchemes)
|
|
131
|
-
structures.extend(i.to_model() for i in self.vtlMappingSchemes)
|
|
132
|
-
structures.extend(
|
|
133
|
-
i.to_model() for i in self.namePersonalisationSchemes
|
|
134
|
-
)
|
|
135
|
-
structures.extend(i.to_model() for i in self.rulesetSchemes)
|
|
136
142
|
structures.extend(
|
|
137
143
|
i.to_model(
|
|
138
144
|
self.customTypeSchemes,
|
|
@@ -146,9 +152,8 @@ class JsonStructures(Struct, frozen=True, omit_defaults=True):
|
|
|
146
152
|
structures.extend(
|
|
147
153
|
i.to_model() for i in self.userDefinedOperatorSchemes
|
|
148
154
|
)
|
|
149
|
-
for
|
|
150
|
-
|
|
151
|
-
structures.append(rm.to_model(multi))
|
|
155
|
+
structures.extend(i.to_model() for i in self.valueLists)
|
|
156
|
+
structures.extend(i.to_model() for i in self.vtlMappingSchemes)
|
|
152
157
|
return structures
|
|
153
158
|
|
|
154
159
|
@classmethod
|
|
@@ -264,6 +269,33 @@ class JsonStructures(Struct, frozen=True, omit_defaults=True):
|
|
|
264
269
|
hierarchies = tuple(
|
|
265
270
|
[JsonHierarchy.from_model(h) for h in msg.get_hierarchies()]
|
|
266
271
|
)
|
|
272
|
+
constraints = tuple(
|
|
273
|
+
[
|
|
274
|
+
JsonDataConstraint.from_model(c)
|
|
275
|
+
for c in msg.get_data_constraints()
|
|
276
|
+
]
|
|
277
|
+
)
|
|
278
|
+
mpas = tuple(
|
|
279
|
+
[
|
|
280
|
+
JsonMetadataProvisionAgreement.from_model(c)
|
|
281
|
+
for c in msg.get_metadata_provision_agreements()
|
|
282
|
+
]
|
|
283
|
+
)
|
|
284
|
+
mprvs = tuple(
|
|
285
|
+
[
|
|
286
|
+
JsonMetadataProviderScheme.from_model(c)
|
|
287
|
+
for c in msg.get_metadata_provider_schemes()
|
|
288
|
+
]
|
|
289
|
+
)
|
|
290
|
+
mdfs = tuple(
|
|
291
|
+
[JsonMetadataflow.from_model(c) for c in msg.get_metadataflows()]
|
|
292
|
+
)
|
|
293
|
+
msds = tuple(
|
|
294
|
+
[
|
|
295
|
+
JsonMetadataStructure.from_model(c)
|
|
296
|
+
for c in msg.get_metadata_structures()
|
|
297
|
+
]
|
|
298
|
+
)
|
|
267
299
|
return JsonStructures(
|
|
268
300
|
agencySchemes=agencies,
|
|
269
301
|
categorisations=categorisations,
|
|
@@ -271,11 +303,16 @@ class JsonStructures(Struct, frozen=True, omit_defaults=True):
|
|
|
271
303
|
codelists=codelists,
|
|
272
304
|
conceptSchemes=concept_schemes,
|
|
273
305
|
customTypeSchemes=custom_types,
|
|
306
|
+
dataConstraints=constraints,
|
|
274
307
|
dataflows=dataflows,
|
|
275
308
|
dataProviderSchemes=data_providers,
|
|
276
309
|
dataStructures=data_structures,
|
|
277
310
|
hierarchies=hierarchies,
|
|
278
311
|
hierarchyAssociations=hier_associations,
|
|
312
|
+
metadataflows=mdfs,
|
|
313
|
+
metadataProviderSchemes=mprvs,
|
|
314
|
+
metadataProvisionAgreements=mpas,
|
|
315
|
+
metadataStructures=msds,
|
|
279
316
|
namePersonalisationSchemes=name_personalisations,
|
|
280
317
|
provisionAgreements=agreements,
|
|
281
318
|
representationMaps=representations_maps,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Collection of SDMX-JSON schemas for VTL artefacts."""
|
|
2
2
|
|
|
3
|
-
from typing import Literal, Optional, Sequence
|
|
3
|
+
from typing import Dict, Literal, Optional, Sequence
|
|
4
4
|
|
|
5
5
|
from msgspec import Struct
|
|
6
6
|
|
|
@@ -506,33 +506,37 @@ class JsonRulesetScheme(ItemSchemeType, frozen=True, omit_defaults=True):
|
|
|
506
506
|
class JsonToVtlMapping(Struct, frozen=True, omit_defaults=True):
|
|
507
507
|
"""SDMX-JSON payload for To VTL mappings."""
|
|
508
508
|
|
|
509
|
-
toVtlSubSpace: Sequence[str]
|
|
510
|
-
|
|
509
|
+
toVtlSubSpace: Dict[str, Sequence[str]]
|
|
510
|
+
method: Optional[str] = None
|
|
511
511
|
|
|
512
512
|
def to_model(self) -> ToVtlMapping:
|
|
513
513
|
"""Converts deserialized class to pysdmx model class."""
|
|
514
|
-
return ToVtlMapping(self.toVtlSubSpace, self.
|
|
514
|
+
return ToVtlMapping(self.toVtlSubSpace["keys"], self.method)
|
|
515
515
|
|
|
516
516
|
@classmethod
|
|
517
517
|
def from_model(cls, mapping: ToVtlMapping) -> "JsonToVtlMapping":
|
|
518
518
|
"""Converts a pysdmx "to VTL" mapping to an SDMX-JSON one."""
|
|
519
|
-
return JsonToVtlMapping(
|
|
519
|
+
return JsonToVtlMapping(
|
|
520
|
+
{"keys": mapping.to_vtl_sub_space}, mapping.method
|
|
521
|
+
)
|
|
520
522
|
|
|
521
523
|
|
|
522
524
|
class JsonFromVtlMapping(Struct, frozen=True, omit_defaults=True):
|
|
523
525
|
"""SDMX-JSON payload for from VTL mappings."""
|
|
524
526
|
|
|
525
|
-
fromVtlSuperSpace: Sequence[str]
|
|
526
|
-
|
|
527
|
+
fromVtlSuperSpace: Dict[str, Sequence[str]]
|
|
528
|
+
method: Optional[str] = None
|
|
527
529
|
|
|
528
530
|
def to_model(self) -> FromVtlMapping:
|
|
529
531
|
"""Converts deserialized class to pysdmx model class."""
|
|
530
|
-
return FromVtlMapping(self.fromVtlSuperSpace, self.
|
|
532
|
+
return FromVtlMapping(self.fromVtlSuperSpace["keys"], self.method)
|
|
531
533
|
|
|
532
534
|
@classmethod
|
|
533
535
|
def from_model(cls, mapping: FromVtlMapping) -> "JsonFromVtlMapping":
|
|
534
536
|
"""Converts a pysdmx "from VTL" mapping to an SDMX-JSON one."""
|
|
535
|
-
return JsonFromVtlMapping(
|
|
537
|
+
return JsonFromVtlMapping(
|
|
538
|
+
{"keys": mapping.from_vtl_sub_space}, mapping.method
|
|
539
|
+
)
|
|
536
540
|
|
|
537
541
|
|
|
538
542
|
class JsonVtlMapping(NameableType, frozen=True, omit_defaults=True):
|
|
@@ -42,20 +42,31 @@ def check_dimension_at_observation(
|
|
|
42
42
|
return dimension_at_observation
|
|
43
43
|
|
|
44
44
|
|
|
45
|
-
def writing_validation(dataset: PandasDataset) ->
|
|
46
|
-
"""Structural validation of the dataset.
|
|
45
|
+
def writing_validation(dataset: PandasDataset) -> Schema:
|
|
46
|
+
"""Structural validation of the dataset.
|
|
47
|
+
|
|
48
|
+
Args:
|
|
49
|
+
dataset: The dataset to validate.
|
|
50
|
+
|
|
51
|
+
Returns:
|
|
52
|
+
The `Schema` from the dataset.
|
|
53
|
+
|
|
54
|
+
Raises:
|
|
55
|
+
Invalid: If the structure is not a `Schema` or validation fails.
|
|
56
|
+
"""
|
|
47
57
|
if not isinstance(dataset.structure, Schema):
|
|
48
58
|
raise Invalid(
|
|
49
59
|
"Dataset Structure is not a Schema. Cannot perform operation."
|
|
50
60
|
)
|
|
61
|
+
schema = dataset.structure
|
|
51
62
|
required_components = [
|
|
52
63
|
comp.id
|
|
53
|
-
for comp in
|
|
64
|
+
for comp in schema.components
|
|
54
65
|
if comp.role in (Role.DIMENSION, Role.MEASURE)
|
|
55
66
|
]
|
|
56
67
|
required_components.extend(
|
|
57
68
|
att.id
|
|
58
|
-
for att in
|
|
69
|
+
for att in schema.components.attributes
|
|
59
70
|
if (
|
|
60
71
|
att.required
|
|
61
72
|
and att.attachment_level is not None
|
|
@@ -64,7 +75,7 @@ def writing_validation(dataset: PandasDataset) -> None:
|
|
|
64
75
|
)
|
|
65
76
|
non_required = [
|
|
66
77
|
comp.id
|
|
67
|
-
for comp in
|
|
78
|
+
for comp in schema.components
|
|
68
79
|
if comp.id not in required_components
|
|
69
80
|
]
|
|
70
81
|
# Columns match components
|
|
@@ -80,9 +91,11 @@ def writing_validation(dataset: PandasDataset) -> None:
|
|
|
80
91
|
f"Difference: {', '.join(difference)}"
|
|
81
92
|
)
|
|
82
93
|
# Check if the dataset has at least one dimension and one measure
|
|
83
|
-
if not
|
|
94
|
+
if not schema.components.dimensions:
|
|
84
95
|
raise Invalid(
|
|
85
96
|
"The dataset structure must have at least one dimension."
|
|
86
97
|
)
|
|
87
|
-
if not
|
|
98
|
+
if not schema.components.measures:
|
|
88
99
|
raise Invalid("The dataset structure must have at least one measure.")
|
|
100
|
+
|
|
101
|
+
return schema
|