pysdmx 1.7.0__py3-none-any.whl → 1.8.1__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.
Files changed (34) hide show
  1. pysdmx/__init__.py +1 -1
  2. pysdmx/api/fmr/__init__.py +266 -0
  3. pysdmx/api/qb/refmeta.py +1 -1
  4. pysdmx/api/qb/schema.py +4 -10
  5. pysdmx/api/qb/service.py +26 -4
  6. pysdmx/api/qb/structure.py +2 -2
  7. pysdmx/io/json/fusion/messages/__init__.py +14 -0
  8. pysdmx/io/json/fusion/messages/dsd.py +52 -1
  9. pysdmx/io/json/fusion/messages/metadataflow.py +44 -0
  10. pysdmx/io/json/fusion/messages/mpa.py +45 -0
  11. pysdmx/io/json/fusion/messages/msd.py +121 -0
  12. pysdmx/io/json/fusion/messages/org.py +90 -0
  13. pysdmx/io/json/fusion/reader/__init__.py +5 -0
  14. pysdmx/io/json/sdmxjson2/messages/__init__.py +15 -1
  15. pysdmx/io/json/sdmxjson2/messages/dsd.py +9 -6
  16. pysdmx/io/json/sdmxjson2/messages/metadataflow.py +88 -0
  17. pysdmx/io/json/sdmxjson2/messages/mpa.py +88 -0
  18. pysdmx/io/json/sdmxjson2/messages/msd.py +245 -0
  19. pysdmx/io/json/sdmxjson2/messages/provider.py +117 -1
  20. pysdmx/io/json/sdmxjson2/messages/structure.py +25 -1
  21. pysdmx/io/json/sdmxjson2/reader/__init__.py +5 -0
  22. pysdmx/io/serde.py +5 -0
  23. pysdmx/io/writer.py +2 -4
  24. pysdmx/io/xml/__structure_aux_writer.py +9 -9
  25. pysdmx/io/xml/__write_data_aux.py +1 -2
  26. pysdmx/model/__init__.py +12 -1
  27. pysdmx/model/dataflow.py +1 -0
  28. pysdmx/model/map.py +3 -1
  29. pysdmx/model/message.py +29 -2
  30. pysdmx/model/metadata.py +255 -4
  31. {pysdmx-1.7.0.dist-info → pysdmx-1.8.1.dist-info}/METADATA +1 -1
  32. {pysdmx-1.7.0.dist-info → pysdmx-1.8.1.dist-info}/RECORD +34 -28
  33. {pysdmx-1.7.0.dist-info → pysdmx-1.8.1.dist-info}/WHEEL +0 -0
  34. {pysdmx-1.7.0.dist-info → pysdmx-1.8.1.dist-info}/licenses/LICENSE +0 -0
@@ -0,0 +1,245 @@
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
+ metadataAttributes: Sequence["JsonMetadataAttribute"] = ()
41
+
42
+ def to_model(
43
+ self, cs: Sequence[JsonConceptScheme], cls: Sequence[Codelist]
44
+ ) -> MetadataComponent:
45
+ """Returns a metadata component."""
46
+ c = (
47
+ _find_concept(cs, self.conceptIdentity).to_model(cls)
48
+ if cs
49
+ else parse_item_urn(self.conceptIdentity)
50
+ )
51
+ dt, facets, codes, _ = _get_representation(
52
+ self.id, self.localRepresentation, cls, {}
53
+ )
54
+
55
+ if self.localRepresentation and self.localRepresentation.enumeration:
56
+ local_enum_ref = self.localRepresentation.enumeration
57
+ else:
58
+ local_enum_ref = None
59
+
60
+ if self.maxOccurs == "unbounded":
61
+ ab = ArrayBoundaries(self.minOccurs)
62
+ elif self.maxOccurs > 1:
63
+ ab = ArrayBoundaries(self.minOccurs, self.maxOccurs)
64
+ else:
65
+ ab = None
66
+
67
+ return MetadataComponent(
68
+ id=self.id,
69
+ is_presentational=self.isPresentational,
70
+ concept=c,
71
+ local_dtype=dt,
72
+ local_facets=facets,
73
+ local_codes=codes,
74
+ array_def=ab,
75
+ local_enum_ref=local_enum_ref,
76
+ components=[a.to_model(cs, cls) for a in self.metadataAttributes],
77
+ )
78
+
79
+ @classmethod
80
+ def from_model(self, cmp: MetadataComponent) -> "JsonMetadataAttribute":
81
+ """Converts a pysdmx metadata attribute to an SDMX-JSON one."""
82
+ concept = _get_concept_reference(cmp)
83
+ repr = _get_json_representation(cmp)
84
+
85
+ min_occurs = cmp.array_def.min_size if cmp.array_def else 0
86
+ if cmp.array_def is None or cmp.array_def.max_size is None:
87
+ max_occurs: Union[int, Literal["unbounded"]] = "unbounded"
88
+ else:
89
+ max_occurs = cmp.array_def.max_size
90
+
91
+ return JsonMetadataAttribute(
92
+ id=cmp.id,
93
+ conceptIdentity=concept,
94
+ localRepresentation=repr,
95
+ minOccurs=min_occurs,
96
+ maxOccurs=max_occurs,
97
+ isPresentational=cmp.is_presentational,
98
+ metadataAttributes=[
99
+ JsonMetadataAttribute.from_model(c) for c in cmp.components
100
+ ],
101
+ )
102
+
103
+
104
+ class JsonMetadataAttributes(Struct, frozen=True, omit_defaults=True):
105
+ """SDMX-JSON payload for the list of metadata attributes."""
106
+
107
+ id: Literal["MetadataAttributeDescriptor"] = "MetadataAttributeDescriptor"
108
+ metadataAttributes: Sequence[JsonMetadataAttribute] = ()
109
+
110
+ def to_model(
111
+ self, cs: Sequence[JsonConceptScheme], cls: Sequence[Codelist]
112
+ ) -> List[MetadataComponent]:
113
+ """Returns the list of metadata attributes."""
114
+ return [a.to_model(cs, cls) for a in self.metadataAttributes]
115
+
116
+ @classmethod
117
+ def from_model(
118
+ self, attributes: Sequence[MetadataComponent]
119
+ ) -> "JsonMetadataAttributes":
120
+ """Converts a pysdmx list of metadata attributes to SDMX-JSON."""
121
+ return JsonMetadataAttributes(
122
+ metadataAttributes=[
123
+ JsonMetadataAttribute.from_model(a) for a in attributes
124
+ ]
125
+ )
126
+
127
+
128
+ class JsonMetadataComponents(Struct, frozen=True, omit_defaults=True):
129
+ """SDMX-JSON payload for the list of DSD components."""
130
+
131
+ metadataAttributeList: Optional[JsonMetadataAttributes] = None
132
+
133
+ def to_model(
134
+ self,
135
+ cs: Sequence[JsonConceptScheme],
136
+ cls: Sequence[JsonCodelist],
137
+ vls: Sequence[JsonValuelist],
138
+ ) -> Sequence[MetadataComponent]:
139
+ """Returns the components for this DSD."""
140
+ enums = [cl.to_model() for cl in cls]
141
+ enums.extend([vl.to_model() for vl in vls])
142
+ comps = (
143
+ self.metadataAttributeList.to_model(cs, enums)
144
+ if self.metadataAttributeList
145
+ else []
146
+ )
147
+ return comps
148
+
149
+ @classmethod
150
+ def from_model(
151
+ self, components: Sequence[MetadataComponent]
152
+ ) -> "JsonMetadataComponents":
153
+ """Converts a pysdmx components list to an SDMX-JSON one."""
154
+ attributes = JsonMetadataAttributes.from_model(components)
155
+ return JsonMetadataComponents(metadataAttributeList=attributes)
156
+
157
+
158
+ class JsonMetadataStructure(MaintainableType, frozen=True, omit_defaults=True):
159
+ """SDMX-JSON payload for a DSD."""
160
+
161
+ metadataStructureComponents: Optional[JsonMetadataComponents] = None
162
+
163
+ def to_model(
164
+ self,
165
+ cs: Sequence[JsonConceptScheme],
166
+ cls: Sequence[JsonCodelist],
167
+ vls: Sequence[JsonValuelist],
168
+ ) -> MetadataStructure:
169
+ """Map to pysdmx model class."""
170
+ c = self.metadataStructureComponents.to_model( # type: ignore[union-attr]
171
+ cs,
172
+ cls,
173
+ vls,
174
+ )
175
+ return MetadataStructure(
176
+ id=self.id,
177
+ name=self.name,
178
+ agency=self.agency,
179
+ description=self.description,
180
+ version=self.version,
181
+ annotations=[a.to_model() for a in self.annotations],
182
+ is_external_reference=self.isExternalReference,
183
+ valid_from=self.validFrom,
184
+ valid_to=self.validTo,
185
+ components=c,
186
+ )
187
+
188
+ @classmethod
189
+ def from_model(self, msd: MetadataStructure) -> "JsonMetadataStructure":
190
+ """Converts a pysdmx MSD to an SDMX-JSON one."""
191
+ if not msd.name:
192
+ raise errors.Invalid(
193
+ "Invalid input",
194
+ "SDMX-JSON metadata structures must have a name",
195
+ {"metadata_structure": msd.id},
196
+ )
197
+
198
+ return JsonMetadataStructure(
199
+ agency=(
200
+ msd.agency.id if isinstance(msd.agency, Agency) else msd.agency
201
+ ),
202
+ id=msd.id,
203
+ name=msd.name,
204
+ version=msd.version,
205
+ isExternalReference=msd.is_external_reference,
206
+ validFrom=msd.valid_from,
207
+ validTo=msd.valid_to,
208
+ description=msd.description,
209
+ annotations=tuple(
210
+ [JsonAnnotation.from_model(a) for a in msd.annotations]
211
+ ),
212
+ metadataStructureComponents=JsonMetadataComponents.from_model(
213
+ msd.components
214
+ ),
215
+ )
216
+
217
+
218
+ class JsonMetadataStructures(Struct, frozen=True, omit_defaults=True):
219
+ """SDMX-JSON payload for metadata structures."""
220
+
221
+ metadataStructures: Sequence[JsonMetadataStructure]
222
+ conceptSchemes: Sequence[JsonConceptScheme] = ()
223
+ valuelists: Sequence[JsonValuelist] = ()
224
+ codelists: Sequence[JsonCodelist] = ()
225
+
226
+ def to_model(self) -> Sequence[MetadataStructure]:
227
+ """Returns the requested msds."""
228
+ return [
229
+ msd.to_model(
230
+ self.conceptSchemes,
231
+ self.codelists,
232
+ self.valuelists,
233
+ )
234
+ for msd in self.metadataStructures
235
+ ]
236
+
237
+
238
+ class JsonMetadataStructuresMessage(Struct, frozen=True, omit_defaults=True):
239
+ """SDMX-JSON payload for /metadatastructure queries."""
240
+
241
+ data: JsonMetadataStructures
242
+
243
+ def to_model(self) -> Sequence[MetadataStructure]:
244
+ """Returns the requested metadata structures."""
245
+ 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 Agency, DataflowRef, DataProvider, DataProviderScheme
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 JsonDataProviderScheme
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
- "Attributes"
315
- ] += f" isFinal={str(maintainable.is_final).lower()!r}"
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
- "Attributes"
814
- ] += f" isPartial={str(item_scheme.is_partial).lower()!r}"
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
- "Attributes"
825
- ] += f" {_write_vtl(item_scheme, indent, references_30)}"
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 MetadataAttribute, MetadataReport
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__(self, id_: str) -> Optional[
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 MetadataReport
38
- from pysdmx.model.organisation import AgencyScheme, DataProviderScheme
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)