pysdmx 1.5.2__py3-none-any.whl → 1.6.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 +8 -3
- pysdmx/api/fmr/maintenance.py +158 -0
- pysdmx/api/qb/structure.py +1 -0
- pysdmx/api/qb/util.py +1 -0
- pysdmx/io/csv/__csv_aux_reader.py +99 -0
- pysdmx/io/csv/__csv_aux_writer.py +118 -0
- pysdmx/io/csv/sdmx10/reader/__init__.py +9 -14
- pysdmx/io/csv/sdmx10/writer/__init__.py +28 -2
- pysdmx/io/csv/sdmx20/__init__.py +0 -9
- pysdmx/io/csv/sdmx20/reader/__init__.py +8 -61
- pysdmx/io/csv/sdmx20/writer/__init__.py +32 -25
- pysdmx/io/csv/sdmx21/__init__.py +1 -0
- pysdmx/io/csv/sdmx21/reader/__init__.py +86 -0
- pysdmx/io/csv/sdmx21/writer/__init__.py +70 -0
- pysdmx/io/format.py +8 -0
- pysdmx/io/input_processor.py +16 -2
- pysdmx/io/json/fusion/messages/code.py +21 -4
- pysdmx/io/json/fusion/messages/concept.py +16 -8
- pysdmx/io/json/fusion/messages/dataflow.py +8 -1
- pysdmx/io/json/fusion/messages/dsd.py +15 -0
- pysdmx/io/json/fusion/messages/schema.py +8 -1
- pysdmx/io/json/sdmxjson2/messages/agency.py +43 -7
- pysdmx/io/json/sdmxjson2/messages/category.py +92 -7
- pysdmx/io/json/sdmxjson2/messages/code.py +239 -18
- pysdmx/io/json/sdmxjson2/messages/concept.py +78 -13
- pysdmx/io/json/sdmxjson2/messages/constraint.py +5 -5
- pysdmx/io/json/sdmxjson2/messages/core.py +121 -14
- pysdmx/io/json/sdmxjson2/messages/dataflow.py +63 -8
- pysdmx/io/json/sdmxjson2/messages/dsd.py +215 -20
- pysdmx/io/json/sdmxjson2/messages/map.py +200 -24
- pysdmx/io/json/sdmxjson2/messages/pa.py +36 -5
- pysdmx/io/json/sdmxjson2/messages/provider.py +35 -7
- pysdmx/io/json/sdmxjson2/messages/report.py +85 -7
- pysdmx/io/json/sdmxjson2/messages/schema.py +11 -12
- pysdmx/io/json/sdmxjson2/messages/structure.py +150 -2
- pysdmx/io/json/sdmxjson2/messages/vtl.py +547 -17
- pysdmx/io/json/sdmxjson2/reader/metadata.py +32 -0
- pysdmx/io/json/sdmxjson2/reader/structure.py +32 -0
- pysdmx/io/json/sdmxjson2/writer/__init__.py +9 -0
- pysdmx/io/json/sdmxjson2/writer/metadata.py +60 -0
- pysdmx/io/json/sdmxjson2/writer/structure.py +61 -0
- pysdmx/io/reader.py +28 -9
- pysdmx/io/serde.py +17 -0
- pysdmx/io/writer.py +45 -9
- pysdmx/io/xml/__write_data_aux.py +1 -54
- pysdmx/io/xml/__write_structure_specific_aux.py +1 -1
- pysdmx/io/xml/sdmx21/writer/generic.py +1 -1
- pysdmx/model/code.py +11 -1
- pysdmx/model/dataflow.py +23 -0
- pysdmx/model/map.py +12 -4
- pysdmx/model/message.py +9 -1
- pysdmx/toolkit/pd/_data_utils.py +100 -0
- pysdmx/toolkit/vtl/_validations.py +2 -3
- {pysdmx-1.5.2.dist-info → pysdmx-1.6.0.dist-info}/METADATA +3 -2
- {pysdmx-1.5.2.dist-info → pysdmx-1.6.0.dist-info}/RECORD +58 -46
- {pysdmx-1.5.2.dist-info → pysdmx-1.6.0.dist-info}/WHEEL +1 -1
- {pysdmx-1.5.2.dist-info → pysdmx-1.6.0.dist-info/licenses}/LICENSE +0 -0
|
@@ -6,11 +6,13 @@ from typing import Any, Dict, Literal, Optional, Sequence, Union
|
|
|
6
6
|
|
|
7
7
|
from msgspec import Struct
|
|
8
8
|
|
|
9
|
+
from pysdmx import errors
|
|
9
10
|
from pysdmx.io.json.sdmxjson2.messages.core import (
|
|
10
11
|
JsonAnnotation,
|
|
11
12
|
MaintainableType,
|
|
12
13
|
)
|
|
13
14
|
from pysdmx.model import (
|
|
15
|
+
Agency,
|
|
14
16
|
ComponentMap,
|
|
15
17
|
DataType,
|
|
16
18
|
DatePatternMap,
|
|
@@ -26,7 +28,7 @@ from pysdmx.model import (
|
|
|
26
28
|
from pysdmx.util import find_by_urn
|
|
27
29
|
|
|
28
30
|
|
|
29
|
-
class JsonSourceValue(Struct, frozen=True):
|
|
31
|
+
class JsonSourceValue(Struct, frozen=True, omit_defaults=True):
|
|
30
32
|
"""SDMX-JSON payload for a source value."""
|
|
31
33
|
|
|
32
34
|
value: str
|
|
@@ -39,8 +41,16 @@ class JsonSourceValue(Struct, frozen=True):
|
|
|
39
41
|
else:
|
|
40
42
|
return self.value
|
|
41
43
|
|
|
44
|
+
@classmethod
|
|
45
|
+
def from_model(self, value: str) -> "JsonSourceValue":
|
|
46
|
+
"""Converts a pysdmx source string value to an SDMX-JSON one."""
|
|
47
|
+
if value.startswith("regex:"):
|
|
48
|
+
return JsonSourceValue(value.replace("regex:", ""), True)
|
|
49
|
+
else:
|
|
50
|
+
return JsonSourceValue(value)
|
|
51
|
+
|
|
42
52
|
|
|
43
|
-
class JsonRepresentationMapping(Struct, frozen=True):
|
|
53
|
+
class JsonRepresentationMapping(Struct, frozen=True, omit_defaults=True):
|
|
44
54
|
"""SDMX-JSON payload for a representation mapping."""
|
|
45
55
|
|
|
46
56
|
sourceValues: Sequence[JsonSourceValue]
|
|
@@ -72,8 +82,28 @@ class JsonRepresentationMapping(Struct, frozen=True):
|
|
|
72
82
|
valid_to=self.__get_dt(self.validTo) if self.validTo else None,
|
|
73
83
|
)
|
|
74
84
|
|
|
85
|
+
@classmethod
|
|
86
|
+
def from_model(
|
|
87
|
+
self, vm: Union[MultiValueMap, ValueMap]
|
|
88
|
+
) -> "JsonRepresentationMapping":
|
|
89
|
+
"""Converts a value map to an SDMX-JSON JsonRepresentationMapping."""
|
|
90
|
+
if isinstance(vm, ValueMap):
|
|
91
|
+
return JsonRepresentationMapping(
|
|
92
|
+
[JsonSourceValue.from_model(vm.source)],
|
|
93
|
+
[vm.target],
|
|
94
|
+
vm.valid_from.strftime("%Y-%m-%d") if vm.valid_from else None,
|
|
95
|
+
vm.valid_to.strftime("%Y-%m-%d") if vm.valid_to else None,
|
|
96
|
+
)
|
|
97
|
+
else:
|
|
98
|
+
return JsonRepresentationMapping(
|
|
99
|
+
[JsonSourceValue.from_model(s) for s in vm.source],
|
|
100
|
+
vm.target,
|
|
101
|
+
vm.valid_from.strftime("%Y-%m-%d") if vm.valid_from else None,
|
|
102
|
+
vm.valid_to.strftime("%Y-%m-%d") if vm.valid_to else None,
|
|
103
|
+
)
|
|
104
|
+
|
|
75
105
|
|
|
76
|
-
class JsonRepresentationMap(MaintainableType, frozen=True):
|
|
106
|
+
class JsonRepresentationMap(MaintainableType, frozen=True, omit_defaults=True):
|
|
77
107
|
"""SDMX-JSON payload for a representation map."""
|
|
78
108
|
|
|
79
109
|
source: Sequence[Dict[str, str]] = ()
|
|
@@ -83,8 +113,6 @@ class JsonRepresentationMap(MaintainableType, frozen=True):
|
|
|
83
113
|
def __parse_st(self, item: Dict[str, str]) -> Union[DataType, str]:
|
|
84
114
|
if "dataType" in item:
|
|
85
115
|
return DataType(item["dataType"])
|
|
86
|
-
elif "valuelist" in item:
|
|
87
|
-
return item["valuelist"]
|
|
88
116
|
else:
|
|
89
117
|
return item["codelist"]
|
|
90
118
|
|
|
@@ -118,8 +146,54 @@ class JsonRepresentationMap(MaintainableType, frozen=True):
|
|
|
118
146
|
version=self.version,
|
|
119
147
|
)
|
|
120
148
|
|
|
149
|
+
@classmethod
|
|
150
|
+
def from_model(
|
|
151
|
+
self, rm: Union[MultiRepresentationMap, RepresentationMap]
|
|
152
|
+
) -> "JsonRepresentationMap":
|
|
153
|
+
"""Converts a pysdmx representation map to an SDMX-JSON one."""
|
|
154
|
+
|
|
155
|
+
def __convert_st(st: str) -> Dict[str, str]:
|
|
156
|
+
if "Codelist" in st or "ValueList" in st:
|
|
157
|
+
return {"codelist": st}
|
|
158
|
+
else:
|
|
159
|
+
return {"dataType": st}
|
|
160
|
+
|
|
161
|
+
if not rm.name:
|
|
162
|
+
raise errors.Invalid(
|
|
163
|
+
"Invalid input",
|
|
164
|
+
"SDMX-JSON representation maps must have a name",
|
|
165
|
+
{"representation_map": rm.id},
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
if isinstance(rm, RepresentationMap):
|
|
169
|
+
source = [__convert_st(rm.source)] if rm.source else []
|
|
170
|
+
target = [__convert_st(rm.target)] if rm.target else []
|
|
171
|
+
else:
|
|
172
|
+
source = [__convert_st(s) for s in rm.source]
|
|
173
|
+
target = [__convert_st(t) for t in rm.target]
|
|
174
|
+
return JsonRepresentationMap(
|
|
175
|
+
agency=(
|
|
176
|
+
rm.agency.id if isinstance(rm.agency, Agency) else rm.agency
|
|
177
|
+
),
|
|
178
|
+
id=rm.id,
|
|
179
|
+
name=rm.name,
|
|
180
|
+
version=rm.version,
|
|
181
|
+
isExternalReference=rm.is_external_reference,
|
|
182
|
+
validFrom=rm.valid_from,
|
|
183
|
+
validTo=rm.valid_to,
|
|
184
|
+
description=rm.description,
|
|
185
|
+
annotations=tuple(
|
|
186
|
+
[JsonAnnotation.from_model(a) for a in rm.annotations]
|
|
187
|
+
),
|
|
188
|
+
source=tuple(source),
|
|
189
|
+
target=tuple(target),
|
|
190
|
+
representationMappings=tuple(
|
|
191
|
+
[JsonRepresentationMapping.from_model(m) for m in rm.maps]
|
|
192
|
+
),
|
|
193
|
+
)
|
|
194
|
+
|
|
121
195
|
|
|
122
|
-
class JsonFixedValueMap(Struct, frozen=True):
|
|
196
|
+
class JsonFixedValueMap(Struct, frozen=True, omit_defaults=True):
|
|
123
197
|
"""SDMX-JSON payload for a fixed value map."""
|
|
124
198
|
|
|
125
199
|
values: Sequence[Any]
|
|
@@ -136,8 +210,17 @@ class JsonFixedValueMap(Struct, frozen=True):
|
|
|
136
210
|
located_in, # type: ignore[arg-type]
|
|
137
211
|
)
|
|
138
212
|
|
|
213
|
+
@classmethod
|
|
214
|
+
def from_model(self, fvm: FixedValueMap) -> "JsonFixedValueMap":
|
|
215
|
+
"""Converts a pysdmx fixed value map to an SDMX-JSON one."""
|
|
216
|
+
return JsonFixedValueMap(
|
|
217
|
+
values=[fvm.value],
|
|
218
|
+
source=fvm.target if fvm.located_in == "source" else None,
|
|
219
|
+
target=fvm.target if fvm.located_in == "target" else None,
|
|
220
|
+
)
|
|
221
|
+
|
|
139
222
|
|
|
140
|
-
class JsonComponentMap(Struct, frozen=True):
|
|
223
|
+
class JsonComponentMap(Struct, frozen=True, omit_defaults=True):
|
|
141
224
|
"""SDMX-JSON payload for a component map."""
|
|
142
225
|
|
|
143
226
|
source: Sequence[str]
|
|
@@ -162,15 +245,43 @@ class JsonComponentMap(Struct, frozen=True):
|
|
|
162
245
|
else:
|
|
163
246
|
return ImplicitComponentMap(self.source[0], self.target[0])
|
|
164
247
|
|
|
248
|
+
@classmethod
|
|
249
|
+
def from_model(
|
|
250
|
+
self, cm: Union[ComponentMap, MultiComponentMap, ImplicitComponentMap]
|
|
251
|
+
) -> "JsonComponentMap":
|
|
252
|
+
"""Converts a pysdmx component map to an SDMX-JSON one."""
|
|
253
|
+
if isinstance(cm, ImplicitComponentMap):
|
|
254
|
+
return JsonComponentMap([cm.source], [cm.target])
|
|
255
|
+
elif isinstance(cm, ComponentMap):
|
|
256
|
+
rm = (
|
|
257
|
+
(
|
|
258
|
+
"urn:sdmx:org.sdmx.infomodel.structuremapping."
|
|
259
|
+
f"{cm.values.short_urn}"
|
|
260
|
+
)
|
|
261
|
+
if isinstance(cm.values, RepresentationMap)
|
|
262
|
+
else cm.values
|
|
263
|
+
)
|
|
264
|
+
return JsonComponentMap([cm.source], [cm.target], rm)
|
|
265
|
+
else:
|
|
266
|
+
rm = (
|
|
267
|
+
(
|
|
268
|
+
"urn:sdmx:org.sdmx.infomodel.structuremapping."
|
|
269
|
+
f"{cm.values.short_urn}"
|
|
270
|
+
)
|
|
271
|
+
if isinstance(cm.values, MultiRepresentationMap)
|
|
272
|
+
else cm.values
|
|
273
|
+
)
|
|
274
|
+
return JsonComponentMap(cm.source, cm.target, rm)
|
|
275
|
+
|
|
165
276
|
|
|
166
|
-
class JsonMappedPair(Struct, frozen=True):
|
|
277
|
+
class JsonMappedPair(Struct, frozen=True, omit_defaults=True):
|
|
167
278
|
"""SDMX-JSON payload for a pair of mapped components."""
|
|
168
279
|
|
|
169
280
|
source: str
|
|
170
281
|
target: str
|
|
171
282
|
|
|
172
283
|
|
|
173
|
-
class JsonDatePatternMap(Struct, frozen=True):
|
|
284
|
+
class JsonDatePatternMap(Struct, frozen=True, omit_defaults=True):
|
|
174
285
|
"""SDMX-JSON payload for a date pattern map."""
|
|
175
286
|
|
|
176
287
|
sourcePattern: str
|
|
@@ -194,18 +305,38 @@ class JsonDatePatternMap(Struct, frozen=True):
|
|
|
194
305
|
)
|
|
195
306
|
typ = "fixed" if self.targetFrequencyID else "variable"
|
|
196
307
|
return DatePatternMap(
|
|
197
|
-
self.mappedComponents[0].source,
|
|
198
|
-
self.mappedComponents[0].target,
|
|
199
|
-
self.sourcePattern,
|
|
200
|
-
freq, # type: ignore[arg-type]
|
|
201
|
-
self.id,
|
|
202
|
-
self.locale,
|
|
203
|
-
typ, # type: ignore[arg-type]
|
|
204
|
-
self.resolvePeriod,
|
|
308
|
+
source=self.mappedComponents[0].source,
|
|
309
|
+
target=self.mappedComponents[0].target,
|
|
310
|
+
pattern=self.sourcePattern,
|
|
311
|
+
frequency=freq, # type: ignore[arg-type]
|
|
312
|
+
id=self.id,
|
|
313
|
+
locale=self.locale,
|
|
314
|
+
pattern_type=typ, # type: ignore[arg-type]
|
|
315
|
+
resolve_period=self.resolvePeriod,
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
@classmethod
|
|
319
|
+
def from_model(self, dpm: DatePatternMap) -> "JsonDatePatternMap":
|
|
320
|
+
"""Converts a pysdmx date pattern map to an SDMX-JSON one."""
|
|
321
|
+
if dpm.pattern_type == "fixed":
|
|
322
|
+
tf = dpm.frequency
|
|
323
|
+
fd = None
|
|
324
|
+
else:
|
|
325
|
+
tf = None
|
|
326
|
+
fd = dpm.frequency
|
|
327
|
+
|
|
328
|
+
return JsonDatePatternMap(
|
|
329
|
+
sourcePattern=dpm.pattern,
|
|
330
|
+
mappedComponents=[JsonMappedPair(dpm.source, dpm.target)],
|
|
331
|
+
locale=dpm.locale,
|
|
332
|
+
id=dpm.id,
|
|
333
|
+
resolvePeriod=dpm.resolve_period,
|
|
334
|
+
targetFrequencyID=tf,
|
|
335
|
+
frequencyDimension=fd,
|
|
205
336
|
)
|
|
206
337
|
|
|
207
338
|
|
|
208
|
-
class JsonStructureMap(MaintainableType, frozen=True):
|
|
339
|
+
class JsonStructureMap(MaintainableType, frozen=True, omit_defaults=True):
|
|
209
340
|
"""SDMX-JSON payload for a structure map."""
|
|
210
341
|
|
|
211
342
|
source: str = ""
|
|
@@ -237,8 +368,53 @@ class JsonStructureMap(MaintainableType, frozen=True):
|
|
|
237
368
|
valid_to=self.validTo,
|
|
238
369
|
)
|
|
239
370
|
|
|
371
|
+
@classmethod
|
|
372
|
+
def from_model(self, sm: StructureMap) -> "JsonStructureMap":
|
|
373
|
+
"""Converts a pysdmx structure map to an SDMX-JSON one."""
|
|
374
|
+
cms = list(sm.component_maps)
|
|
375
|
+
cms.extend(list(sm.implicit_component_maps)) # type: ignore[arg-type]
|
|
376
|
+
cms.extend(list(sm.multi_component_maps)) # type: ignore[arg-type]
|
|
377
|
+
if not sm.name:
|
|
378
|
+
raise errors.Invalid(
|
|
379
|
+
"Invalid input",
|
|
380
|
+
"SDMX-JSON structure maps must have a name",
|
|
381
|
+
{"structure_map": sm.id},
|
|
382
|
+
)
|
|
383
|
+
return JsonStructureMap(
|
|
384
|
+
agency=(
|
|
385
|
+
sm.agency.id if isinstance(sm.agency, Agency) else sm.agency
|
|
386
|
+
),
|
|
387
|
+
id=sm.id,
|
|
388
|
+
name=sm.name,
|
|
389
|
+
version=sm.version,
|
|
390
|
+
isExternalReference=sm.is_external_reference,
|
|
391
|
+
validFrom=sm.valid_from,
|
|
392
|
+
validTo=sm.valid_to,
|
|
393
|
+
description=sm.description,
|
|
394
|
+
annotations=tuple(
|
|
395
|
+
[JsonAnnotation.from_model(a) for a in sm.annotations]
|
|
396
|
+
),
|
|
397
|
+
source=sm.source,
|
|
398
|
+
target=sm.target,
|
|
399
|
+
datePatternMaps=tuple(
|
|
400
|
+
[
|
|
401
|
+
JsonDatePatternMap.from_model(dpm)
|
|
402
|
+
for dpm in sm.date_pattern_maps
|
|
403
|
+
]
|
|
404
|
+
),
|
|
405
|
+
componentMaps=tuple(
|
|
406
|
+
[JsonComponentMap.from_model(cm) for cm in cms]
|
|
407
|
+
),
|
|
408
|
+
fixedValueMaps=tuple(
|
|
409
|
+
[
|
|
410
|
+
JsonFixedValueMap.from_model(fvm)
|
|
411
|
+
for fvm in sm.fixed_value_maps
|
|
412
|
+
]
|
|
413
|
+
),
|
|
414
|
+
)
|
|
415
|
+
|
|
240
416
|
|
|
241
|
-
class JsonStructureMaps(Struct, frozen=True):
|
|
417
|
+
class JsonStructureMaps(Struct, frozen=True, omit_defaults=True):
|
|
242
418
|
"""SDMX-JSON payload for structure maps."""
|
|
243
419
|
|
|
244
420
|
structureMaps: Sequence[JsonStructureMap]
|
|
@@ -251,7 +427,7 @@ class JsonStructureMaps(Struct, frozen=True):
|
|
|
251
427
|
]
|
|
252
428
|
|
|
253
429
|
|
|
254
|
-
class JsonMappingMessage(Struct, frozen=True):
|
|
430
|
+
class JsonMappingMessage(Struct, frozen=True, omit_defaults=True):
|
|
255
431
|
"""SDMX-JSON payload for /structuremap queries."""
|
|
256
432
|
|
|
257
433
|
data: JsonStructureMaps
|
|
@@ -261,7 +437,7 @@ class JsonMappingMessage(Struct, frozen=True):
|
|
|
261
437
|
return self.data.to_model()[0]
|
|
262
438
|
|
|
263
439
|
|
|
264
|
-
class JsonStructureMapsMessage(Struct, frozen=True):
|
|
440
|
+
class JsonStructureMapsMessage(Struct, frozen=True, omit_defaults=True):
|
|
265
441
|
"""SDMX-JSON payload for generic /structuremap queries."""
|
|
266
442
|
|
|
267
443
|
data: JsonStructureMaps
|
|
@@ -271,7 +447,7 @@ class JsonStructureMapsMessage(Struct, frozen=True):
|
|
|
271
447
|
return self.data.to_model()
|
|
272
448
|
|
|
273
449
|
|
|
274
|
-
class JsonRepresentationMaps(Struct, frozen=True):
|
|
450
|
+
class JsonRepresentationMaps(Struct, frozen=True, omit_defaults=True):
|
|
275
451
|
"""SDMX-JSON payload for representation maps."""
|
|
276
452
|
|
|
277
453
|
representationMaps: Sequence[JsonRepresentationMap]
|
|
@@ -287,7 +463,7 @@ class JsonRepresentationMaps(Struct, frozen=True):
|
|
|
287
463
|
return maps
|
|
288
464
|
|
|
289
465
|
|
|
290
|
-
class JsonRepresentationMapMessage(Struct, frozen=True):
|
|
466
|
+
class JsonRepresentationMapMessage(Struct, frozen=True, omit_defaults=True):
|
|
291
467
|
"""SDMX-JSON payload for /representationmap queries."""
|
|
292
468
|
|
|
293
469
|
data: JsonRepresentationMaps
|
|
@@ -297,7 +473,7 @@ class JsonRepresentationMapMessage(Struct, frozen=True):
|
|
|
297
473
|
return self.data.to_model()[0]
|
|
298
474
|
|
|
299
475
|
|
|
300
|
-
class JsonRepresentationMapsMessage(Struct, frozen=True):
|
|
476
|
+
class JsonRepresentationMapsMessage(Struct, frozen=True, omit_defaults=True):
|
|
301
477
|
"""SDMX-JSON payload for /representationmap queries."""
|
|
302
478
|
|
|
303
479
|
data: JsonRepresentationMaps
|
|
@@ -4,13 +4,17 @@ from typing import Sequence
|
|
|
4
4
|
|
|
5
5
|
from msgspec import Struct
|
|
6
6
|
|
|
7
|
+
from pysdmx import errors
|
|
7
8
|
from pysdmx.io.json.sdmxjson2.messages.core import (
|
|
9
|
+
JsonAnnotation,
|
|
8
10
|
MaintainableType,
|
|
9
11
|
)
|
|
10
|
-
from pysdmx.model import ProvisionAgreement
|
|
12
|
+
from pysdmx.model import Agency, ProvisionAgreement
|
|
11
13
|
|
|
12
14
|
|
|
13
|
-
class JsonProvisionAgreement(
|
|
15
|
+
class JsonProvisionAgreement(
|
|
16
|
+
MaintainableType, frozen=True, omit_defaults=True
|
|
17
|
+
):
|
|
14
18
|
"""SDMX-JSON payload for a provision agreement."""
|
|
15
19
|
|
|
16
20
|
dataflow: str = ""
|
|
@@ -28,12 +32,39 @@ class JsonProvisionAgreement(MaintainableType, frozen=True):
|
|
|
28
32
|
valid_to=self.validTo,
|
|
29
33
|
dataflow=self.dataflow,
|
|
30
34
|
provider=self.dataProvider,
|
|
31
|
-
annotations=[a.to_model() for a in self.annotations],
|
|
35
|
+
annotations=tuple([a.to_model() for a in self.annotations]),
|
|
32
36
|
is_external_reference=self.isExternalReference,
|
|
33
37
|
)
|
|
34
38
|
|
|
39
|
+
@classmethod
|
|
40
|
+
def from_model(self, pa: ProvisionAgreement) -> "JsonProvisionAgreement":
|
|
41
|
+
"""Converts a pysdmx provision agreement to an SDMX-JSON one."""
|
|
42
|
+
if not pa.name:
|
|
43
|
+
raise errors.Invalid(
|
|
44
|
+
"Invalid input",
|
|
45
|
+
"SDMX-JSON provision agreements must have a name",
|
|
46
|
+
{"provision_agreement": pa.id},
|
|
47
|
+
)
|
|
48
|
+
return JsonProvisionAgreement(
|
|
49
|
+
agency=(
|
|
50
|
+
pa.agency.id if isinstance(pa.agency, Agency) else pa.agency
|
|
51
|
+
),
|
|
52
|
+
id=pa.id,
|
|
53
|
+
name=pa.name,
|
|
54
|
+
version=pa.version,
|
|
55
|
+
isExternalReference=pa.is_external_reference,
|
|
56
|
+
validFrom=pa.valid_from,
|
|
57
|
+
validTo=pa.valid_to,
|
|
58
|
+
description=pa.description,
|
|
59
|
+
annotations=tuple(
|
|
60
|
+
[JsonAnnotation.from_model(a) for a in pa.annotations]
|
|
61
|
+
),
|
|
62
|
+
dataflow=pa.dataflow,
|
|
63
|
+
dataProvider=pa.provider,
|
|
64
|
+
)
|
|
65
|
+
|
|
35
66
|
|
|
36
|
-
class JsonProvisionAgreements(Struct, frozen=True):
|
|
67
|
+
class JsonProvisionAgreements(Struct, frozen=True, omit_defaults=True):
|
|
37
68
|
"""SDMX-JSON payload for provision agreements."""
|
|
38
69
|
|
|
39
70
|
provisionAgreements: Sequence[JsonProvisionAgreement]
|
|
@@ -43,7 +74,7 @@ class JsonProvisionAgreements(Struct, frozen=True):
|
|
|
43
74
|
return [pa.to_model() for pa in self.provisionAgreements]
|
|
44
75
|
|
|
45
76
|
|
|
46
|
-
class JsonProvisionAgreementsMessage(Struct, frozen=True):
|
|
77
|
+
class JsonProvisionAgreementsMessage(Struct, frozen=True, omit_defaults=True):
|
|
47
78
|
"""SDMX-JSON payload for /provisionagreement queries."""
|
|
48
79
|
|
|
49
80
|
data: JsonProvisionAgreements
|
|
@@ -5,13 +5,16 @@ from typing import Dict, Sequence, Set
|
|
|
5
5
|
|
|
6
6
|
from msgspec import Struct
|
|
7
7
|
|
|
8
|
-
from pysdmx.io.json.sdmxjson2.messages.core import
|
|
8
|
+
from pysdmx.io.json.sdmxjson2.messages.core import (
|
|
9
|
+
ItemSchemeType,
|
|
10
|
+
JsonAnnotation,
|
|
11
|
+
)
|
|
9
12
|
from pysdmx.io.json.sdmxjson2.messages.pa import JsonProvisionAgreement
|
|
10
|
-
from pysdmx.model import DataflowRef, DataProvider, DataProviderScheme
|
|
13
|
+
from pysdmx.model import Agency, DataflowRef, DataProvider, DataProviderScheme
|
|
11
14
|
from pysdmx.util import parse_item_urn, parse_urn
|
|
12
15
|
|
|
13
16
|
|
|
14
|
-
class JsonDataProviderScheme(ItemSchemeType, frozen=True):
|
|
17
|
+
class JsonDataProviderScheme(ItemSchemeType, frozen=True, omit_defaults=True):
|
|
15
18
|
"""SDMX-JSON payload for a data provider scheme."""
|
|
16
19
|
|
|
17
20
|
dataProviders: Sequence[DataProvider] = ()
|
|
@@ -37,7 +40,9 @@ class JsonDataProviderScheme(ItemSchemeType, frozen=True):
|
|
|
37
40
|
description=p.description,
|
|
38
41
|
contacts=p.contacts,
|
|
39
42
|
dataflows=list(paprs[f"{self.agency}:{p.id}"]),
|
|
40
|
-
annotations=
|
|
43
|
+
annotations=tuple(
|
|
44
|
+
[a.to_model() for a in self.annotations]
|
|
45
|
+
),
|
|
41
46
|
)
|
|
42
47
|
for p in self.dataProviders
|
|
43
48
|
]
|
|
@@ -48,7 +53,9 @@ class JsonDataProviderScheme(ItemSchemeType, frozen=True):
|
|
|
48
53
|
name=p.name,
|
|
49
54
|
description=p.description,
|
|
50
55
|
contacts=p.contacts,
|
|
51
|
-
annotations=
|
|
56
|
+
annotations=tuple(
|
|
57
|
+
[a.to_model() for a in self.annotations]
|
|
58
|
+
),
|
|
52
59
|
)
|
|
53
60
|
for p in self.dataProviders
|
|
54
61
|
]
|
|
@@ -63,8 +70,29 @@ class JsonDataProviderScheme(ItemSchemeType, frozen=True):
|
|
|
63
70
|
valid_to=self.validTo,
|
|
64
71
|
)
|
|
65
72
|
|
|
73
|
+
@classmethod
|
|
74
|
+
def from_model(self, dps: DataProviderScheme) -> "JsonDataProviderScheme":
|
|
75
|
+
"""Converts a pysdmx data provider scheme to an SDMX-JSON one."""
|
|
76
|
+
return JsonDataProviderScheme(
|
|
77
|
+
id="DATA_PROVIDERS",
|
|
78
|
+
name="DATA_PROVIDERS",
|
|
79
|
+
agency=(
|
|
80
|
+
dps.agency.id if isinstance(dps.agency, Agency) else dps.agency
|
|
81
|
+
),
|
|
82
|
+
description=dps.description,
|
|
83
|
+
version="1.0",
|
|
84
|
+
dataProviders=dps.items,
|
|
85
|
+
annotations=tuple(
|
|
86
|
+
[JsonAnnotation.from_model(a) for a in dps.annotations]
|
|
87
|
+
),
|
|
88
|
+
isExternalReference=dps.is_external_reference,
|
|
89
|
+
isPartial=dps.is_partial,
|
|
90
|
+
validFrom=dps.valid_from,
|
|
91
|
+
validTo=dps.valid_to,
|
|
92
|
+
)
|
|
93
|
+
|
|
66
94
|
|
|
67
|
-
class JsonDataProviderSchemes(Struct, frozen=True):
|
|
95
|
+
class JsonDataProviderSchemes(Struct, frozen=True, omit_defaults=True):
|
|
68
96
|
"""SDMX-JSON payload for the list of data provider schemes."""
|
|
69
97
|
|
|
70
98
|
dataProviderSchemes: Sequence[JsonDataProviderScheme]
|
|
@@ -78,7 +106,7 @@ class JsonDataProviderSchemes(Struct, frozen=True):
|
|
|
78
106
|
]
|
|
79
107
|
|
|
80
108
|
|
|
81
|
-
class JsonProviderMessage(Struct, frozen=True):
|
|
109
|
+
class JsonProviderMessage(Struct, frozen=True, omit_defaults=True):
|
|
82
110
|
"""SDMX-JSON payload for /dataproviderscheme queries."""
|
|
83
111
|
|
|
84
112
|
data: JsonDataProviderSchemes
|
|
@@ -4,13 +4,16 @@ from typing import Any, Optional, Sequence
|
|
|
4
4
|
|
|
5
5
|
from msgspec import Struct
|
|
6
6
|
|
|
7
|
+
from pysdmx import errors
|
|
7
8
|
from pysdmx.io.json.sdmxjson2.messages.core import (
|
|
8
9
|
IdentifiableType,
|
|
9
10
|
ItemSchemeType,
|
|
11
|
+
JsonAnnotation,
|
|
10
12
|
JsonHeader,
|
|
11
13
|
JsonTextFormat,
|
|
12
14
|
get_facets,
|
|
13
15
|
)
|
|
16
|
+
from pysdmx.model import Agency
|
|
14
17
|
from pysdmx.model.dataset import ActionType
|
|
15
18
|
from pysdmx.model.message import MetadataMessage
|
|
16
19
|
from pysdmx.model.metadata import (
|
|
@@ -35,12 +38,27 @@ class JsonMetadataAttribute(IdentifiableType, frozen=True, omit_defaults=True):
|
|
|
35
38
|
id=self.id,
|
|
36
39
|
value=self.value,
|
|
37
40
|
attributes=attrs,
|
|
38
|
-
annotations=[a.to_model() for a in self.annotations],
|
|
41
|
+
annotations=tuple([a.to_model() for a in self.annotations]),
|
|
39
42
|
format=get_facets(self.format) if self.format else None,
|
|
40
43
|
)
|
|
41
44
|
|
|
45
|
+
@classmethod
|
|
46
|
+
def from_model(self, attr: MetadataAttribute) -> "JsonMetadataAttribute":
|
|
47
|
+
"""Converts a pysdmx metadata attribute to an SDMX-JSON one."""
|
|
48
|
+
return JsonMetadataAttribute(
|
|
49
|
+
id=attr.id,
|
|
50
|
+
annotations=tuple(
|
|
51
|
+
[JsonAnnotation.from_model(a) for a in attr.annotations]
|
|
52
|
+
),
|
|
53
|
+
value=attr.value,
|
|
54
|
+
attributes=tuple(
|
|
55
|
+
[JsonMetadataAttribute.from_model(a) for a in attr.attributes]
|
|
56
|
+
),
|
|
57
|
+
format=JsonTextFormat.from_model(None, attr.format),
|
|
58
|
+
)
|
|
59
|
+
|
|
42
60
|
|
|
43
|
-
class JsonMetadataReport(ItemSchemeType, frozen=True):
|
|
61
|
+
class JsonMetadataReport(ItemSchemeType, frozen=True, omit_defaults=True):
|
|
44
62
|
"""SDMX-JSON payload for a metadata report."""
|
|
45
63
|
|
|
46
64
|
metadataflow: str = ""
|
|
@@ -58,7 +76,7 @@ class JsonMetadataReport(ItemSchemeType, frozen=True):
|
|
|
58
76
|
attrs = [a.to_model() for a in self.attributes]
|
|
59
77
|
attrs = merge_attributes(attrs) # type: ignore[assignment]
|
|
60
78
|
return MetadataReport(
|
|
61
|
-
annotations=[a.to_model() for a in self.annotations],
|
|
79
|
+
annotations=tuple([a.to_model() for a in self.annotations]),
|
|
62
80
|
id=self.id,
|
|
63
81
|
name=self.name,
|
|
64
82
|
description=self.description,
|
|
@@ -68,8 +86,8 @@ class JsonMetadataReport(ItemSchemeType, frozen=True):
|
|
|
68
86
|
agency=self.agency,
|
|
69
87
|
is_external_reference=self.isExternalReference,
|
|
70
88
|
metadataflow=self.metadataflow,
|
|
71
|
-
targets=self.targets,
|
|
72
|
-
attributes=attrs,
|
|
89
|
+
targets=tuple(self.targets),
|
|
90
|
+
attributes=tuple(attrs),
|
|
73
91
|
metadataProvisionAgreement=self.metadataProvisionAgreement,
|
|
74
92
|
publicationPeriod=self.publicationPeriod,
|
|
75
93
|
publicationYear=self.publicationYear,
|
|
@@ -78,8 +96,50 @@ class JsonMetadataReport(ItemSchemeType, frozen=True):
|
|
|
78
96
|
action=ActionType(self.action) if self.action else None,
|
|
79
97
|
)
|
|
80
98
|
|
|
99
|
+
@classmethod
|
|
100
|
+
def from_model(cls, report: MetadataReport) -> "JsonMetadataReport":
|
|
101
|
+
"""Converts a pysdmx metadata report to an SDMX-JSON one."""
|
|
102
|
+
if not report.name:
|
|
103
|
+
raise errors.Invalid(
|
|
104
|
+
"Invalid input",
|
|
105
|
+
"SDMX-JSON metadata reports must have a name",
|
|
106
|
+
{"metadata_report": report.id},
|
|
107
|
+
)
|
|
108
|
+
|
|
109
|
+
return JsonMetadataReport(
|
|
110
|
+
agency=(
|
|
111
|
+
report.agency.id
|
|
112
|
+
if isinstance(report.agency, Agency)
|
|
113
|
+
else report.agency
|
|
114
|
+
),
|
|
115
|
+
id=report.id,
|
|
116
|
+
name=report.name,
|
|
117
|
+
version=report.version,
|
|
118
|
+
isExternalReference=report.is_external_reference,
|
|
119
|
+
validFrom=report.valid_from,
|
|
120
|
+
validTo=report.valid_to,
|
|
121
|
+
description=report.description,
|
|
122
|
+
annotations=tuple(
|
|
123
|
+
[JsonAnnotation.from_model(a) for a in report.annotations]
|
|
124
|
+
),
|
|
125
|
+
metadataflow=report.metadataflow,
|
|
126
|
+
targets=report.targets,
|
|
127
|
+
attributes=tuple(
|
|
128
|
+
[
|
|
129
|
+
JsonMetadataAttribute.from_model(a)
|
|
130
|
+
for a in report.attributes
|
|
131
|
+
]
|
|
132
|
+
),
|
|
133
|
+
metadataProvisionAgreement=report.metadataProvisionAgreement,
|
|
134
|
+
publicationPeriod=report.publicationPeriod,
|
|
135
|
+
publicationYear=report.publicationYear,
|
|
136
|
+
reportingBegin=report.reportingBegin,
|
|
137
|
+
reportingEnd=report.reportingEnd,
|
|
138
|
+
action=report.action.value if report.action else None,
|
|
139
|
+
)
|
|
140
|
+
|
|
81
141
|
|
|
82
|
-
class JsonMetadataSets(Struct, frozen=True):
|
|
142
|
+
class JsonMetadataSets(Struct, frozen=True, omit_defaults=True):
|
|
83
143
|
"""SDMX-JSON payload for the list of metadata sets."""
|
|
84
144
|
|
|
85
145
|
metadataSets: Sequence[JsonMetadataReport]
|
|
@@ -89,7 +149,7 @@ class JsonMetadataSets(Struct, frozen=True):
|
|
|
89
149
|
return [r.to_model() for r in self.metadataSets]
|
|
90
150
|
|
|
91
151
|
|
|
92
|
-
class JsonMetadataMessage(Struct, frozen=True):
|
|
152
|
+
class JsonMetadataMessage(Struct, frozen=True, omit_defaults=True):
|
|
93
153
|
"""SDMX-JSON payload for /metadata queries."""
|
|
94
154
|
|
|
95
155
|
meta: JsonHeader
|
|
@@ -100,3 +160,21 @@ class JsonMetadataMessage(Struct, frozen=True):
|
|
|
100
160
|
header = self.meta.to_model()
|
|
101
161
|
reports = self.data.to_model()
|
|
102
162
|
return MetadataMessage(header, reports)
|
|
163
|
+
|
|
164
|
+
@classmethod
|
|
165
|
+
def from_model(self, msg: MetadataMessage) -> "JsonMetadataMessage":
|
|
166
|
+
"""Converts a pysdmx metadata message to an SDMX-JSON one."""
|
|
167
|
+
if not msg.header:
|
|
168
|
+
raise errors.Invalid(
|
|
169
|
+
"Invalid input",
|
|
170
|
+
"SDMX-JSON metadata messages must have a header.",
|
|
171
|
+
)
|
|
172
|
+
if not msg.reports:
|
|
173
|
+
raise errors.Invalid(
|
|
174
|
+
"Invalid input",
|
|
175
|
+
"SDMX-JSON metadata messages must have metadata reports.",
|
|
176
|
+
)
|
|
177
|
+
|
|
178
|
+
header = JsonHeader.from_model(msg.header, "metadata")
|
|
179
|
+
reports = [JsonMetadataReport.from_model(r) for r in msg.reports]
|
|
180
|
+
return JsonMetadataMessage(header, JsonMetadataSets(reports))
|