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,121 @@
|
|
|
1
|
+
"""Collection of Fusion-JSON schemas for SDMX-REST schema queries."""
|
|
2
|
+
|
|
3
|
+
from typing import Literal, Optional, Sequence, Union
|
|
4
|
+
|
|
5
|
+
from msgspec import Struct
|
|
6
|
+
|
|
7
|
+
from pysdmx.io.json.fusion.messages.code import FusionCodelist
|
|
8
|
+
from pysdmx.io.json.fusion.messages.concept import FusionConceptScheme
|
|
9
|
+
from pysdmx.io.json.fusion.messages.core import (
|
|
10
|
+
FusionRepresentation,
|
|
11
|
+
FusionString,
|
|
12
|
+
)
|
|
13
|
+
from pysdmx.io.json.fusion.messages.dsd import (
|
|
14
|
+
_find_concept,
|
|
15
|
+
_get_representation,
|
|
16
|
+
)
|
|
17
|
+
from pysdmx.model import (
|
|
18
|
+
ArrayBoundaries,
|
|
19
|
+
MetadataComponent,
|
|
20
|
+
)
|
|
21
|
+
from pysdmx.model import (
|
|
22
|
+
MetadataStructure as MSD,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class FusionMetadataAttribute(Struct, frozen=True):
|
|
27
|
+
"""Fusion-JSON payload for a metadata attribute."""
|
|
28
|
+
|
|
29
|
+
id: str
|
|
30
|
+
concept: str
|
|
31
|
+
minOccurs: int
|
|
32
|
+
maxOccurs: Union[int, Literal["unbounded"]]
|
|
33
|
+
presentational: Optional[bool] = False
|
|
34
|
+
representation: Optional[FusionRepresentation] = None
|
|
35
|
+
metadataAttributes: Sequence["FusionMetadataAttribute"] = ()
|
|
36
|
+
|
|
37
|
+
def to_model(
|
|
38
|
+
self,
|
|
39
|
+
cs: Sequence[FusionConceptScheme],
|
|
40
|
+
cls: Sequence[FusionCodelist],
|
|
41
|
+
) -> MetadataComponent:
|
|
42
|
+
"""Returns an attribute."""
|
|
43
|
+
c = _find_concept(cs, self.concept)
|
|
44
|
+
dt, facets, codes, _ = _get_representation(
|
|
45
|
+
self.id, self.representation, cls, {}
|
|
46
|
+
)
|
|
47
|
+
|
|
48
|
+
if self.representation and self.representation.representation:
|
|
49
|
+
local_enum_ref = self.representation.representation
|
|
50
|
+
else:
|
|
51
|
+
local_enum_ref = None
|
|
52
|
+
|
|
53
|
+
if self.maxOccurs == "unbounded":
|
|
54
|
+
ab = ArrayBoundaries(self.minOccurs)
|
|
55
|
+
elif self.maxOccurs > 1:
|
|
56
|
+
ab = ArrayBoundaries(self.minOccurs, self.maxOccurs)
|
|
57
|
+
else:
|
|
58
|
+
ab = None
|
|
59
|
+
|
|
60
|
+
return MetadataComponent(
|
|
61
|
+
self.id,
|
|
62
|
+
is_presentational=self.presentational, # type: ignore[arg-type]
|
|
63
|
+
concept=c.to_model(cls),
|
|
64
|
+
local_dtype=dt,
|
|
65
|
+
local_facets=facets,
|
|
66
|
+
local_codes=codes,
|
|
67
|
+
array_def=ab,
|
|
68
|
+
local_enum_ref=local_enum_ref,
|
|
69
|
+
components=[
|
|
70
|
+
ma.to_model(cs, cls) for ma in self.metadataAttributes
|
|
71
|
+
],
|
|
72
|
+
)
|
|
73
|
+
|
|
74
|
+
|
|
75
|
+
class FusionMetadataStructure(
|
|
76
|
+
Struct, frozen=True, rename={"agency": "agencyId"}
|
|
77
|
+
):
|
|
78
|
+
"""Fusion-JSON payload for an MSD."""
|
|
79
|
+
|
|
80
|
+
id: str
|
|
81
|
+
names: Sequence[FusionString]
|
|
82
|
+
agency: str
|
|
83
|
+
descriptions: Optional[Sequence[FusionString]] = None
|
|
84
|
+
version: str = "1.0"
|
|
85
|
+
metadataAttributes: Sequence[FusionMetadataAttribute] = ()
|
|
86
|
+
|
|
87
|
+
def to_model(
|
|
88
|
+
self,
|
|
89
|
+
cs: Sequence[FusionConceptScheme],
|
|
90
|
+
cls: Sequence[FusionCodelist],
|
|
91
|
+
) -> MSD:
|
|
92
|
+
"""Returns the schema for this DSD."""
|
|
93
|
+
return MSD(
|
|
94
|
+
id=self.id,
|
|
95
|
+
agency=self.agency,
|
|
96
|
+
name=self.names[0].value if self.names else None,
|
|
97
|
+
description=(
|
|
98
|
+
self.descriptions[0].value if self.descriptions else None
|
|
99
|
+
),
|
|
100
|
+
version=self.version,
|
|
101
|
+
components=[a.to_model(cs, cls) for a in self.metadataAttributes],
|
|
102
|
+
)
|
|
103
|
+
|
|
104
|
+
|
|
105
|
+
class FusionMetadataStructuresMessage(Struct, frozen=True):
|
|
106
|
+
"""Fusion-JSON payload for /metadatastructure queries."""
|
|
107
|
+
|
|
108
|
+
ConceptScheme: Sequence[FusionConceptScheme]
|
|
109
|
+
MetadataStructure: Sequence[FusionMetadataStructure]
|
|
110
|
+
ValueList: Sequence[FusionCodelist] = ()
|
|
111
|
+
Codelist: Sequence[FusionCodelist] = ()
|
|
112
|
+
|
|
113
|
+
def to_model(self) -> Sequence[MSD]:
|
|
114
|
+
"""Returns the requested metadata structures."""
|
|
115
|
+
all_mds = []
|
|
116
|
+
for msd in self.MetadataStructure:
|
|
117
|
+
cls: list[FusionCodelist] = []
|
|
118
|
+
cls.extend(self.Codelist)
|
|
119
|
+
cls.extend(self.ValueList)
|
|
120
|
+
all_mds.append(msd.to_model(self.ConceptScheme, cls))
|
|
121
|
+
return all_mds
|
|
@@ -6,12 +6,14 @@ from typing import Dict, Optional, Sequence, Set
|
|
|
6
6
|
from msgspec import Struct
|
|
7
7
|
|
|
8
8
|
from pysdmx.io.json.fusion.messages.core import FusionString
|
|
9
|
+
from pysdmx.io.json.fusion.messages.mpa import FusionMetadataProvisionAgreement
|
|
9
10
|
from pysdmx.io.json.fusion.messages.pa import FusionProvisionAgreement
|
|
10
11
|
from pysdmx.model import (
|
|
11
12
|
Agency,
|
|
12
13
|
Contact,
|
|
13
14
|
DataflowRef,
|
|
14
15
|
DataProvider,
|
|
16
|
+
MetadataProvider,
|
|
15
17
|
)
|
|
16
18
|
from pysdmx.model import (
|
|
17
19
|
AgencyScheme as AS,
|
|
@@ -19,6 +21,9 @@ from pysdmx.model import (
|
|
|
19
21
|
from pysdmx.model import (
|
|
20
22
|
DataProviderScheme as DPS,
|
|
21
23
|
)
|
|
24
|
+
from pysdmx.model import (
|
|
25
|
+
MetadataProviderScheme as MDPS,
|
|
26
|
+
)
|
|
22
27
|
from pysdmx.util import parse_item_urn, parse_urn
|
|
23
28
|
|
|
24
29
|
|
|
@@ -188,3 +193,88 @@ class FusionProviderMessage(Struct, frozen=True):
|
|
|
188
193
|
p.to_model(self.ProvisionAgreement)
|
|
189
194
|
for p in self.DataProviderScheme
|
|
190
195
|
]
|
|
196
|
+
|
|
197
|
+
|
|
198
|
+
class FusionMetadataProvider(Struct, frozen=True):
|
|
199
|
+
"""Fusion-JSON payload for a metadata provider."""
|
|
200
|
+
|
|
201
|
+
id: str
|
|
202
|
+
names: Sequence[FusionString]
|
|
203
|
+
descriptions: Optional[Sequence[FusionString]] = None
|
|
204
|
+
contacts: Sequence[FusionContact] = ()
|
|
205
|
+
|
|
206
|
+
def to_model(self, owner: Optional[str] = None) -> MetadataProvider:
|
|
207
|
+
"""Converts a FusionMetadataProvider to a pysdmx one."""
|
|
208
|
+
d = self.descriptions[0].value if self.descriptions else None
|
|
209
|
+
c = tuple([c.to_model() for c in self.contacts])
|
|
210
|
+
oid = f"{owner}.{self.id}" if owner and owner != "SDMX" else self.id
|
|
211
|
+
return MetadataProvider(
|
|
212
|
+
id=oid, name=self.names[0].value, description=d, contacts=c
|
|
213
|
+
)
|
|
214
|
+
|
|
215
|
+
|
|
216
|
+
class FusionMetadataProviderScheme(Struct, frozen=True):
|
|
217
|
+
"""Fusion-JSON payload for a metadata provider scheme."""
|
|
218
|
+
|
|
219
|
+
id: str
|
|
220
|
+
agencyId: str
|
|
221
|
+
names: Sequence[FusionString] = ()
|
|
222
|
+
descriptions: Sequence[FusionString] = ()
|
|
223
|
+
items: Sequence[FusionMetadataProvider] = ()
|
|
224
|
+
|
|
225
|
+
def __get_df_ref(self, ref: str) -> DataflowRef:
|
|
226
|
+
a = parse_urn(ref)
|
|
227
|
+
return DataflowRef(id=a.id, agency=a.agency, version=a.version)
|
|
228
|
+
|
|
229
|
+
def to_model(
|
|
230
|
+
self, pas: Sequence[FusionMetadataProvisionAgreement]
|
|
231
|
+
) -> MDPS:
|
|
232
|
+
"""Converts a FusionProviderScheme to a DataProviderScheme."""
|
|
233
|
+
if pas:
|
|
234
|
+
paprs: Dict[str, Set[DataflowRef]] = defaultdict(set)
|
|
235
|
+
for pa in pas:
|
|
236
|
+
df = self.__get_df_ref(pa.metadataflowRef)
|
|
237
|
+
ref = parse_item_urn(pa.metadataproviderRef)
|
|
238
|
+
paprs[f"{ref.agency}:{ref.item_id}"].add(df)
|
|
239
|
+
prvs = [o.to_model() for o in self.items]
|
|
240
|
+
prvs = [
|
|
241
|
+
MetadataProvider(
|
|
242
|
+
id=p.id,
|
|
243
|
+
name=p.name,
|
|
244
|
+
description=p.description,
|
|
245
|
+
contacts=p.contacts,
|
|
246
|
+
dataflows=list(paprs[f"{self.agencyId}:{p.id}"]),
|
|
247
|
+
)
|
|
248
|
+
for p in prvs
|
|
249
|
+
]
|
|
250
|
+
return MDPS(
|
|
251
|
+
agency=self.agencyId,
|
|
252
|
+
name=self.names[0].value,
|
|
253
|
+
description=(
|
|
254
|
+
self.descriptions[0].value if self.descriptions else None
|
|
255
|
+
),
|
|
256
|
+
items=prvs,
|
|
257
|
+
)
|
|
258
|
+
else:
|
|
259
|
+
return MDPS(
|
|
260
|
+
agency=self.agencyId,
|
|
261
|
+
name=self.names[0].value,
|
|
262
|
+
description=(
|
|
263
|
+
self.descriptions[0].value if self.descriptions else None
|
|
264
|
+
),
|
|
265
|
+
items=[o.to_model() for o in self.items],
|
|
266
|
+
)
|
|
267
|
+
|
|
268
|
+
|
|
269
|
+
class FusionMetadataProviderMessage(Struct, frozen=True):
|
|
270
|
+
"""Fusion-JSON payload for /metadataproviderscheme queries."""
|
|
271
|
+
|
|
272
|
+
MetadataProviderScheme: Sequence[FusionMetadataProviderScheme]
|
|
273
|
+
MetadataProvisionAgreement: Sequence[FusionMetadataProvisionAgreement] = ()
|
|
274
|
+
|
|
275
|
+
def to_model(self) -> Sequence[MDPS]:
|
|
276
|
+
"""Returns the requested list of metadata providers."""
|
|
277
|
+
return [
|
|
278
|
+
p.to_model(self.MetadataProvisionAgreement)
|
|
279
|
+
for p in self.MetadataProviderScheme
|
|
280
|
+
]
|
|
@@ -20,4 +20,9 @@ deserializers = Deserializers(
|
|
|
20
20
|
mapping=msg.FusionMappingMessage, # type: ignore[arg-type]
|
|
21
21
|
code_map=msg.FusionRepresentationMapMessage, # type: ignore[arg-type]
|
|
22
22
|
transformation_scheme=msg.FusionTransfoMsg, # type: ignore[arg-type]
|
|
23
|
+
metadataflows=msg.FusionMetadataflowsMessage, # type: ignore[arg-type]
|
|
24
|
+
metadata_provision_agreement=msg.FusionMetadataProvisionAgreementMessage, # type: ignore[arg-type]
|
|
25
|
+
metadata_providers=msg.FusionMetadataProviderMessage, # type: ignore[arg-type]
|
|
26
|
+
msds=msg.FusionMetadataStructuresMessage, # type: ignore[arg-type]
|
|
27
|
+
data_structures=msg.FusionDataStructuresMessage, # type: ignore[arg-type]
|
|
23
28
|
)
|
|
@@ -23,10 +23,20 @@ from pysdmx.io.json.sdmxjson2.messages.map import (
|
|
|
23
23
|
JsonRepresentationMapsMessage,
|
|
24
24
|
JsonStructureMapsMessage,
|
|
25
25
|
)
|
|
26
|
+
from pysdmx.io.json.sdmxjson2.messages.metadataflow import (
|
|
27
|
+
JsonMetadataflowsMessage as JsonMdfsMsg,
|
|
28
|
+
)
|
|
29
|
+
from pysdmx.io.json.sdmxjson2.messages.mpa import (
|
|
30
|
+
JsonMetadataProvisionAgreementsMessage as JsonMPAMsg,
|
|
31
|
+
)
|
|
32
|
+
from pysdmx.io.json.sdmxjson2.messages.msd import JsonMetadataStructuresMessage
|
|
26
33
|
from pysdmx.io.json.sdmxjson2.messages.pa import (
|
|
27
34
|
JsonProvisionAgreementsMessage as JsonPAMessage,
|
|
28
35
|
)
|
|
29
|
-
from pysdmx.io.json.sdmxjson2.messages.provider import
|
|
36
|
+
from pysdmx.io.json.sdmxjson2.messages.provider import (
|
|
37
|
+
JsonMetadataProviderMessage,
|
|
38
|
+
JsonProviderMessage,
|
|
39
|
+
)
|
|
30
40
|
from pysdmx.io.json.sdmxjson2.messages.report import JsonMetadataMessage
|
|
31
41
|
from pysdmx.io.json.sdmxjson2.messages.schema import JsonSchemaMessage
|
|
32
42
|
from pysdmx.io.json.sdmxjson2.messages.structure import JsonStructureMessage
|
|
@@ -43,14 +53,18 @@ __all__ = [
|
|
|
43
53
|
"JsonDataflowMessage",
|
|
44
54
|
"JsonDataflowsMessage",
|
|
45
55
|
"JsonDataStructuresMessage",
|
|
56
|
+
"JsonMetadataProviderMessage",
|
|
46
57
|
"JsonProviderMessage",
|
|
47
58
|
"JsonPAMessage",
|
|
48
59
|
"JsonSchemaMessage",
|
|
49
60
|
"JsonHierarchyAssociationMessage",
|
|
50
61
|
"JsonHierarchiesMessage",
|
|
51
62
|
"JsonHierarchyMessage",
|
|
63
|
+
"JsonMdfsMsg",
|
|
52
64
|
"JsonMetadataMessage",
|
|
65
|
+
"JsonMPAMsg",
|
|
53
66
|
"JsonMappingMessage",
|
|
67
|
+
"JsonMetadataStructuresMessage",
|
|
54
68
|
"JsonRepresentationMapMessage",
|
|
55
69
|
"JsonRepresentationMapsMessage",
|
|
56
70
|
"JsonStructureMapsMessage",
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
"""Collection of SDMX-JSON schemas for SDMX-REST
|
|
1
|
+
"""Collection of SDMX-JSON schemas for SDMX-REST DSD queries."""
|
|
2
2
|
|
|
3
|
-
from typing import Dict, List, Literal, Optional, Sequence, Tuple
|
|
3
|
+
from typing import Dict, List, Literal, Optional, Sequence, Tuple, Union
|
|
4
4
|
|
|
5
5
|
from msgspec import Struct
|
|
6
6
|
|
|
@@ -27,6 +27,7 @@ from pysdmx.model import (
|
|
|
27
27
|
DataType,
|
|
28
28
|
Facets,
|
|
29
29
|
ItemReference,
|
|
30
|
+
MetadataComponent,
|
|
30
31
|
Role,
|
|
31
32
|
)
|
|
32
33
|
from pysdmx.model.dataflow import Group
|
|
@@ -43,7 +44,7 @@ def _find_concept(cs: Sequence[JsonConceptScheme], urn: str) -> JsonConcept:
|
|
|
43
44
|
return [c for c in f[0].concepts if c.id == r.item_id][0]
|
|
44
45
|
|
|
45
46
|
|
|
46
|
-
def
|
|
47
|
+
def _get_type(repr_: JsonRepresentation) -> Optional[str]:
|
|
47
48
|
t: Optional[str] = None
|
|
48
49
|
if repr_.enumerationFormat:
|
|
49
50
|
t = repr_.enumerationFormat.dataType
|
|
@@ -67,13 +68,15 @@ def _get_representation(
|
|
|
67
68
|
]:
|
|
68
69
|
valid = cons.get(id_, [])
|
|
69
70
|
codes = local.to_enumeration(cls, valid) if local else None
|
|
70
|
-
dt = DataType(
|
|
71
|
+
dt = DataType(_get_type(local)) if local else None
|
|
71
72
|
facets = local.to_facets() if local else None
|
|
72
73
|
ab = local.to_array_def() if local else None
|
|
73
74
|
return (dt, facets, codes, ab)
|
|
74
75
|
|
|
75
76
|
|
|
76
|
-
def _get_concept_reference(
|
|
77
|
+
def _get_concept_reference(
|
|
78
|
+
component: Union[Component, MetadataComponent],
|
|
79
|
+
) -> str:
|
|
77
80
|
if isinstance(component.concept, ItemReference):
|
|
78
81
|
concept = (
|
|
79
82
|
"urn:sdmx:org.sdmx.infomodel.conceptscheme."
|
|
@@ -95,7 +98,7 @@ def _get_concept_reference(component: Component) -> str:
|
|
|
95
98
|
|
|
96
99
|
|
|
97
100
|
def _get_json_representation(
|
|
98
|
-
comp: Component,
|
|
101
|
+
comp: Union[Component, MetadataComponent],
|
|
99
102
|
) -> Optional[JsonRepresentation]:
|
|
100
103
|
enum = comp.local_enum_ref if comp.local_enum_ref else None
|
|
101
104
|
return JsonRepresentation.from_model(
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Collection of SDMX-JSON schemas for dataflow queries."""
|
|
2
|
+
|
|
3
|
+
from typing import Sequence
|
|
4
|
+
|
|
5
|
+
from msgspec import Struct
|
|
6
|
+
|
|
7
|
+
from pysdmx import errors
|
|
8
|
+
from pysdmx.io.json.sdmxjson2.messages.core import (
|
|
9
|
+
JsonAnnotation,
|
|
10
|
+
MaintainableType,
|
|
11
|
+
)
|
|
12
|
+
from pysdmx.model import Agency, Metadataflow, MetadataStructure
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class JsonMetadataflow(MaintainableType, frozen=True, omit_defaults=True):
|
|
16
|
+
"""SDMX-JSON payload for a metadataflow."""
|
|
17
|
+
|
|
18
|
+
structure: str = ""
|
|
19
|
+
targets: Sequence[str] = ()
|
|
20
|
+
|
|
21
|
+
def to_model(self) -> Metadataflow:
|
|
22
|
+
"""Converts a FusionMetadataflow to a standard one."""
|
|
23
|
+
return Metadataflow(
|
|
24
|
+
id=self.id,
|
|
25
|
+
agency=self.agency,
|
|
26
|
+
name=self.name,
|
|
27
|
+
description=self.description,
|
|
28
|
+
version=self.version,
|
|
29
|
+
structure=self.structure,
|
|
30
|
+
targets=self.targets,
|
|
31
|
+
annotations=[a.to_model() for a in self.annotations],
|
|
32
|
+
is_external_reference=self.isExternalReference,
|
|
33
|
+
valid_from=self.validFrom,
|
|
34
|
+
valid_to=self.validTo,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def from_model(self, df: Metadataflow) -> "JsonMetadataflow":
|
|
39
|
+
"""Converts a pysdmx metadataflow to an SDMX-JSON one."""
|
|
40
|
+
if not df.name:
|
|
41
|
+
raise errors.Invalid(
|
|
42
|
+
"Invalid input",
|
|
43
|
+
"SDMX-JSON metadataflows must have a name",
|
|
44
|
+
{"metadataflow": df.id},
|
|
45
|
+
)
|
|
46
|
+
if isinstance(df.structure, MetadataStructure):
|
|
47
|
+
dsdref = (
|
|
48
|
+
"urn:sdmx:org.sdmx.infomodel.metadatastructure.MetadataStructure="
|
|
49
|
+
f"{df.structure.agency}:{df.structure.id}({df.structure.version})"
|
|
50
|
+
)
|
|
51
|
+
else:
|
|
52
|
+
dsdref = df.structure # type: ignore[assignment]
|
|
53
|
+
return JsonMetadataflow(
|
|
54
|
+
agency=(
|
|
55
|
+
df.agency.id if isinstance(df.agency, Agency) else df.agency
|
|
56
|
+
),
|
|
57
|
+
id=df.id,
|
|
58
|
+
name=df.name,
|
|
59
|
+
version=df.version,
|
|
60
|
+
isExternalReference=df.is_external_reference,
|
|
61
|
+
validFrom=df.valid_from,
|
|
62
|
+
validTo=df.valid_to,
|
|
63
|
+
description=df.description,
|
|
64
|
+
annotations=tuple(
|
|
65
|
+
[JsonAnnotation.from_model(a) for a in df.annotations]
|
|
66
|
+
),
|
|
67
|
+
structure=dsdref,
|
|
68
|
+
)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class JsonMetadataflows(Struct, frozen=True, omit_defaults=True):
|
|
72
|
+
"""SDMX-JSON payload for the list of metadataflows."""
|
|
73
|
+
|
|
74
|
+
metadataflows: Sequence[JsonMetadataflow]
|
|
75
|
+
|
|
76
|
+
def to_model(self) -> Sequence[Metadataflow]:
|
|
77
|
+
"""Returns the requested metadataflows."""
|
|
78
|
+
return [mdf.to_model() for mdf in self.metadataflows]
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
class JsonMetadataflowsMessage(Struct, frozen=True, omit_defaults=True):
|
|
82
|
+
"""SDMX-JSON payload for /metadataflow queries."""
|
|
83
|
+
|
|
84
|
+
data: JsonMetadataflows
|
|
85
|
+
|
|
86
|
+
def to_model(self) -> Sequence[Metadataflow]:
|
|
87
|
+
"""Returns the requested dataflows."""
|
|
88
|
+
return self.data.to_model()
|
|
@@ -0,0 +1,88 @@
|
|
|
1
|
+
"""Collection of SDMX-JSON schemas for provision agreements."""
|
|
2
|
+
|
|
3
|
+
from typing import Sequence
|
|
4
|
+
|
|
5
|
+
from msgspec import Struct
|
|
6
|
+
|
|
7
|
+
from pysdmx import errors
|
|
8
|
+
from pysdmx.io.json.sdmxjson2.messages.core import (
|
|
9
|
+
JsonAnnotation,
|
|
10
|
+
MaintainableType,
|
|
11
|
+
)
|
|
12
|
+
from pysdmx.model import Agency, MetadataProvisionAgreement
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
class JsonMetadataProvisionAgreement(
|
|
16
|
+
MaintainableType, frozen=True, omit_defaults=True
|
|
17
|
+
):
|
|
18
|
+
"""SDMX-JSON payload for a metadata provision agreement."""
|
|
19
|
+
|
|
20
|
+
metadataflow: str = ""
|
|
21
|
+
metadataProvider: str = ""
|
|
22
|
+
|
|
23
|
+
def to_model(self) -> MetadataProvisionAgreement:
|
|
24
|
+
"""Converts a FusionPA to a standard metadata provision agreement."""
|
|
25
|
+
return MetadataProvisionAgreement(
|
|
26
|
+
id=self.id,
|
|
27
|
+
agency=self.agency,
|
|
28
|
+
name=self.name,
|
|
29
|
+
description=self.description,
|
|
30
|
+
version=self.version,
|
|
31
|
+
valid_from=self.validFrom,
|
|
32
|
+
valid_to=self.validTo,
|
|
33
|
+
metadataflow=self.metadataflow,
|
|
34
|
+
metadata_provider=self.metadataProvider,
|
|
35
|
+
annotations=tuple([a.to_model() for a in self.annotations]),
|
|
36
|
+
is_external_reference=self.isExternalReference,
|
|
37
|
+
)
|
|
38
|
+
|
|
39
|
+
@classmethod
|
|
40
|
+
def from_model(
|
|
41
|
+
self, mpa: MetadataProvisionAgreement
|
|
42
|
+
) -> "JsonMetadataProvisionAgreement":
|
|
43
|
+
"""Converts a pysdmx metadata provision agreement to SDMX-JSON."""
|
|
44
|
+
if not mpa.name:
|
|
45
|
+
raise errors.Invalid(
|
|
46
|
+
"Invalid input",
|
|
47
|
+
"SDMX-JSON metadata provision agreements must have a name",
|
|
48
|
+
{"metadata_provision_agreement": mpa.id},
|
|
49
|
+
)
|
|
50
|
+
return JsonMetadataProvisionAgreement(
|
|
51
|
+
agency=(
|
|
52
|
+
mpa.agency.id if isinstance(mpa.agency, Agency) else mpa.agency
|
|
53
|
+
),
|
|
54
|
+
id=mpa.id,
|
|
55
|
+
name=mpa.name,
|
|
56
|
+
version=mpa.version,
|
|
57
|
+
isExternalReference=mpa.is_external_reference,
|
|
58
|
+
validFrom=mpa.valid_from,
|
|
59
|
+
validTo=mpa.valid_to,
|
|
60
|
+
description=mpa.description,
|
|
61
|
+
annotations=tuple(
|
|
62
|
+
[JsonAnnotation.from_model(a) for a in mpa.annotations]
|
|
63
|
+
),
|
|
64
|
+
metadataflow=mpa.metadataflow,
|
|
65
|
+
metadataProvider=mpa.metadata_provider,
|
|
66
|
+
)
|
|
67
|
+
|
|
68
|
+
|
|
69
|
+
class JsonMetadataProvisionAgreements(Struct, frozen=True, omit_defaults=True):
|
|
70
|
+
"""SDMX-JSON payload for provision agreements."""
|
|
71
|
+
|
|
72
|
+
metadataProvisionAgreements: Sequence[JsonMetadataProvisionAgreement]
|
|
73
|
+
|
|
74
|
+
def to_model(self) -> Sequence[MetadataProvisionAgreement]:
|
|
75
|
+
"""Returns the requested metadata provision agreements."""
|
|
76
|
+
return [pa.to_model() for pa in self.metadataProvisionAgreements]
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
class JsonMetadataProvisionAgreementsMessage(
|
|
80
|
+
Struct, frozen=True, omit_defaults=True
|
|
81
|
+
):
|
|
82
|
+
"""SDMX-JSON payload for /metadataprovisionagreement queries."""
|
|
83
|
+
|
|
84
|
+
data: JsonMetadataProvisionAgreements
|
|
85
|
+
|
|
86
|
+
def to_model(self) -> Sequence[MetadataProvisionAgreement]:
|
|
87
|
+
"""Returns the requested metadata provision agreements."""
|
|
88
|
+
return self.data.to_model()
|