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
pysdmx/__init__.py
CHANGED
pysdmx/api/fmr/__init__.py
CHANGED
|
@@ -41,6 +41,8 @@ from pysdmx.model import (
|
|
|
41
41
|
DataProvider,
|
|
42
42
|
Hierarchy,
|
|
43
43
|
HierarchyAssociation,
|
|
44
|
+
Metadataflow,
|
|
45
|
+
MetadataProvisionAgreement,
|
|
44
46
|
MetadataReport,
|
|
45
47
|
MultiRepresentationMap,
|
|
46
48
|
ProvisionAgreement,
|
|
@@ -196,6 +198,18 @@ class __BaseRegistryClient:
|
|
|
196
198
|
StructureType.DATA_PROVIDER_SCHEME, agency, references=r
|
|
197
199
|
)
|
|
198
200
|
|
|
201
|
+
def _metadata_providers_q(
|
|
202
|
+
self, agency: str, with_flows: bool
|
|
203
|
+
) -> StructureQuery:
|
|
204
|
+
r = (
|
|
205
|
+
StructureReference.METADATA_PROVISION_AGREEMENT
|
|
206
|
+
if with_flows
|
|
207
|
+
else StructureReference.NONE
|
|
208
|
+
)
|
|
209
|
+
return StructureQuery(
|
|
210
|
+
StructureType.METADATA_PROVIDER_SCHEME, agency, references=r
|
|
211
|
+
)
|
|
212
|
+
|
|
199
213
|
def _categories_q(
|
|
200
214
|
self, agency: str, id: str, version: str
|
|
201
215
|
) -> StructureQuery:
|
|
@@ -273,6 +287,38 @@ class __BaseRegistryClient:
|
|
|
273
287
|
StructureType.PROVISION_AGREEMENT, agency, id, version
|
|
274
288
|
)
|
|
275
289
|
|
|
290
|
+
def _metadataflows_q(
|
|
291
|
+
self, agency: str, id: str, version: str
|
|
292
|
+
) -> StructureQuery:
|
|
293
|
+
return StructureQuery(StructureType.METADATAFLOW, agency, id, version)
|
|
294
|
+
|
|
295
|
+
def _mpa_q(self, agency: str, id: str, version: str) -> StructureQuery:
|
|
296
|
+
return StructureQuery(
|
|
297
|
+
StructureType.METADATA_PROVISION_AGREEMENT, agency, id, version
|
|
298
|
+
)
|
|
299
|
+
|
|
300
|
+
def _msds_q(self, agency: str, id: str, version: str) -> StructureQuery:
|
|
301
|
+
return StructureQuery(
|
|
302
|
+
StructureType.METADATA_STRUCTURE,
|
|
303
|
+
agency,
|
|
304
|
+
id,
|
|
305
|
+
version,
|
|
306
|
+
detail=StructureDetail.REFERENCE_PARTIAL,
|
|
307
|
+
references=StructureReference.DESCENDANTS,
|
|
308
|
+
)
|
|
309
|
+
|
|
310
|
+
def _data_structures_q(
|
|
311
|
+
self, agency: str, id: str, version: str
|
|
312
|
+
) -> StructureQuery:
|
|
313
|
+
return StructureQuery(
|
|
314
|
+
StructureType.DATA_STRUCTURE,
|
|
315
|
+
agency,
|
|
316
|
+
id,
|
|
317
|
+
version,
|
|
318
|
+
detail=StructureDetail.REFERENCE_PARTIAL,
|
|
319
|
+
references=StructureReference.DESCENDANTS,
|
|
320
|
+
)
|
|
321
|
+
|
|
276
322
|
|
|
277
323
|
class RegistryClient(__BaseRegistryClient):
|
|
278
324
|
"""A client to be used to retrieve metadata from the FMR.
|
|
@@ -385,6 +431,28 @@ class RegistryClient(__BaseRegistryClient):
|
|
|
385
431
|
schemes = super()._out(out, self.deser.providers)
|
|
386
432
|
return schemes[0].items
|
|
387
433
|
|
|
434
|
+
def get_metadata_providers(
|
|
435
|
+
self,
|
|
436
|
+
agency: str,
|
|
437
|
+
with_flows: bool = False,
|
|
438
|
+
) -> Sequence[DataProvider]:
|
|
439
|
+
"""Get the list of **metadata providers** for the supplied agency.
|
|
440
|
+
|
|
441
|
+
Args:
|
|
442
|
+
agency: The agency maintaining the metadata provider scheme from
|
|
443
|
+
which metadata providers must be returned.
|
|
444
|
+
with_flows: Whether the metadata providers should contain the list
|
|
445
|
+
of metadataflows for which the metadata provider provides
|
|
446
|
+
metadata reports.
|
|
447
|
+
|
|
448
|
+
Returns:
|
|
449
|
+
The requested list of metadata providers.
|
|
450
|
+
"""
|
|
451
|
+
query = super()._metadata_providers_q(agency, with_flows)
|
|
452
|
+
out = self.__fetch(query)
|
|
453
|
+
schemes = super()._out(out, self.deser.metadata_providers)
|
|
454
|
+
return schemes[0].items
|
|
455
|
+
|
|
388
456
|
def get_categories(
|
|
389
457
|
self,
|
|
390
458
|
agency: str,
|
|
@@ -605,6 +673,28 @@ class RegistryClient(__BaseRegistryClient):
|
|
|
605
673
|
out = self.__fetch(query)
|
|
606
674
|
return super()._out(out, self.deser.dataflows)
|
|
607
675
|
|
|
676
|
+
def get_data_structures(
|
|
677
|
+
self,
|
|
678
|
+
agency: str = "*",
|
|
679
|
+
id: str = "*",
|
|
680
|
+
version: str = "+",
|
|
681
|
+
) -> Sequence[Dataflow]:
|
|
682
|
+
"""Get the data structures(s) matching the supplied parameters.
|
|
683
|
+
|
|
684
|
+
Args:
|
|
685
|
+
agency: The agency maintaining the data structures(s).
|
|
686
|
+
id: The ID of the data structures(s) to be returned.
|
|
687
|
+
version: The version of the data structures(s) to be returned.
|
|
688
|
+
The most recent version will be returned, unless specified
|
|
689
|
+
otherwise.
|
|
690
|
+
|
|
691
|
+
Returns:
|
|
692
|
+
The requested data structures(s).
|
|
693
|
+
"""
|
|
694
|
+
query = super()._data_structures_q(agency, id, version)
|
|
695
|
+
out = self.__fetch(query)
|
|
696
|
+
return super()._out(out, self.deser.data_structures)
|
|
697
|
+
|
|
608
698
|
def get_hierarchy(
|
|
609
699
|
self,
|
|
610
700
|
agency: str,
|
|
@@ -672,6 +762,72 @@ class RegistryClient(__BaseRegistryClient):
|
|
|
672
762
|
out = self.__fetch(query)
|
|
673
763
|
return super()._out(out, self.deser.report).reports
|
|
674
764
|
|
|
765
|
+
def get_metadata_structures(
|
|
766
|
+
self,
|
|
767
|
+
agency: str = "*",
|
|
768
|
+
id: str = "*",
|
|
769
|
+
version: str = "+",
|
|
770
|
+
) -> Sequence[Dataflow]:
|
|
771
|
+
"""Get the metadata structures (MSD) matching the supplied parameters.
|
|
772
|
+
|
|
773
|
+
Args:
|
|
774
|
+
agency: The agency maintaining the MSD(s).
|
|
775
|
+
id: The ID of the metadata structure(s) to be returned.
|
|
776
|
+
version: The version of the metadata structure(s) to be returned.
|
|
777
|
+
The most recent version will be returned, unless specified
|
|
778
|
+
otherwise.
|
|
779
|
+
|
|
780
|
+
Returns:
|
|
781
|
+
The requested MSD(s).
|
|
782
|
+
"""
|
|
783
|
+
query = super()._msds_q(agency, id, version)
|
|
784
|
+
out = self.__fetch(query)
|
|
785
|
+
return super()._out(out, self.deser.msds)
|
|
786
|
+
|
|
787
|
+
def get_metadataflows(
|
|
788
|
+
self,
|
|
789
|
+
agency: str = "*",
|
|
790
|
+
id: str = "*",
|
|
791
|
+
version: str = "+",
|
|
792
|
+
) -> Sequence[Metadataflow]:
|
|
793
|
+
"""Get the metadataflow(s) matching the supplied parameters.
|
|
794
|
+
|
|
795
|
+
Args:
|
|
796
|
+
agency: The agency maintaining the metadataflow(s).
|
|
797
|
+
id: The ID of the metadataflow(s) to be returned.
|
|
798
|
+
version: The version of the metadataflow(s) to be returned.
|
|
799
|
+
The most recent version will be returned, unless specified
|
|
800
|
+
otherwise.
|
|
801
|
+
|
|
802
|
+
Returns:
|
|
803
|
+
The requested metadataflow(s).
|
|
804
|
+
"""
|
|
805
|
+
query = super()._metadataflows_q(agency, id, version)
|
|
806
|
+
out = self.__fetch(query)
|
|
807
|
+
return super()._out(out, self.deser.metadataflows)
|
|
808
|
+
|
|
809
|
+
def get_metadata_provision_agreement(
|
|
810
|
+
self,
|
|
811
|
+
agency: str,
|
|
812
|
+
id: str,
|
|
813
|
+
version: str = "+",
|
|
814
|
+
) -> MetadataProvisionAgreement:
|
|
815
|
+
"""Get the metadata provision agreement matching the parameters.
|
|
816
|
+
|
|
817
|
+
Args:
|
|
818
|
+
agency: The agency maintaining the metadata provision agreement.
|
|
819
|
+
id: The ID of the metadata provision agreement to be returned.
|
|
820
|
+
version: The version of the metadata provision agreement to be
|
|
821
|
+
returned. The most recent version will be returned, unless
|
|
822
|
+
specified otherwise.
|
|
823
|
+
|
|
824
|
+
Returns:
|
|
825
|
+
The requested metadata provision agreement.
|
|
826
|
+
"""
|
|
827
|
+
query = super()._mpa_q(agency, id, version)
|
|
828
|
+
out = self.__fetch(query)
|
|
829
|
+
return super()._out(out, self.deser.metadata_provision_agreement)[0]
|
|
830
|
+
|
|
675
831
|
def get_mapping(
|
|
676
832
|
self,
|
|
677
833
|
agency: str,
|
|
@@ -844,6 +1000,28 @@ class AsyncRegistryClient(__BaseRegistryClient):
|
|
|
844
1000
|
schemes = super()._out(out, self.deser.providers)
|
|
845
1001
|
return schemes[0].items
|
|
846
1002
|
|
|
1003
|
+
async def get_metadata_providers(
|
|
1004
|
+
self,
|
|
1005
|
+
agency: str,
|
|
1006
|
+
with_flows: bool = False,
|
|
1007
|
+
) -> Sequence[DataProvider]:
|
|
1008
|
+
"""Get the list of **metadata providers** for the supplied agency.
|
|
1009
|
+
|
|
1010
|
+
Args:
|
|
1011
|
+
agency: The agency maintaining the metadata provider scheme from
|
|
1012
|
+
which metadata providers must be returned.
|
|
1013
|
+
with_flows: Whether the metadata providers should contain the list
|
|
1014
|
+
of metadataflows for which the metadata provider provides
|
|
1015
|
+
metadata reports.
|
|
1016
|
+
|
|
1017
|
+
Returns:
|
|
1018
|
+
The requested list of metadata providers.
|
|
1019
|
+
"""
|
|
1020
|
+
query = super()._metadata_providers_q(agency, with_flows)
|
|
1021
|
+
out = await self.__fetch(query)
|
|
1022
|
+
schemes = super()._out(out, self.deser.metadata_providers)
|
|
1023
|
+
return schemes[0].items
|
|
1024
|
+
|
|
847
1025
|
async def get_categories(
|
|
848
1026
|
self,
|
|
849
1027
|
agency: str,
|
|
@@ -1069,6 +1247,28 @@ class AsyncRegistryClient(__BaseRegistryClient):
|
|
|
1069
1247
|
out = await self.__fetch(query)
|
|
1070
1248
|
return super()._out(out, self.deser.dataflows)
|
|
1071
1249
|
|
|
1250
|
+
async def get_data_structures(
|
|
1251
|
+
self,
|
|
1252
|
+
agency: str = "*",
|
|
1253
|
+
id: str = "*",
|
|
1254
|
+
version: str = "+",
|
|
1255
|
+
) -> Sequence[Dataflow]:
|
|
1256
|
+
"""Get the data structures(s) matching the supplied parameters.
|
|
1257
|
+
|
|
1258
|
+
Args:
|
|
1259
|
+
agency: The agency maintaining the data structures(s).
|
|
1260
|
+
id: The ID of the data structures(s) to be returned.
|
|
1261
|
+
version: The version of the data structures(s) to be returned.
|
|
1262
|
+
The most recent version will be returned, unless specified
|
|
1263
|
+
otherwise.
|
|
1264
|
+
|
|
1265
|
+
Returns:
|
|
1266
|
+
The requested data structures(s).
|
|
1267
|
+
"""
|
|
1268
|
+
query = super()._data_structures_q(agency, id, version)
|
|
1269
|
+
out = await self.__fetch(query)
|
|
1270
|
+
return super()._out(out, self.deser.data_structures)
|
|
1271
|
+
|
|
1072
1272
|
async def get_hierarchy(
|
|
1073
1273
|
self,
|
|
1074
1274
|
agency: str,
|
|
@@ -1136,6 +1336,72 @@ class AsyncRegistryClient(__BaseRegistryClient):
|
|
|
1136
1336
|
out = await self.__fetch(query)
|
|
1137
1337
|
return super()._out(out, self.deser.report).reports
|
|
1138
1338
|
|
|
1339
|
+
async def get_metadata_structures(
|
|
1340
|
+
self,
|
|
1341
|
+
agency: str = "*",
|
|
1342
|
+
id: str = "*",
|
|
1343
|
+
version: str = "+",
|
|
1344
|
+
) -> Sequence[Dataflow]:
|
|
1345
|
+
"""Get the metadata structures (MSD) matching the supplied parameters.
|
|
1346
|
+
|
|
1347
|
+
Args:
|
|
1348
|
+
agency: The agency maintaining the MSD(s).
|
|
1349
|
+
id: The ID of the metadata structure(s) to be returned.
|
|
1350
|
+
version: The version of the metadata structure(s) to be returned.
|
|
1351
|
+
The most recent version will be returned, unless specified
|
|
1352
|
+
otherwise.
|
|
1353
|
+
|
|
1354
|
+
Returns:
|
|
1355
|
+
The requested MSD(s).
|
|
1356
|
+
"""
|
|
1357
|
+
query = super()._msds_q(agency, id, version)
|
|
1358
|
+
out = await self.__fetch(query)
|
|
1359
|
+
return super()._out(out, self.deser.msds)
|
|
1360
|
+
|
|
1361
|
+
async def get_metadataflows(
|
|
1362
|
+
self,
|
|
1363
|
+
agency: str = "*",
|
|
1364
|
+
id: str = "*",
|
|
1365
|
+
version: str = "+",
|
|
1366
|
+
) -> Sequence[Metadataflow]:
|
|
1367
|
+
"""Get the metadataflow(s) matching the supplied parameters.
|
|
1368
|
+
|
|
1369
|
+
Args:
|
|
1370
|
+
agency: The agency maintaining the metadataflow(s).
|
|
1371
|
+
id: The ID of the metadataflow(s) to be returned.
|
|
1372
|
+
version: The version of the metadataflow(s) to be returned.
|
|
1373
|
+
The most recent version will be returned, unless specified
|
|
1374
|
+
otherwise.
|
|
1375
|
+
|
|
1376
|
+
Returns:
|
|
1377
|
+
The requested metadataflow(s).
|
|
1378
|
+
"""
|
|
1379
|
+
query = super()._metadataflows_q(agency, id, version)
|
|
1380
|
+
out = await self.__fetch(query)
|
|
1381
|
+
return super()._out(out, self.deser.metadataflows)
|
|
1382
|
+
|
|
1383
|
+
async def get_metadata_provision_agreement(
|
|
1384
|
+
self,
|
|
1385
|
+
agency: str,
|
|
1386
|
+
id: str,
|
|
1387
|
+
version: str = "+",
|
|
1388
|
+
) -> MetadataProvisionAgreement:
|
|
1389
|
+
"""Get the metadata provision agreement matching the parameters.
|
|
1390
|
+
|
|
1391
|
+
Args:
|
|
1392
|
+
agency: The agency maintaining the metadata provision agreement.
|
|
1393
|
+
id: The ID of the metadata provision agreement to be returned.
|
|
1394
|
+
version: The version of the metadata provision agreement to be
|
|
1395
|
+
returned. The most recent version will be returned, unless
|
|
1396
|
+
specified otherwise.
|
|
1397
|
+
|
|
1398
|
+
Returns:
|
|
1399
|
+
The requested metadata provision agreement.
|
|
1400
|
+
"""
|
|
1401
|
+
query = super()._mpa_q(agency, id, version)
|
|
1402
|
+
out = await self.__fetch(query)
|
|
1403
|
+
return super()._out(out, self.deser.metadata_provision_agreement)[0]
|
|
1404
|
+
|
|
1139
1405
|
async def get_mapping(
|
|
1140
1406
|
self,
|
|
1141
1407
|
agency: str,
|
pysdmx/api/qb/refmeta.py
CHANGED
|
@@ -43,7 +43,7 @@ class _RefMetaCoreQuery(CoreQuery, frozen=True, omit_defaults=True):
|
|
|
43
43
|
)
|
|
44
44
|
|
|
45
45
|
def _get_as_of_value(self, as_of: Optional[datetime]) -> str:
|
|
46
|
-
return f
|
|
46
|
+
return f"&asOf={as_of.isoformat('T', 'seconds')}" if as_of else ""
|
|
47
47
|
|
|
48
48
|
def _get_short_qs(
|
|
49
49
|
self, detail: RefMetaDetail, as_of: Optional[datetime]
|
pysdmx/api/qb/schema.py
CHANGED
|
@@ -101,10 +101,7 @@ class SchemaQuery(CoreQuery, frozen=True, omit_defaults=True):
|
|
|
101
101
|
self.__check_deletion(version)
|
|
102
102
|
|
|
103
103
|
def _create_full_query(self, ver: ApiVersion) -> str:
|
|
104
|
-
u =
|
|
105
|
-
f"/schema/{self.context.value}/"
|
|
106
|
-
f"{self.agency_id}/{self.resource_id}"
|
|
107
|
-
)
|
|
104
|
+
u = f"/schema/{self.context.value}/{self.agency_id}/{self.resource_id}"
|
|
108
105
|
u += f"/{self._to_kw(self.version, ver)}"
|
|
109
106
|
if (
|
|
110
107
|
self.obs_dimension
|
|
@@ -122,7 +119,7 @@ class SchemaQuery(CoreQuery, frozen=True, omit_defaults=True):
|
|
|
122
119
|
if self.as_of:
|
|
123
120
|
if self.obs_dimension:
|
|
124
121
|
u += "&"
|
|
125
|
-
u += f
|
|
122
|
+
u += f"asOf={self.as_of.isoformat('T', 'seconds')}"
|
|
126
123
|
if ver >= ApiVersion.V2_2_0:
|
|
127
124
|
if self.obs_dimension or self.as_of:
|
|
128
125
|
u += "&"
|
|
@@ -130,10 +127,7 @@ class SchemaQuery(CoreQuery, frozen=True, omit_defaults=True):
|
|
|
130
127
|
return u
|
|
131
128
|
|
|
132
129
|
def _create_short_query(self, ver: ApiVersion) -> str:
|
|
133
|
-
u =
|
|
134
|
-
f"/schema/{self.context.value}/"
|
|
135
|
-
f"{self.agency_id}/{self.resource_id}"
|
|
136
|
-
)
|
|
130
|
+
u = f"/schema/{self.context.value}/{self.agency_id}/{self.resource_id}"
|
|
137
131
|
if self.version != REST_LATEST:
|
|
138
132
|
u += f"/{self._to_kw(self.version, ver)}"
|
|
139
133
|
if self.obs_dimension or self.explicit or self.as_of or self.deletion:
|
|
@@ -147,7 +141,7 @@ class SchemaQuery(CoreQuery, frozen=True, omit_defaults=True):
|
|
|
147
141
|
if self.as_of:
|
|
148
142
|
if self.obs_dimension:
|
|
149
143
|
u += "&"
|
|
150
|
-
u += f
|
|
144
|
+
u += f"asOf={self.as_of.isoformat('T', 'seconds')}"
|
|
151
145
|
if self.deletion:
|
|
152
146
|
if self.obs_dimension or self.as_of:
|
|
153
147
|
u += "&"
|
pysdmx/api/qb/service.py
CHANGED
|
@@ -166,8 +166,12 @@ class RestService(_CoreRestService):
|
|
|
166
166
|
return self.__fetch(q, f)
|
|
167
167
|
|
|
168
168
|
def __fetch(self, query: str, format: str) -> bytes:
|
|
169
|
-
with httpx.Client(
|
|
169
|
+
with httpx.Client(
|
|
170
|
+
verify=self._ssl_context, follow_redirects=True
|
|
171
|
+
) as client:
|
|
170
172
|
try:
|
|
173
|
+
query = _add_query_slash(query)
|
|
174
|
+
|
|
171
175
|
url = f"{self._api_endpoint}{query}"
|
|
172
176
|
h = self._headers.copy()
|
|
173
177
|
h["Accept"] = format
|
|
@@ -282,8 +286,12 @@ class AsyncRestService(_CoreRestService):
|
|
|
282
286
|
return out
|
|
283
287
|
|
|
284
288
|
async def __fetch(self, query: str, format: str) -> bytes:
|
|
285
|
-
async with httpx.AsyncClient(
|
|
289
|
+
async with httpx.AsyncClient(
|
|
290
|
+
verify=self._ssl_context, follow_redirects=True
|
|
291
|
+
) as client:
|
|
286
292
|
try:
|
|
293
|
+
query = _add_query_slash(query)
|
|
294
|
+
|
|
287
295
|
url = f"{self._api_endpoint}{query}"
|
|
288
296
|
h = self._headers.copy()
|
|
289
297
|
h["Accept"] = format
|
|
@@ -336,12 +344,22 @@ class _CoreGdsRestService:
|
|
|
336
344
|
raise errors.Unavailable("Connection error", msg) from e
|
|
337
345
|
|
|
338
346
|
|
|
347
|
+
def _add_query_slash(query: str) -> str:
|
|
348
|
+
if "?" not in query and "#" not in query and not query.endswith("/"):
|
|
349
|
+
query += "/"
|
|
350
|
+
return query
|
|
351
|
+
|
|
352
|
+
|
|
339
353
|
class GdsRestService(_CoreGdsRestService):
|
|
340
354
|
"""Synchronous GDS-REST service."""
|
|
341
355
|
|
|
342
356
|
def _fetch(self, query: str, format_: str) -> bytes:
|
|
343
|
-
with httpx.Client(
|
|
357
|
+
with httpx.Client(
|
|
358
|
+
verify=self._ssl_context, follow_redirects=True
|
|
359
|
+
) as client:
|
|
344
360
|
try:
|
|
361
|
+
query = _add_query_slash(query)
|
|
362
|
+
|
|
345
363
|
url = f"{self._api_endpoint}{query}"
|
|
346
364
|
headers = {**self._headers, "Accept": format_}
|
|
347
365
|
response = client.get(
|
|
@@ -362,8 +380,12 @@ class GdsAsyncRestService(_CoreGdsRestService):
|
|
|
362
380
|
"""Asynchronous GDS-REST service."""
|
|
363
381
|
|
|
364
382
|
async def _fetch(self, query: str, format_: str) -> bytes:
|
|
365
|
-
async with httpx.AsyncClient(
|
|
383
|
+
async with httpx.AsyncClient(
|
|
384
|
+
verify=self._ssl_context, follow_redirects=True
|
|
385
|
+
) as client:
|
|
366
386
|
try:
|
|
387
|
+
query = _add_query_slash(query)
|
|
388
|
+
|
|
367
389
|
url = f"{self._api_endpoint}{query}"
|
|
368
390
|
headers = {**self._headers, "Accept": format_}
|
|
369
391
|
response = await client.get(
|
pysdmx/api/qb/structure.py
CHANGED
|
@@ -433,7 +433,7 @@ class StructureQuery(CoreQuery, frozen=True, omit_defaults=True):
|
|
|
433
433
|
u += f"/{i}" if all(ck) else ""
|
|
434
434
|
u += f"?detail={self.detail.value}&references={self.references.value}"
|
|
435
435
|
if self.as_of:
|
|
436
|
-
u += f
|
|
436
|
+
u += f"&asOf={self.as_of.isoformat('T', 'seconds')}"
|
|
437
437
|
return u
|
|
438
438
|
|
|
439
439
|
def _create_short_query(self, ver: ApiVersion) -> str:
|
|
@@ -492,7 +492,7 @@ class StructureQuery(CoreQuery, frozen=True, omit_defaults=True):
|
|
|
492
492
|
else ""
|
|
493
493
|
)
|
|
494
494
|
u += (
|
|
495
|
-
f
|
|
495
|
+
f"asOf={self.as_of.isoformat('T', 'seconds')}"
|
|
496
496
|
if self.as_of
|
|
497
497
|
else ""
|
|
498
498
|
)
|
|
@@ -14,12 +14,21 @@ from pysdmx.io.json.fusion.messages.dataflow import (
|
|
|
14
14
|
FusionDataflowMessage,
|
|
15
15
|
FusionDataflowsMessage,
|
|
16
16
|
)
|
|
17
|
+
from pysdmx.io.json.fusion.messages.dsd import FusionDataStructuresMessage
|
|
17
18
|
from pysdmx.io.json.fusion.messages.map import (
|
|
18
19
|
FusionMappingMessage,
|
|
19
20
|
FusionRepresentationMapMessage,
|
|
20
21
|
)
|
|
22
|
+
from pysdmx.io.json.fusion.messages.metadataflow import (
|
|
23
|
+
FusionMetadataflowsMessage,
|
|
24
|
+
)
|
|
25
|
+
from pysdmx.io.json.fusion.messages.mpa import (
|
|
26
|
+
FusionMetadataProvisionAgreementMessage,
|
|
27
|
+
)
|
|
28
|
+
from pysdmx.io.json.fusion.messages.msd import FusionMetadataStructuresMessage
|
|
21
29
|
from pysdmx.io.json.fusion.messages.org import (
|
|
22
30
|
FusionAgencyMessage,
|
|
31
|
+
FusionMetadataProviderMessage,
|
|
23
32
|
FusionProviderMessage,
|
|
24
33
|
)
|
|
25
34
|
from pysdmx.io.json.fusion.messages.pa import (
|
|
@@ -41,6 +50,10 @@ __all__ = [
|
|
|
41
50
|
"FusionDataflowMessage",
|
|
42
51
|
"FusionDataflowsMessage",
|
|
43
52
|
"FusionMappingMessage",
|
|
53
|
+
"FusionMetadataflowsMessage",
|
|
54
|
+
"FusionMetadataProviderMessage",
|
|
55
|
+
"FusionMetadataProvisionAgreementMessage",
|
|
56
|
+
"FusionMetadataStructuresMessage",
|
|
44
57
|
"FusionRepresentationMapMessage",
|
|
45
58
|
"FusionAgencyMessage",
|
|
46
59
|
"FusionProviderMessage",
|
|
@@ -48,4 +61,5 @@ __all__ = [
|
|
|
48
61
|
"FusionSchemaMessage",
|
|
49
62
|
"FusionTransfoMsg",
|
|
50
63
|
"FusionPAMessage",
|
|
64
|
+
"FusionDataStructuresMessage",
|
|
51
65
|
]
|
|
@@ -20,10 +20,12 @@ from pysdmx.model import (
|
|
|
20
20
|
Codelist,
|
|
21
21
|
Component,
|
|
22
22
|
Components,
|
|
23
|
+
DataStructureDefinition,
|
|
23
24
|
DataType,
|
|
24
25
|
Facets,
|
|
25
26
|
Role,
|
|
26
27
|
)
|
|
28
|
+
from pysdmx.model.dataflow import Group
|
|
27
29
|
from pysdmx.util import parse_item_urn
|
|
28
30
|
|
|
29
31
|
|
|
@@ -248,7 +250,7 @@ class FusionDataStructure(Struct, frozen=True, rename={"agency": "agencyId"}):
|
|
|
248
250
|
measures: Sequence[FusionMeasure] = ()
|
|
249
251
|
attributeList: Optional[FusionAttributes] = None
|
|
250
252
|
groups: Sequence[FusionGroup] = ()
|
|
251
|
-
|
|
253
|
+
descriptions: Optional[Sequence[FusionString]] = None
|
|
252
254
|
version: str = "1.0"
|
|
253
255
|
|
|
254
256
|
def get_components(
|
|
@@ -273,3 +275,52 @@ class FusionDataStructure(Struct, frozen=True, rename={"agency": "agencyId"}):
|
|
|
273
275
|
)
|
|
274
276
|
)
|
|
275
277
|
return Components(comps)
|
|
278
|
+
|
|
279
|
+
def to_model(
|
|
280
|
+
self,
|
|
281
|
+
cs: Sequence[FusionConceptScheme],
|
|
282
|
+
cls: Sequence[FusionCodelist],
|
|
283
|
+
vls: Sequence[FusionCodelist],
|
|
284
|
+
constraints: Sequence[FusionContentConstraint],
|
|
285
|
+
) -> DataStructureDefinition:
|
|
286
|
+
"""Map to pysdmx model class."""
|
|
287
|
+
enums: list[FusionCodelist] = []
|
|
288
|
+
enums.extend(cls)
|
|
289
|
+
enums.extend(vls)
|
|
290
|
+
cmps = self.get_components(cs, enums, constraints)
|
|
291
|
+
grps = [
|
|
292
|
+
Group(g.id, dimensions=g.dimensionReferences) for g in self.groups
|
|
293
|
+
]
|
|
294
|
+
return DataStructureDefinition(
|
|
295
|
+
id=self.id,
|
|
296
|
+
name=self.names[0].value,
|
|
297
|
+
agency=self.agency,
|
|
298
|
+
description=(
|
|
299
|
+
self.descriptions[0].value if self.descriptions else None
|
|
300
|
+
),
|
|
301
|
+
version=self.version,
|
|
302
|
+
components=cmps,
|
|
303
|
+
groups=grps,
|
|
304
|
+
)
|
|
305
|
+
|
|
306
|
+
|
|
307
|
+
class FusionDataStructuresMessage(Struct, frozen=True, omit_defaults=True):
|
|
308
|
+
"""Fusion-JSON payload for /datastructure queries."""
|
|
309
|
+
|
|
310
|
+
ConceptScheme: Sequence[FusionConceptScheme]
|
|
311
|
+
DataStructure: Sequence[FusionDataStructure]
|
|
312
|
+
ValueList: Sequence[FusionCodelist] = ()
|
|
313
|
+
Codelist: Sequence[FusionCodelist] = ()
|
|
314
|
+
DataConstraint: Sequence[FusionContentConstraint] = ()
|
|
315
|
+
|
|
316
|
+
def to_model(self) -> Sequence[DataStructureDefinition]:
|
|
317
|
+
"""Returns the requested data structures."""
|
|
318
|
+
return [
|
|
319
|
+
dsd.to_model(
|
|
320
|
+
self.ConceptScheme,
|
|
321
|
+
self.Codelist,
|
|
322
|
+
self.ValueList,
|
|
323
|
+
self.DataConstraint,
|
|
324
|
+
)
|
|
325
|
+
for dsd in self.DataStructure
|
|
326
|
+
]
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"""Collection of Fusion-JSON schemas for dataflow queries."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional, Sequence
|
|
4
|
+
|
|
5
|
+
from msgspec import Struct
|
|
6
|
+
|
|
7
|
+
from pysdmx.io.json.fusion.messages.core import FusionString
|
|
8
|
+
from pysdmx.model import Metadataflow as MDF
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class FusionMetadataflow(Struct, frozen=True, rename={"agency": "agencyId"}):
|
|
12
|
+
"""Fusion-JSON payload for a metadataflow."""
|
|
13
|
+
|
|
14
|
+
id: str
|
|
15
|
+
agency: str
|
|
16
|
+
names: Sequence[FusionString]
|
|
17
|
+
metadataStructureRef: str
|
|
18
|
+
targets: Sequence[str]
|
|
19
|
+
descriptions: Optional[Sequence[FusionString]] = None
|
|
20
|
+
version: str = "1.0"
|
|
21
|
+
|
|
22
|
+
def to_model(self) -> MDF:
|
|
23
|
+
"""Converts a FusionMetadataflow to a standard metadataflow."""
|
|
24
|
+
return MDF(
|
|
25
|
+
id=self.id,
|
|
26
|
+
agency=self.agency,
|
|
27
|
+
name=self.names[0].value if self.names else None,
|
|
28
|
+
description=(
|
|
29
|
+
self.descriptions[0].value if self.descriptions else None
|
|
30
|
+
),
|
|
31
|
+
version=self.version,
|
|
32
|
+
structure=self.metadataStructureRef,
|
|
33
|
+
targets=self.targets,
|
|
34
|
+
)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
class FusionMetadataflowsMessage(Struct, frozen=True):
|
|
38
|
+
"""Fusion-JSON payload for /metadataflow queries."""
|
|
39
|
+
|
|
40
|
+
Metadataflow: Sequence[FusionMetadataflow]
|
|
41
|
+
|
|
42
|
+
def to_model(self) -> Sequence[MDF]:
|
|
43
|
+
"""Returns the requested metadataflow details."""
|
|
44
|
+
return [df.to_model() for df in self.Metadataflow]
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"""Collection of Fusion-JSON schemas for provision agreements."""
|
|
2
|
+
|
|
3
|
+
from typing import Optional, Sequence
|
|
4
|
+
|
|
5
|
+
from msgspec import Struct
|
|
6
|
+
|
|
7
|
+
from pysdmx.io.json.fusion.messages.core import FusionString
|
|
8
|
+
from pysdmx.model import MetadataProvisionAgreement as MPA
|
|
9
|
+
|
|
10
|
+
|
|
11
|
+
class FusionMetadataProvisionAgreement(
|
|
12
|
+
Struct, frozen=True, rename={"agency": "agencyId"}
|
|
13
|
+
):
|
|
14
|
+
"""Fusion-JSON payload for a metadata provision agreement."""
|
|
15
|
+
|
|
16
|
+
id: str
|
|
17
|
+
names: Sequence[FusionString]
|
|
18
|
+
agency: str
|
|
19
|
+
metadataflowRef: str
|
|
20
|
+
metadataproviderRef: str
|
|
21
|
+
descriptions: Optional[Sequence[FusionString]] = None
|
|
22
|
+
version: str = "1.0"
|
|
23
|
+
|
|
24
|
+
def to_model(self) -> MPA:
|
|
25
|
+
"""Converts a JsonPA to a standard provision agreement."""
|
|
26
|
+
description = self.descriptions[0].value if self.descriptions else None
|
|
27
|
+
return MPA(
|
|
28
|
+
id=self.id,
|
|
29
|
+
name=self.names[0].value,
|
|
30
|
+
agency=self.agency,
|
|
31
|
+
description=description,
|
|
32
|
+
version=self.version,
|
|
33
|
+
metadata_provider=self.metadataproviderRef,
|
|
34
|
+
metadataflow=self.metadataflowRef,
|
|
35
|
+
)
|
|
36
|
+
|
|
37
|
+
|
|
38
|
+
class FusionMetadataProvisionAgreementMessage(Struct, frozen=True):
|
|
39
|
+
"""Fusion-JSON payload for /metadataprovisionagreement queries."""
|
|
40
|
+
|
|
41
|
+
MetadataProvisionAgreement: Sequence[FusionMetadataProvisionAgreement]
|
|
42
|
+
|
|
43
|
+
def to_model(self) -> Sequence[MPA]:
|
|
44
|
+
"""Returns the requested metadata provision agreements."""
|
|
45
|
+
return [c.to_model() for c in self.MetadataProvisionAgreement]
|