pysdmx 1.7.0__py3-none-any.whl → 1.8.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/__init__.py +266 -0
- pysdmx/api/qb/refmeta.py +1 -1
- pysdmx/api/qb/schema.py +4 -10
- pysdmx/api/qb/service.py +26 -4
- pysdmx/api/qb/structure.py +2 -2
- pysdmx/io/json/fusion/messages/__init__.py +14 -0
- pysdmx/io/json/fusion/messages/dsd.py +52 -1
- pysdmx/io/json/fusion/messages/metadataflow.py +44 -0
- pysdmx/io/json/fusion/messages/mpa.py +45 -0
- pysdmx/io/json/fusion/messages/msd.py +121 -0
- pysdmx/io/json/fusion/messages/org.py +90 -0
- pysdmx/io/json/fusion/reader/__init__.py +5 -0
- pysdmx/io/json/sdmxjson2/messages/__init__.py +15 -1
- pysdmx/io/json/sdmxjson2/messages/dsd.py +9 -6
- pysdmx/io/json/sdmxjson2/messages/metadataflow.py +88 -0
- pysdmx/io/json/sdmxjson2/messages/mpa.py +88 -0
- pysdmx/io/json/sdmxjson2/messages/msd.py +241 -0
- pysdmx/io/json/sdmxjson2/messages/provider.py +117 -1
- pysdmx/io/json/sdmxjson2/messages/structure.py +25 -1
- pysdmx/io/json/sdmxjson2/reader/__init__.py +5 -0
- pysdmx/io/serde.py +5 -0
- pysdmx/io/writer.py +2 -4
- pysdmx/io/xml/__structure_aux_writer.py +9 -9
- pysdmx/io/xml/__write_data_aux.py +1 -2
- pysdmx/model/__init__.py +12 -1
- pysdmx/model/dataflow.py +1 -0
- pysdmx/model/map.py +3 -1
- pysdmx/model/message.py +29 -2
- pysdmx/model/metadata.py +255 -4
- {pysdmx-1.7.0.dist-info → pysdmx-1.8.0.dist-info}/METADATA +1 -1
- {pysdmx-1.7.0.dist-info → pysdmx-1.8.0.dist-info}/RECORD +34 -28
- {pysdmx-1.7.0.dist-info → pysdmx-1.8.0.dist-info}/WHEEL +0 -0
- {pysdmx-1.7.0.dist-info → pysdmx-1.8.0.dist-info}/licenses/LICENSE +0 -0
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
"""Collection of SDMX-JSON schemas for SDMX-REST MSD queries."""
|
|
2
|
+
|
|
3
|
+
from typing import List, Literal, Optional, Sequence, Union
|
|
4
|
+
|
|
5
|
+
from msgspec import Struct
|
|
6
|
+
|
|
7
|
+
from pysdmx import errors
|
|
8
|
+
from pysdmx.io.json.sdmxjson2.messages.code import JsonCodelist, JsonValuelist
|
|
9
|
+
from pysdmx.io.json.sdmxjson2.messages.concept import JsonConceptScheme
|
|
10
|
+
from pysdmx.io.json.sdmxjson2.messages.core import (
|
|
11
|
+
JsonAnnotation,
|
|
12
|
+
JsonRepresentation,
|
|
13
|
+
MaintainableType,
|
|
14
|
+
)
|
|
15
|
+
from pysdmx.io.json.sdmxjson2.messages.dsd import (
|
|
16
|
+
_find_concept,
|
|
17
|
+
_get_concept_reference,
|
|
18
|
+
_get_json_representation,
|
|
19
|
+
_get_representation,
|
|
20
|
+
)
|
|
21
|
+
from pysdmx.model import (
|
|
22
|
+
Agency,
|
|
23
|
+
ArrayBoundaries,
|
|
24
|
+
Codelist,
|
|
25
|
+
MetadataComponent,
|
|
26
|
+
MetadataStructure,
|
|
27
|
+
)
|
|
28
|
+
from pysdmx.util import parse_item_urn
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class JsonMetadataAttribute(Struct, frozen=True, omit_defaults=True):
|
|
32
|
+
"""SDMX-JSON payload for an attribute."""
|
|
33
|
+
|
|
34
|
+
id: str
|
|
35
|
+
conceptIdentity: str
|
|
36
|
+
minOccurs: int
|
|
37
|
+
maxOccurs: Union[int, Literal["unbounded"]]
|
|
38
|
+
isPresentational: bool
|
|
39
|
+
localRepresentation: Optional[JsonRepresentation] = None
|
|
40
|
+
|
|
41
|
+
def to_model(
|
|
42
|
+
self, cs: Sequence[JsonConceptScheme], cls: Sequence[Codelist]
|
|
43
|
+
) -> MetadataComponent:
|
|
44
|
+
"""Returns a metadata component."""
|
|
45
|
+
c = (
|
|
46
|
+
_find_concept(cs, self.conceptIdentity).to_model(cls)
|
|
47
|
+
if cs
|
|
48
|
+
else parse_item_urn(self.conceptIdentity)
|
|
49
|
+
)
|
|
50
|
+
dt, facets, codes, _ = _get_representation(
|
|
51
|
+
self.id, self.localRepresentation, cls, {}
|
|
52
|
+
)
|
|
53
|
+
|
|
54
|
+
if self.localRepresentation and self.localRepresentation.enumeration:
|
|
55
|
+
local_enum_ref = self.localRepresentation.enumeration
|
|
56
|
+
else:
|
|
57
|
+
local_enum_ref = None
|
|
58
|
+
|
|
59
|
+
if self.maxOccurs == "unbounded":
|
|
60
|
+
ab = ArrayBoundaries(self.minOccurs)
|
|
61
|
+
elif self.maxOccurs > 1:
|
|
62
|
+
ab = ArrayBoundaries(self.minOccurs, self.maxOccurs)
|
|
63
|
+
else:
|
|
64
|
+
ab = None
|
|
65
|
+
|
|
66
|
+
return MetadataComponent(
|
|
67
|
+
id=self.id,
|
|
68
|
+
is_presentational=self.isPresentational,
|
|
69
|
+
concept=c,
|
|
70
|
+
local_dtype=dt,
|
|
71
|
+
local_facets=facets,
|
|
72
|
+
local_codes=codes,
|
|
73
|
+
array_def=ab,
|
|
74
|
+
local_enum_ref=local_enum_ref,
|
|
75
|
+
components=(),
|
|
76
|
+
)
|
|
77
|
+
|
|
78
|
+
@classmethod
|
|
79
|
+
def from_model(self, cmp: MetadataComponent) -> "JsonMetadataAttribute":
|
|
80
|
+
"""Converts a pysdmx metadata attribute to an SDMX-JSON one."""
|
|
81
|
+
concept = _get_concept_reference(cmp)
|
|
82
|
+
repr = _get_json_representation(cmp)
|
|
83
|
+
|
|
84
|
+
min_occurs = cmp.array_def.min_size if cmp.array_def else 0
|
|
85
|
+
if cmp.array_def is None or cmp.array_def.max_size is None:
|
|
86
|
+
max_occurs: Union[int, Literal["unbounded"]] = "unbounded"
|
|
87
|
+
else:
|
|
88
|
+
max_occurs = cmp.array_def.max_size
|
|
89
|
+
|
|
90
|
+
return JsonMetadataAttribute(
|
|
91
|
+
id=cmp.id,
|
|
92
|
+
conceptIdentity=concept,
|
|
93
|
+
localRepresentation=repr,
|
|
94
|
+
minOccurs=min_occurs,
|
|
95
|
+
maxOccurs=max_occurs,
|
|
96
|
+
isPresentational=cmp.is_presentational,
|
|
97
|
+
)
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
class JsonMetadataAttributes(Struct, frozen=True, omit_defaults=True):
|
|
101
|
+
"""SDMX-JSON payload for the list of metadata attributes."""
|
|
102
|
+
|
|
103
|
+
id: Literal["MetadataAttributeDescriptor"] = "MetadataAttributeDescriptor"
|
|
104
|
+
metadataAttributes: Sequence[JsonMetadataAttribute] = ()
|
|
105
|
+
|
|
106
|
+
def to_model(
|
|
107
|
+
self, cs: Sequence[JsonConceptScheme], cls: Sequence[Codelist]
|
|
108
|
+
) -> List[MetadataComponent]:
|
|
109
|
+
"""Returns the list of metadata attributes."""
|
|
110
|
+
return [a.to_model(cs, cls) for a in self.metadataAttributes]
|
|
111
|
+
|
|
112
|
+
@classmethod
|
|
113
|
+
def from_model(
|
|
114
|
+
self, attributes: Sequence[MetadataComponent]
|
|
115
|
+
) -> "JsonMetadataAttributes":
|
|
116
|
+
"""Converts a pysdmx list of metadata attributes to SDMX-JSON."""
|
|
117
|
+
return JsonMetadataAttributes(
|
|
118
|
+
metadataAttributes=[
|
|
119
|
+
JsonMetadataAttribute.from_model(a) for a in attributes
|
|
120
|
+
]
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
|
|
124
|
+
class JsonMetadataComponents(Struct, frozen=True, omit_defaults=True):
|
|
125
|
+
"""SDMX-JSON payload for the list of DSD components."""
|
|
126
|
+
|
|
127
|
+
metadataAttributeList: Optional[JsonMetadataAttributes] = None
|
|
128
|
+
|
|
129
|
+
def to_model(
|
|
130
|
+
self,
|
|
131
|
+
cs: Sequence[JsonConceptScheme],
|
|
132
|
+
cls: Sequence[JsonCodelist],
|
|
133
|
+
vls: Sequence[JsonValuelist],
|
|
134
|
+
) -> Sequence[MetadataComponent]:
|
|
135
|
+
"""Returns the components for this DSD."""
|
|
136
|
+
enums = [cl.to_model() for cl in cls]
|
|
137
|
+
enums.extend([vl.to_model() for vl in vls])
|
|
138
|
+
comps = (
|
|
139
|
+
self.metadataAttributeList.to_model(cs, enums)
|
|
140
|
+
if self.metadataAttributeList
|
|
141
|
+
else []
|
|
142
|
+
)
|
|
143
|
+
return comps
|
|
144
|
+
|
|
145
|
+
@classmethod
|
|
146
|
+
def from_model(
|
|
147
|
+
self, components: Sequence[MetadataComponent]
|
|
148
|
+
) -> "JsonMetadataComponents":
|
|
149
|
+
"""Converts a pysdmx components list to an SDMX-JSON one."""
|
|
150
|
+
attributes = JsonMetadataAttributes.from_model(components)
|
|
151
|
+
return JsonMetadataComponents(metadataAttributeList=attributes)
|
|
152
|
+
|
|
153
|
+
|
|
154
|
+
class JsonMetadataStructure(MaintainableType, frozen=True, omit_defaults=True):
|
|
155
|
+
"""SDMX-JSON payload for a DSD."""
|
|
156
|
+
|
|
157
|
+
metadataStructureComponents: Optional[JsonMetadataComponents] = None
|
|
158
|
+
|
|
159
|
+
def to_model(
|
|
160
|
+
self,
|
|
161
|
+
cs: Sequence[JsonConceptScheme],
|
|
162
|
+
cls: Sequence[JsonCodelist],
|
|
163
|
+
vls: Sequence[JsonValuelist],
|
|
164
|
+
) -> MetadataStructure:
|
|
165
|
+
"""Map to pysdmx model class."""
|
|
166
|
+
c = self.metadataStructureComponents.to_model( # type: ignore[union-attr]
|
|
167
|
+
cs,
|
|
168
|
+
cls,
|
|
169
|
+
vls,
|
|
170
|
+
)
|
|
171
|
+
return MetadataStructure(
|
|
172
|
+
id=self.id,
|
|
173
|
+
name=self.name,
|
|
174
|
+
agency=self.agency,
|
|
175
|
+
description=self.description,
|
|
176
|
+
version=self.version,
|
|
177
|
+
annotations=[a.to_model() for a in self.annotations],
|
|
178
|
+
is_external_reference=self.isExternalReference,
|
|
179
|
+
valid_from=self.validFrom,
|
|
180
|
+
valid_to=self.validTo,
|
|
181
|
+
components=c,
|
|
182
|
+
)
|
|
183
|
+
|
|
184
|
+
@classmethod
|
|
185
|
+
def from_model(self, msd: MetadataStructure) -> "JsonMetadataStructure":
|
|
186
|
+
"""Converts a pysdmx MSD to an SDMX-JSON one."""
|
|
187
|
+
if not msd.name:
|
|
188
|
+
raise errors.Invalid(
|
|
189
|
+
"Invalid input",
|
|
190
|
+
"SDMX-JSON metadata structures must have a name",
|
|
191
|
+
{"metadata_structure": msd.id},
|
|
192
|
+
)
|
|
193
|
+
|
|
194
|
+
return JsonMetadataStructure(
|
|
195
|
+
agency=(
|
|
196
|
+
msd.agency.id if isinstance(msd.agency, Agency) else msd.agency
|
|
197
|
+
),
|
|
198
|
+
id=msd.id,
|
|
199
|
+
name=msd.name,
|
|
200
|
+
version=msd.version,
|
|
201
|
+
isExternalReference=msd.is_external_reference,
|
|
202
|
+
validFrom=msd.valid_from,
|
|
203
|
+
validTo=msd.valid_to,
|
|
204
|
+
description=msd.description,
|
|
205
|
+
annotations=tuple(
|
|
206
|
+
[JsonAnnotation.from_model(a) for a in msd.annotations]
|
|
207
|
+
),
|
|
208
|
+
metadataStructureComponents=JsonMetadataComponents.from_model(
|
|
209
|
+
msd.components
|
|
210
|
+
),
|
|
211
|
+
)
|
|
212
|
+
|
|
213
|
+
|
|
214
|
+
class JsonMetadataStructures(Struct, frozen=True, omit_defaults=True):
|
|
215
|
+
"""SDMX-JSON payload for metadata structures."""
|
|
216
|
+
|
|
217
|
+
metadataStructures: Sequence[JsonMetadataStructure]
|
|
218
|
+
conceptSchemes: Sequence[JsonConceptScheme] = ()
|
|
219
|
+
valuelists: Sequence[JsonValuelist] = ()
|
|
220
|
+
codelists: Sequence[JsonCodelist] = ()
|
|
221
|
+
|
|
222
|
+
def to_model(self) -> Sequence[MetadataStructure]:
|
|
223
|
+
"""Returns the requested msds."""
|
|
224
|
+
return [
|
|
225
|
+
msd.to_model(
|
|
226
|
+
self.conceptSchemes,
|
|
227
|
+
self.codelists,
|
|
228
|
+
self.valuelists,
|
|
229
|
+
)
|
|
230
|
+
for msd in self.metadataStructures
|
|
231
|
+
]
|
|
232
|
+
|
|
233
|
+
|
|
234
|
+
class JsonMetadataStructuresMessage(Struct, frozen=True, omit_defaults=True):
|
|
235
|
+
"""SDMX-JSON payload for /metadatastructure queries."""
|
|
236
|
+
|
|
237
|
+
data: JsonMetadataStructures
|
|
238
|
+
|
|
239
|
+
def to_model(self) -> Sequence[MetadataStructure]:
|
|
240
|
+
"""Returns the requested metadata structures."""
|
|
241
|
+
return self.data.to_model()
|
|
@@ -9,8 +9,18 @@ from pysdmx.io.json.sdmxjson2.messages.core import (
|
|
|
9
9
|
ItemSchemeType,
|
|
10
10
|
JsonAnnotation,
|
|
11
11
|
)
|
|
12
|
+
from pysdmx.io.json.sdmxjson2.messages.mpa import (
|
|
13
|
+
JsonMetadataProvisionAgreement,
|
|
14
|
+
)
|
|
12
15
|
from pysdmx.io.json.sdmxjson2.messages.pa import JsonProvisionAgreement
|
|
13
|
-
from pysdmx.model import
|
|
16
|
+
from pysdmx.model import (
|
|
17
|
+
Agency,
|
|
18
|
+
DataflowRef,
|
|
19
|
+
DataProvider,
|
|
20
|
+
DataProviderScheme,
|
|
21
|
+
MetadataProvider,
|
|
22
|
+
MetadataProviderScheme,
|
|
23
|
+
)
|
|
14
24
|
from pysdmx.util import parse_item_urn, parse_urn
|
|
15
25
|
|
|
16
26
|
|
|
@@ -114,3 +124,109 @@ class JsonProviderMessage(Struct, frozen=True, omit_defaults=True):
|
|
|
114
124
|
def to_model(self) -> Sequence[DataProviderScheme]:
|
|
115
125
|
"""Returns the requested list of data provider schemes."""
|
|
116
126
|
return self.data.to_model()
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
class JsonMetadataProviderScheme(
|
|
130
|
+
ItemSchemeType, frozen=True, omit_defaults=True
|
|
131
|
+
):
|
|
132
|
+
"""SDMX-JSON payload for a metadata provider scheme."""
|
|
133
|
+
|
|
134
|
+
metadataProviders: Sequence[MetadataProvider] = ()
|
|
135
|
+
|
|
136
|
+
def __get_df_ref(self, ref: str) -> DataflowRef:
|
|
137
|
+
a = parse_urn(ref)
|
|
138
|
+
return DataflowRef(id=a.id, agency=a.agency, version=a.version)
|
|
139
|
+
|
|
140
|
+
def to_model(
|
|
141
|
+
self, pas: Sequence[JsonMetadataProvisionAgreement]
|
|
142
|
+
) -> MetadataProviderScheme:
|
|
143
|
+
"""Converts a JsonMetadataProviderScheme to a pysdmx one."""
|
|
144
|
+
if pas:
|
|
145
|
+
paprs: Dict[str, Set[DataflowRef]] = defaultdict(set)
|
|
146
|
+
for pa in pas:
|
|
147
|
+
df = self.__get_df_ref(pa.metadataflow)
|
|
148
|
+
ref = parse_item_urn(pa.metadataProvider)
|
|
149
|
+
paprs[f"{ref.agency}:{ref.item_id}"].add(df)
|
|
150
|
+
provs = [
|
|
151
|
+
MetadataProvider(
|
|
152
|
+
id=p.id,
|
|
153
|
+
name=p.name,
|
|
154
|
+
description=p.description,
|
|
155
|
+
contacts=p.contacts,
|
|
156
|
+
dataflows=list(paprs[f"{self.agency}:{p.id}"]),
|
|
157
|
+
annotations=tuple(
|
|
158
|
+
[a.to_model() for a in self.annotations]
|
|
159
|
+
),
|
|
160
|
+
)
|
|
161
|
+
for p in self.metadataProviders
|
|
162
|
+
]
|
|
163
|
+
else:
|
|
164
|
+
provs = [
|
|
165
|
+
MetadataProvider(
|
|
166
|
+
id=p.id,
|
|
167
|
+
name=p.name,
|
|
168
|
+
description=p.description,
|
|
169
|
+
contacts=p.contacts,
|
|
170
|
+
annotations=tuple(
|
|
171
|
+
[a.to_model() for a in self.annotations]
|
|
172
|
+
),
|
|
173
|
+
)
|
|
174
|
+
for p in self.metadataProviders
|
|
175
|
+
]
|
|
176
|
+
return MetadataProviderScheme(
|
|
177
|
+
agency=self.agency,
|
|
178
|
+
description=self.description,
|
|
179
|
+
items=provs,
|
|
180
|
+
annotations=[a.to_model() for a in self.annotations],
|
|
181
|
+
is_external_reference=self.isExternalReference,
|
|
182
|
+
is_partial=self.isPartial,
|
|
183
|
+
valid_from=self.validFrom,
|
|
184
|
+
valid_to=self.validTo,
|
|
185
|
+
)
|
|
186
|
+
|
|
187
|
+
@classmethod
|
|
188
|
+
def from_model(
|
|
189
|
+
self, dps: MetadataProviderScheme
|
|
190
|
+
) -> "JsonMetadataProviderScheme":
|
|
191
|
+
"""Converts a pysdmx metadata provider scheme to an SDMX-JSON one."""
|
|
192
|
+
return JsonMetadataProviderScheme(
|
|
193
|
+
id="METADATA_PROVIDERS",
|
|
194
|
+
name="METADATA_PROVIDERS",
|
|
195
|
+
agency=(
|
|
196
|
+
dps.agency.id if isinstance(dps.agency, Agency) else dps.agency
|
|
197
|
+
),
|
|
198
|
+
description=dps.description,
|
|
199
|
+
version="1.0",
|
|
200
|
+
metadataProviders=dps.items,
|
|
201
|
+
annotations=tuple(
|
|
202
|
+
[JsonAnnotation.from_model(a) for a in dps.annotations]
|
|
203
|
+
),
|
|
204
|
+
isExternalReference=dps.is_external_reference,
|
|
205
|
+
isPartial=dps.is_partial,
|
|
206
|
+
validFrom=dps.valid_from,
|
|
207
|
+
validTo=dps.valid_to,
|
|
208
|
+
)
|
|
209
|
+
|
|
210
|
+
|
|
211
|
+
class JsonMetadataProviderSchemes(Struct, frozen=True, omit_defaults=True):
|
|
212
|
+
"""SDMX-JSON payload for the list of metadata provider schemes."""
|
|
213
|
+
|
|
214
|
+
metadataProviderSchemes: Sequence[JsonMetadataProviderScheme]
|
|
215
|
+
metadataProvisionAgreements: Sequence[JsonMetadataProvisionAgreement] = ()
|
|
216
|
+
|
|
217
|
+
def to_model(self) -> Sequence[MetadataProviderScheme]:
|
|
218
|
+
"""Converts a JsonMetadataProviderSchemes to the pysdmx model."""
|
|
219
|
+
return [
|
|
220
|
+
s.to_model(self.metadataProvisionAgreements)
|
|
221
|
+
for s in self.metadataProviderSchemes
|
|
222
|
+
]
|
|
223
|
+
|
|
224
|
+
|
|
225
|
+
class JsonMetadataProviderMessage(Struct, frozen=True, omit_defaults=True):
|
|
226
|
+
"""SDMX-JSON payload for /metadataproviderscheme queries."""
|
|
227
|
+
|
|
228
|
+
data: JsonMetadataProviderSchemes
|
|
229
|
+
|
|
230
|
+
def to_model(self) -> Sequence[MetadataProviderScheme]:
|
|
231
|
+
"""Returns the requested list of metadata provider schemes."""
|
|
232
|
+
return self.data.to_model()
|
|
@@ -24,8 +24,16 @@ from pysdmx.io.json.sdmxjson2.messages.map import (
|
|
|
24
24
|
JsonRepresentationMap,
|
|
25
25
|
JsonStructureMap,
|
|
26
26
|
)
|
|
27
|
+
from pysdmx.io.json.sdmxjson2.messages.metadataflow import JsonMetadataflow
|
|
28
|
+
from pysdmx.io.json.sdmxjson2.messages.mpa import (
|
|
29
|
+
JsonMetadataProvisionAgreement,
|
|
30
|
+
)
|
|
31
|
+
from pysdmx.io.json.sdmxjson2.messages.msd import JsonMetadataStructure
|
|
27
32
|
from pysdmx.io.json.sdmxjson2.messages.pa import JsonProvisionAgreement
|
|
28
|
-
from pysdmx.io.json.sdmxjson2.messages.provider import
|
|
33
|
+
from pysdmx.io.json.sdmxjson2.messages.provider import (
|
|
34
|
+
JsonDataProviderScheme,
|
|
35
|
+
JsonMetadataProviderScheme,
|
|
36
|
+
)
|
|
29
37
|
from pysdmx.io.json.sdmxjson2.messages.vtl import (
|
|
30
38
|
JsonCustomTypeScheme,
|
|
31
39
|
JsonNamePersonalisationScheme,
|
|
@@ -50,8 +58,12 @@ class JsonStructures(Struct, frozen=True, omit_defaults=True):
|
|
|
50
58
|
hierarchyAssociations: Sequence[JsonHierarchyAssociation] = ()
|
|
51
59
|
agencySchemes: Sequence[JsonAgencyScheme] = ()
|
|
52
60
|
dataProviderSchemes: Sequence[JsonDataProviderScheme] = ()
|
|
61
|
+
metadataProviderSchemes: Sequence[JsonMetadataProviderScheme] = ()
|
|
53
62
|
dataflows: Sequence[JsonDataflow] = ()
|
|
54
63
|
provisionAgreements: Sequence[JsonProvisionAgreement] = ()
|
|
64
|
+
metadataflows: Sequence[JsonMetadataflow] = ()
|
|
65
|
+
metadataProvisionAgreements: Sequence[JsonMetadataProvisionAgreement] = ()
|
|
66
|
+
metadataStructures: Sequence[JsonMetadataStructure] = ()
|
|
55
67
|
structureMaps: Sequence[JsonStructureMap] = ()
|
|
56
68
|
representationMaps: Sequence[JsonRepresentationMap] = ()
|
|
57
69
|
categorisations: Sequence[JsonCategorisation] = ()
|
|
@@ -89,6 +101,14 @@ class JsonStructures(Struct, frozen=True, omit_defaults=True):
|
|
|
89
101
|
i.to_model(self.provisionAgreements)
|
|
90
102
|
for i in self.dataProviderSchemes
|
|
91
103
|
)
|
|
104
|
+
structures.extend(
|
|
105
|
+
i.to_model(self.metadataProvisionAgreements)
|
|
106
|
+
for i in self.metadataProviderSchemes
|
|
107
|
+
)
|
|
108
|
+
structures.extend(
|
|
109
|
+
i.to_model(self.conceptSchemes, self.codelists, self.valueLists)
|
|
110
|
+
for i in self.metadataStructures
|
|
111
|
+
)
|
|
92
112
|
structures.extend(
|
|
93
113
|
i.to_model(
|
|
94
114
|
self.dataStructures,
|
|
@@ -99,6 +119,10 @@ class JsonStructures(Struct, frozen=True, omit_defaults=True):
|
|
|
99
119
|
for i in self.dataflows
|
|
100
120
|
)
|
|
101
121
|
structures.extend(i.to_model() for i in self.provisionAgreements)
|
|
122
|
+
structures.extend(i.to_model() for i in self.metadataflows)
|
|
123
|
+
structures.extend(
|
|
124
|
+
i.to_model() for i in self.metadataProvisionAgreements
|
|
125
|
+
)
|
|
102
126
|
structures.extend(
|
|
103
127
|
i.to_model(self.representationMaps) for i in self.structureMaps
|
|
104
128
|
)
|
|
@@ -20,4 +20,9 @@ deserializers = Deserializers(
|
|
|
20
20
|
mapping=msg.JsonMappingMessage, # type: ignore[arg-type]
|
|
21
21
|
code_map=msg.JsonRepresentationMapMessage, # type: ignore[arg-type]
|
|
22
22
|
transformation_scheme=msg.JsonTransfoMsg, # type: ignore[arg-type]
|
|
23
|
+
metadataflows=msg.JsonMdfsMsg, # type: ignore[arg-type]
|
|
24
|
+
metadata_provision_agreement=msg.JsonMPAMsg, # type: ignore[arg-type]
|
|
25
|
+
metadata_providers=msg.JsonMetadataProviderMessage, # type: ignore[arg-type]
|
|
26
|
+
msds=msg.JsonMetadataStructuresMessage, # type: ignore[arg-type]
|
|
27
|
+
data_structures=msg.JsonDataStructuresMessage, # type: ignore[arg-type]
|
|
23
28
|
)
|
pysdmx/io/serde.py
CHANGED
|
@@ -41,6 +41,11 @@ class Deserializers:
|
|
|
41
41
|
mapping: Deserializer
|
|
42
42
|
code_map: Deserializer
|
|
43
43
|
transformation_scheme: Deserializer
|
|
44
|
+
metadataflows: Deserializer
|
|
45
|
+
metadata_provision_agreement: Deserializer
|
|
46
|
+
metadata_providers: Deserializer
|
|
47
|
+
msds: Deserializer
|
|
48
|
+
data_structures: Deserializer
|
|
44
49
|
|
|
45
50
|
|
|
46
51
|
@dataclass
|
pysdmx/io/writer.py
CHANGED
|
@@ -19,11 +19,9 @@ WRITERS = {
|
|
|
19
19
|
Format.DATA_SDMX_ML_2_1_STR: "pysdmx.io.xml.sdmx21.writer."
|
|
20
20
|
"structure_specific",
|
|
21
21
|
Format.STRUCTURE_SDMX_ML_2_1: "pysdmx.io.xml.sdmx21.writer.structure",
|
|
22
|
-
Format.DATA_SDMX_ML_3_0: "pysdmx.io.xml.sdmx30.writer."
|
|
23
|
-
"structure_specific",
|
|
22
|
+
Format.DATA_SDMX_ML_3_0: "pysdmx.io.xml.sdmx30.writer.structure_specific",
|
|
24
23
|
Format.STRUCTURE_SDMX_ML_3_0: "pysdmx.io.xml.sdmx30.writer.structure",
|
|
25
|
-
Format.DATA_SDMX_ML_3_1: "pysdmx.io.xml.sdmx31.writer."
|
|
26
|
-
"structure_specific",
|
|
24
|
+
Format.DATA_SDMX_ML_3_1: "pysdmx.io.xml.sdmx31.writer.structure_specific",
|
|
27
25
|
Format.STRUCTURE_SDMX_ML_3_1: "pysdmx.io.xml.sdmx31.writer.structure",
|
|
28
26
|
Format.STRUCTURE_SDMX_JSON_2_0_0: (
|
|
29
27
|
"pysdmx.io.json.sdmxjson2.writer.structure"
|
|
@@ -310,9 +310,9 @@ def __write_maintainable(
|
|
|
310
310
|
f"{str(maintainable.is_external_reference).lower()!r}"
|
|
311
311
|
)
|
|
312
312
|
if not references_30 and not (isinstance(maintainable, AgencyScheme)):
|
|
313
|
-
outfile[
|
|
314
|
-
"
|
|
315
|
-
|
|
313
|
+
outfile["Attributes"] += (
|
|
314
|
+
f" isFinal={str(maintainable.is_final).lower()!r}"
|
|
315
|
+
)
|
|
316
316
|
|
|
317
317
|
if isinstance(maintainable.agency, str):
|
|
318
318
|
outfile["Attributes"] += f" agencyID={maintainable.agency!r}"
|
|
@@ -809,9 +809,9 @@ def __write_scheme( # noqa: C901
|
|
|
809
809
|
DSD,
|
|
810
810
|
DFW,
|
|
811
811
|
]:
|
|
812
|
-
data[
|
|
813
|
-
"
|
|
814
|
-
|
|
812
|
+
data["Attributes"] += (
|
|
813
|
+
f" isPartial={str(item_scheme.is_partial).lower()!r}"
|
|
814
|
+
)
|
|
815
815
|
if scheme in [
|
|
816
816
|
RULE_SCHEME,
|
|
817
817
|
UDO_SCHEME,
|
|
@@ -820,9 +820,9 @@ def __write_scheme( # noqa: C901
|
|
|
820
820
|
CUSTOM_TYPE_SCHEME,
|
|
821
821
|
NAME_PER_SCHEME,
|
|
822
822
|
]:
|
|
823
|
-
data[
|
|
824
|
-
"
|
|
825
|
-
|
|
823
|
+
data["Attributes"] += (
|
|
824
|
+
f" {_write_vtl(item_scheme, indent, references_30)}"
|
|
825
|
+
)
|
|
826
826
|
|
|
827
827
|
outfile = ""
|
|
828
828
|
|
|
@@ -33,8 +33,7 @@ def check_dimension_at_observation(
|
|
|
33
33
|
dimension_codes = [dim.id for dim in components.dimensions]
|
|
34
34
|
if value not in dimension_codes:
|
|
35
35
|
raise Invalid(
|
|
36
|
-
f"Dimension at observation {value} "
|
|
37
|
-
f"not found in dataset {key}."
|
|
36
|
+
f"Dimension at observation {value} not found in dataset {key}."
|
|
38
37
|
)
|
|
39
38
|
# Add the missing datasets on mapping with ALL_DIM
|
|
40
39
|
for key in datasets:
|
pysdmx/model/__init__.py
CHANGED
|
@@ -53,7 +53,14 @@ from pysdmx.model.map import (
|
|
|
53
53
|
StructureMap,
|
|
54
54
|
ValueMap,
|
|
55
55
|
)
|
|
56
|
-
from pysdmx.model.metadata import
|
|
56
|
+
from pysdmx.model.metadata import (
|
|
57
|
+
MetadataAttribute,
|
|
58
|
+
MetadataComponent,
|
|
59
|
+
Metadataflow,
|
|
60
|
+
MetadataProvisionAgreement,
|
|
61
|
+
MetadataReport,
|
|
62
|
+
MetadataStructure,
|
|
63
|
+
)
|
|
57
64
|
from pysdmx.model.organisation import (
|
|
58
65
|
AgencyScheme,
|
|
59
66
|
DataConsumerScheme,
|
|
@@ -174,9 +181,13 @@ __all__ = [
|
|
|
174
181
|
"ImplicitComponentMap",
|
|
175
182
|
"ItemReference",
|
|
176
183
|
"MetadataAttribute",
|
|
184
|
+
"MetadataComponent",
|
|
185
|
+
"Metadataflow",
|
|
177
186
|
"MetadataProvider",
|
|
178
187
|
"MetadataProviderScheme",
|
|
188
|
+
"MetadataProvisionAgreement",
|
|
179
189
|
"MetadataReport",
|
|
190
|
+
"MetadataStructure",
|
|
180
191
|
"MultiComponentMap",
|
|
181
192
|
"MultiRepresentationMap",
|
|
182
193
|
"MultiValueMap",
|
pysdmx/model/dataflow.py
CHANGED
|
@@ -116,6 +116,7 @@ class Component(
|
|
|
116
116
|
id: A unique identifier for the component (e.g. FREQ).
|
|
117
117
|
required: Whether the component must have a value.
|
|
118
118
|
role: The role played by the component.
|
|
119
|
+
concept: The concept giving its identity to the component.
|
|
119
120
|
local_dtype: The component's local data type (string, number, etc.).
|
|
120
121
|
local_facets: Additional local details such as the component's minimum
|
|
121
122
|
length.
|
pysdmx/model/map.py
CHANGED
|
@@ -486,7 +486,9 @@ class StructureMap(MaintainableArtefact, frozen=True, omit_defaults=True):
|
|
|
486
486
|
"""Return the number of mapping rules in the structure map."""
|
|
487
487
|
return len(self.maps)
|
|
488
488
|
|
|
489
|
-
def __getitem__(
|
|
489
|
+
def __getitem__(
|
|
490
|
+
self, id_: str
|
|
491
|
+
) -> Optional[
|
|
490
492
|
Sequence[
|
|
491
493
|
Union[
|
|
492
494
|
ComponentMap,
|
pysdmx/model/message.py
CHANGED
|
@@ -34,8 +34,17 @@ from pysdmx.model.map import (
|
|
|
34
34
|
RepresentationMap,
|
|
35
35
|
StructureMap,
|
|
36
36
|
)
|
|
37
|
-
from pysdmx.model.metadata import
|
|
38
|
-
|
|
37
|
+
from pysdmx.model.metadata import (
|
|
38
|
+
Metadataflow,
|
|
39
|
+
MetadataProvisionAgreement,
|
|
40
|
+
MetadataReport,
|
|
41
|
+
MetadataStructure,
|
|
42
|
+
)
|
|
43
|
+
from pysdmx.model.organisation import (
|
|
44
|
+
AgencyScheme,
|
|
45
|
+
DataProviderScheme,
|
|
46
|
+
MetadataProviderScheme,
|
|
47
|
+
)
|
|
39
48
|
from pysdmx.model.submission import SubmissionResult
|
|
40
49
|
from pysdmx.model.vtl import (
|
|
41
50
|
CustomTypeScheme,
|
|
@@ -221,6 +230,10 @@ class StructureMessage(Struct, repr_omit_defaults=True, frozen=True):
|
|
|
221
230
|
"""Returns the Dataflows."""
|
|
222
231
|
return self.__get_elements(Dataflow)
|
|
223
232
|
|
|
233
|
+
def get_metadataflows(self) -> List[Metadataflow]:
|
|
234
|
+
"""Returns the MetadataProvisionAgreements."""
|
|
235
|
+
return self.__get_elements(Metadataflow)
|
|
236
|
+
|
|
224
237
|
def get_organisation_scheme(self, short_urn: str) -> AgencyScheme:
|
|
225
238
|
"""Returns a specific OrganisationScheme."""
|
|
226
239
|
return self.__get_single_structure(AgencyScheme, short_urn)
|
|
@@ -281,6 +294,20 @@ class StructureMessage(Struct, repr_omit_defaults=True, frozen=True):
|
|
|
281
294
|
"""Returns the ProvisionAgreements."""
|
|
282
295
|
return self.__get_elements(ProvisionAgreement)
|
|
283
296
|
|
|
297
|
+
def get_metadata_provider_schemes(self) -> List[MetadataProviderScheme]:
|
|
298
|
+
"""Returns the MetadataProviderSchemes."""
|
|
299
|
+
return self.__get_elements(MetadataProviderScheme)
|
|
300
|
+
|
|
301
|
+
def get_metadata_provision_agreements(
|
|
302
|
+
self,
|
|
303
|
+
) -> List[MetadataProvisionAgreement]:
|
|
304
|
+
"""Returns the MetadataProvisionAgreements."""
|
|
305
|
+
return self.__get_elements(MetadataProvisionAgreement)
|
|
306
|
+
|
|
307
|
+
def get_metadata_structures(self) -> List[MetadataStructure]:
|
|
308
|
+
"""Returns the MetadataStructures."""
|
|
309
|
+
return self.__get_elements(MetadataStructure)
|
|
310
|
+
|
|
284
311
|
def get_structure_maps(self) -> List[StructureMap]:
|
|
285
312
|
"""Returns the StructureMaps."""
|
|
286
313
|
return self.__get_elements(StructureMap)
|