pysdmx 1.10.1__tar.gz → 1.12.0__tar.gz
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-1.10.1 → pysdmx-1.12.0}/PKG-INFO +7 -1
- {pysdmx-1.10.1 → pysdmx-1.12.0}/pyproject.toml +10 -5
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/__init__.py +1 -1
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/fmr/maintenance.py +10 -5
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/input_processor.py +4 -0
- pysdmx-1.12.0/src/pysdmx/io/json/fusion/messages/constraint.py +42 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/fusion/messages/dsd.py +20 -14
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/fusion/messages/msd.py +6 -9
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/fusion/messages/schema.py +20 -1
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/messages/core.py +12 -5
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/messages/dsd.py +11 -17
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/messages/msd.py +2 -5
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/messages/report.py +7 -3
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/messages/schema.py +38 -5
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/messages/structure.py +7 -3
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/reader/metadata.py +3 -3
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/reader/structure.py +3 -3
- pysdmx-1.12.0/src/pysdmx/io/json/sdmxjson2/writer/_helper.py +118 -0
- pysdmx-1.12.0/src/pysdmx/io/json/sdmxjson2/writer/v2_0/__init__.py +1 -0
- pysdmx-1.12.0/src/pysdmx/io/json/sdmxjson2/writer/v2_0/metadata.py +33 -0
- pysdmx-1.12.0/src/pysdmx/io/json/sdmxjson2/writer/v2_0/structure.py +33 -0
- pysdmx-1.12.0/src/pysdmx/io/json/sdmxjson2/writer/v2_1/__init__.py +1 -0
- pysdmx-1.12.0/src/pysdmx/io/json/sdmxjson2/writer/v2_1/metadata.py +31 -0
- pysdmx-1.12.0/src/pysdmx/io/json/sdmxjson2/writer/v2_1/structure.py +33 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/reader.py +12 -3
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/writer.py +13 -3
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/__ss_aux_reader.py +39 -17
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/__structure_aux_reader.py +221 -33
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/__structure_aux_writer.py +304 -5
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/__tokens.py +12 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/__write_aux.py +9 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx21/writer/generic.py +2 -2
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/model/dataflow.py +11 -2
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/toolkit/pd/_data_utils.py +1 -1
- pysdmx-1.10.1/src/pysdmx/io/json/fusion/messages/constraint.py +0 -21
- pysdmx-1.10.1/src/pysdmx/io/json/sdmxjson2/writer/metadata.py +0 -60
- pysdmx-1.10.1/src/pysdmx/io/json/sdmxjson2/writer/structure.py +0 -61
- {pysdmx-1.10.1 → pysdmx-1.12.0}/LICENSE +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/README.rst +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/__extras_check.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/dc/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/dc/_api.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/dc/query/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/dc/query/_model.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/dc/query/_parsing_model.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/dc/query/_parsing_util.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/dc/query/_py_parser.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/dc/query/_sql_parser.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/dc/query/util.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/fmr/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/gds/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/qb/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/qb/availability.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/qb/data.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/qb/gds.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/qb/refmeta.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/qb/registration.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/qb/schema.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/qb/service.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/qb/structure.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/api/qb/util.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/errors.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/csv/__csv_aux_reader.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/csv/__csv_aux_writer.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/csv/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/csv/sdmx10/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/csv/sdmx10/reader/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/csv/sdmx10/writer/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/csv/sdmx20/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/csv/sdmx20/reader/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/csv/sdmx20/writer/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/csv/sdmx21/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/csv/sdmx21/reader/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/csv/sdmx21/writer/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/format.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/fusion/messages/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/fusion/messages/category.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/fusion/messages/code.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/fusion/messages/concept.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/fusion/messages/core.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/fusion/messages/dataflow.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/fusion/messages/map.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/fusion/messages/metadataflow.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/fusion/messages/mpa.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/fusion/messages/org.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/fusion/messages/pa.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/fusion/messages/report.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/fusion/messages/vtl.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/fusion/reader/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/gds/messages/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/gds/messages/agencies.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/gds/messages/catalog.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/gds/messages/sdmx_api.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/gds/messages/services.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/gds/messages/urn_resolver.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/gds/reader/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/messages/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/messages/agency.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/messages/category.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/messages/code.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/messages/concept.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/messages/constraint.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/messages/dataflow.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/messages/map.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/messages/metadataflow.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/messages/mpa.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/messages/pa.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/messages/provider.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/messages/vtl.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/reader/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/reader/doc_validation.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/json/sdmxjson2/writer/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/pd.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/serde.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/__allowed_lxml_errors.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/__data_aux.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/__parse_xml.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/__write_data_aux.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/__write_structure_specific_aux.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/config.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/doc_validation.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/header.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx21/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx21/reader/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx21/reader/error.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx21/reader/generic.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx21/reader/structure.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx21/reader/structure_specific.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx21/reader/submission.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx21/writer/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx21/writer/error.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx21/writer/structure.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx21/writer/structure_specific.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx30/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx30/reader/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx30/reader/structure.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx30/reader/structure_specific.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx30/writer/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx30/writer/structure.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx30/writer/structure_specific.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx31/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx31/reader/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx31/reader/structure.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx31/reader/structure_specific.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx31/writer/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx31/writer/structure.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/sdmx31/writer/structure_specific.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/io/xml/utils.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/model/__base.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/model/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/model/category.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/model/code.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/model/concept.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/model/constraint.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/model/dataset.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/model/gds.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/model/map.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/model/message.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/model/metadata.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/model/organisation.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/model/submission.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/model/vtl.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/py.typed +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/toolkit/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/toolkit/pd/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/toolkit/vtl/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/toolkit/vtl/_validations.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/toolkit/vtl/convert.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/toolkit/vtl/script_generation.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/toolkit/vtl/validation.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/util/__init__.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/util/_date_pattern_map.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/util/_model_utils.py +0 -0
- {pysdmx-1.10.1 → pysdmx-1.12.0}/src/pysdmx/util/_net_utils.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pysdmx
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.12.0
|
|
4
4
|
Summary: Your opinionated Python SDMX library
|
|
5
5
|
License: Apache-2.0
|
|
6
6
|
License-File: LICENSE
|
|
@@ -24,6 +24,12 @@ Requires-Dist: jsonschema (>=4.10) ; extra == "json"
|
|
|
24
24
|
Requires-Dist: lxml (>=5.2) ; extra == "all"
|
|
25
25
|
Requires-Dist: lxml (>=5.2) ; extra == "xml"
|
|
26
26
|
Requires-Dist: msgspec (>=0)
|
|
27
|
+
Requires-Dist: numpy (>=2.0.2,<2.1) ; (python_version < "3.13") and (extra == "all")
|
|
28
|
+
Requires-Dist: numpy (>=2.0.2,<2.1) ; (python_version < "3.13") and (extra == "data")
|
|
29
|
+
Requires-Dist: numpy (>=2.0.2,<2.1) ; (python_version < "3.13") and (extra == "vtl")
|
|
30
|
+
Requires-Dist: numpy (>=2.1.0,<2.3) ; (python_version >= "3.13") and (extra == "all")
|
|
31
|
+
Requires-Dist: numpy (>=2.1.0,<2.3) ; (python_version >= "3.13") and (extra == "data")
|
|
32
|
+
Requires-Dist: numpy (>=2.1.0,<2.3) ; (python_version >= "3.13") and (extra == "vtl")
|
|
27
33
|
Requires-Dist: pandas (>=2.1.4) ; extra == "all"
|
|
28
34
|
Requires-Dist: pandas (>=2.1.4) ; extra == "data"
|
|
29
35
|
Requires-Dist: parsy (>=2.1)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "pysdmx"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.12.0"
|
|
4
4
|
description = "Your opinionated Python SDMX library"
|
|
5
5
|
license = { text = "Apache-2.0" }
|
|
6
6
|
readme = "README.rst"
|
|
@@ -35,12 +35,13 @@ documentation = "https://bis-med-it.github.io/pysdmx"
|
|
|
35
35
|
"Bug Tracker" = "https://bis-med-it.github.io/pysdmx/issues"
|
|
36
36
|
|
|
37
37
|
[project.optional-dependencies]
|
|
38
|
-
data = ["pandas>=2.1.4"]
|
|
38
|
+
data = ["pandas>=2.1.4", "numpy>=2.0.2,<2.1; python_version < '3.13'", "numpy>=2.1.0,<2.3; python_version >= '3.13'"]
|
|
39
39
|
dc = ["python-dateutil>=2.8.2"]
|
|
40
|
-
vtl = ["vtlengine>=1.2,<2.0"]
|
|
40
|
+
vtl = ["vtlengine>=1.2,<2.0", "numpy>=2.0.2,<2.1; python_version < '3.13'", "numpy>=2.1.0,<2.3; python_version >= '3.13'"]
|
|
41
41
|
json = ["sdmxschemas>=1.0.0", "jsonschema>=4.10"]
|
|
42
42
|
xml = ["lxml>=5.2", "xmltodict>=0.13", "sdmxschemas>=1.0.0"]
|
|
43
|
-
all = ["lxml>=5.2", "xmltodict>=0.13", "sdmxschemas>=1.0.0", "pandas>=2.1.4", "python-dateutil>=2.8.2", "vtlengine>=1.2,<2.0"
|
|
43
|
+
all = ["lxml>=5.2", "xmltodict>=0.13", "sdmxschemas>=1.0.0", "pandas>=2.1.4", "python-dateutil>=2.8.2", "vtlengine>=1.2,<2.0",
|
|
44
|
+
"numpy>=2.0.2,<2.1; python_version < '3.13'", "numpy>=2.1.0,<2.3; python_version >= '3.13'"]
|
|
44
45
|
|
|
45
46
|
[tool.poetry]
|
|
46
47
|
requires-poetry = ">=2.0"
|
|
@@ -49,6 +50,7 @@ requires-poetry = ">=2.0"
|
|
|
49
50
|
python = ">=3.9,<4.0"
|
|
50
51
|
|
|
51
52
|
[tool.poetry.group.dev.dependencies]
|
|
53
|
+
coverage = ">=7.0,!=7.13.0,<8.0"
|
|
52
54
|
ruff = ">=0.13.2"
|
|
53
55
|
mypy = "^1.18.2"
|
|
54
56
|
pytest = "^8.3.2"
|
|
@@ -59,7 +61,10 @@ pyroma = "^4.2"
|
|
|
59
61
|
lxml-stubs = "^0.5.1"
|
|
60
62
|
types-xmltodict = "^0.13.0.3"
|
|
61
63
|
types-python-dateutil = "^2.9.0.20240316"
|
|
62
|
-
pandas-stubs =
|
|
64
|
+
pandas-stubs = [
|
|
65
|
+
{ version = ">2.1.4.231227,<2.2.3.240901", python = "<3.13" },
|
|
66
|
+
{ version = ">=2.2.3.241009", python = ">=3.13" }
|
|
67
|
+
]
|
|
63
68
|
types-jsonschema = "^4.25.1.20251009"
|
|
64
69
|
|
|
65
70
|
[tool.poetry.group.docs.dependencies]
|
|
@@ -64,9 +64,7 @@ class RegistryMaintenanceClient:
|
|
|
64
64
|
timeout: The maximum number of seconds to wait before considering
|
|
65
65
|
that a request timed out. Defaults to 10 seconds.
|
|
66
66
|
"""
|
|
67
|
-
|
|
68
|
-
api_endpoint = api_endpoint[0:-1]
|
|
69
|
-
self._api_endpoint = f"{api_endpoint}"
|
|
67
|
+
self._api_endpoint = self.__sanitize_endpoint(api_endpoint)
|
|
70
68
|
self._user = user
|
|
71
69
|
self._password = password
|
|
72
70
|
self._timeout = timeout
|
|
@@ -87,7 +85,6 @@ class RegistryMaintenanceClient:
|
|
|
87
85
|
) -> None:
|
|
88
86
|
with httpx.Client(verify=self._ssl_context) as client:
|
|
89
87
|
try:
|
|
90
|
-
url = f"{endpoint}"
|
|
91
88
|
auth = httpx.BasicAuth(self._user, self._password)
|
|
92
89
|
headers = {
|
|
93
90
|
"Content-Type": "application/text",
|
|
@@ -99,7 +96,7 @@ class RegistryMaintenanceClient:
|
|
|
99
96
|
serializer = serializers.structure_message
|
|
100
97
|
bodyjs = self._encoder.encode(serializer.from_model(message))
|
|
101
98
|
r = client.post(
|
|
102
|
-
|
|
99
|
+
endpoint,
|
|
103
100
|
headers=headers,
|
|
104
101
|
content=bodyjs,
|
|
105
102
|
timeout=self._timeout,
|
|
@@ -156,3 +153,11 @@ class RegistryMaintenanceClient:
|
|
|
156
153
|
message = MetadataMessage(header=header, reports=reports)
|
|
157
154
|
endpoint = f"{self._api_endpoint}/ws/secure/sdmx/v2/metadata"
|
|
158
155
|
return self.__post(message, action, endpoint)
|
|
156
|
+
|
|
157
|
+
def __sanitize_endpoint(self, endpoint: str) -> str:
|
|
158
|
+
if endpoint.endswith("/"):
|
|
159
|
+
endpoint = endpoint[0:-1]
|
|
160
|
+
endpoint = endpoint.replace("/ws/secure/sdmx/v2/metadata", "")
|
|
161
|
+
endpoint = endpoint.replace("/sdmx/v2", "")
|
|
162
|
+
endpoint = endpoint.replace("/ws/secure/sdmxapi/rest", "")
|
|
163
|
+
return endpoint
|
|
@@ -97,6 +97,10 @@ def __get_sdmx_json_flavour(input_str: str) -> Tuple[str, Format]:
|
|
|
97
97
|
return input_str, Format.STRUCTURE_SDMX_JSON_2_0_0
|
|
98
98
|
elif "2.0.0/sdmx-json-metadata-schema.json" in flavour_check:
|
|
99
99
|
return input_str, Format.REFMETA_SDMX_JSON_2_0_0
|
|
100
|
+
elif "2.1/sdmx-json-structure-schema.json" in flavour_check:
|
|
101
|
+
return input_str, Format.STRUCTURE_SDMX_JSON_2_1_0
|
|
102
|
+
elif "2.1/sdmx-json-metadata-schema.json" in flavour_check:
|
|
103
|
+
return input_str, Format.REFMETA_SDMX_JSON_2_1_0
|
|
100
104
|
elif "sdmx-json" in flavour_check:
|
|
101
105
|
raise NotImplemented(
|
|
102
106
|
"Unsupported format", "This flavour of SDMX-JSON is not supported."
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
"""Collection of Fusion-JSON schemas for content constraints."""
|
|
2
|
+
|
|
3
|
+
from typing import Dict, List, Optional, Sequence
|
|
4
|
+
|
|
5
|
+
from msgspec import Struct
|
|
6
|
+
|
|
7
|
+
|
|
8
|
+
class FusionKeyValue(Struct, frozen=True):
|
|
9
|
+
"""Fusion-JSON payload for the list of allowed values per component."""
|
|
10
|
+
|
|
11
|
+
values: Sequence[str]
|
|
12
|
+
|
|
13
|
+
|
|
14
|
+
class FusionKeySet(Struct, frozen=True):
|
|
15
|
+
"""Fusion-JSON payload for the list of allowed values per component."""
|
|
16
|
+
|
|
17
|
+
dims: Sequence[str]
|
|
18
|
+
rows: Sequence[Sequence[str]]
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
class FusionContentConstraint(Struct, frozen=True):
|
|
22
|
+
"""Fusion-JSON payload for a content constraint."""
|
|
23
|
+
|
|
24
|
+
includeCube: Dict[str, FusionKeyValue] = {}
|
|
25
|
+
includeSeries: Optional[FusionKeySet] = None
|
|
26
|
+
|
|
27
|
+
def to_map(self) -> Dict[str, Sequence[str]]:
|
|
28
|
+
"""Gets the list of allowed values for a component."""
|
|
29
|
+
return {k: v.values for k, v in self.includeCube.items()}
|
|
30
|
+
|
|
31
|
+
def get_series(self, dimensions: List[str]) -> Sequence[str]:
|
|
32
|
+
"""Get the list of series defined in the keyset."""
|
|
33
|
+
if self.includeSeries:
|
|
34
|
+
series = []
|
|
35
|
+
for r in self.includeSeries.rows:
|
|
36
|
+
s = dict.fromkeys(dimensions, "*")
|
|
37
|
+
for idx, cd in enumerate(self.includeSeries.dims):
|
|
38
|
+
s[cd] = r[idx]
|
|
39
|
+
series.append(".".join(s.values()))
|
|
40
|
+
return series
|
|
41
|
+
else:
|
|
42
|
+
return []
|
|
@@ -20,6 +20,7 @@ from pysdmx.model import (
|
|
|
20
20
|
Codelist,
|
|
21
21
|
Component,
|
|
22
22
|
Components,
|
|
23
|
+
Concept,
|
|
23
24
|
DataStructureDefinition,
|
|
24
25
|
DataType,
|
|
25
26
|
Facets,
|
|
@@ -32,14 +33,15 @@ from pysdmx.util import parse_item_urn
|
|
|
32
33
|
def _find_concept(
|
|
33
34
|
cs: Sequence[FusionConceptScheme],
|
|
34
35
|
urn: str,
|
|
35
|
-
) -> FusionConcept:
|
|
36
|
+
) -> Optional[FusionConcept]:
|
|
36
37
|
r = parse_item_urn(urn)
|
|
37
38
|
f = [
|
|
38
39
|
m
|
|
39
40
|
for m in cs
|
|
40
41
|
if (m.agency == r.agency and m.id == r.id and m.version == r.version)
|
|
41
42
|
]
|
|
42
|
-
|
|
43
|
+
m = [c for c in f[0].items if c.id == r.item_id]
|
|
44
|
+
return m[0] if m else None
|
|
43
45
|
|
|
44
46
|
|
|
45
47
|
def _get_representation(
|
|
@@ -112,12 +114,13 @@ class FusionAttribute(Struct, frozen=True):
|
|
|
112
114
|
groups: Sequence[FusionGroup],
|
|
113
115
|
) -> Component:
|
|
114
116
|
"""Returns an attribute."""
|
|
115
|
-
|
|
117
|
+
m = _find_concept(cs, self.concept) if cs else None
|
|
118
|
+
c = m.to_model(cls) if m else parse_item_urn(self.concept)
|
|
116
119
|
dt, facets, codes, ab = _get_representation(
|
|
117
120
|
self.id, self.representation, cls, cons
|
|
118
121
|
)
|
|
119
122
|
lvl = self.__derive_level(groups)
|
|
120
|
-
desc = c.
|
|
123
|
+
desc = c.description if isinstance(c, Concept) else None
|
|
121
124
|
if self.representation and self.representation.representation:
|
|
122
125
|
local_enum_ref = self.representation.representation
|
|
123
126
|
else:
|
|
@@ -126,10 +129,10 @@ class FusionAttribute(Struct, frozen=True):
|
|
|
126
129
|
id=self.id,
|
|
127
130
|
required=self.mandatory,
|
|
128
131
|
role=Role.ATTRIBUTE,
|
|
129
|
-
concept=c
|
|
132
|
+
concept=c,
|
|
130
133
|
local_dtype=dt,
|
|
131
134
|
local_facets=facets,
|
|
132
|
-
name=c.
|
|
135
|
+
name=c.name if isinstance(c, Concept) else None,
|
|
133
136
|
description=desc,
|
|
134
137
|
local_codes=codes,
|
|
135
138
|
attachment_level=lvl,
|
|
@@ -160,6 +163,7 @@ class FusionDimension(Struct, frozen=True):
|
|
|
160
163
|
id: str
|
|
161
164
|
concept: str
|
|
162
165
|
representation: Optional[FusionRepresentation] = None
|
|
166
|
+
isTimeDimension: bool = False
|
|
163
167
|
|
|
164
168
|
def to_model(
|
|
165
169
|
self,
|
|
@@ -168,11 +172,12 @@ class FusionDimension(Struct, frozen=True):
|
|
|
168
172
|
cons: Dict[str, Sequence[str]],
|
|
169
173
|
) -> Component:
|
|
170
174
|
"""Returns a dimension."""
|
|
171
|
-
|
|
175
|
+
m = _find_concept(cs, self.concept) if cs else None
|
|
176
|
+
c = m.to_model(cls) if m else parse_item_urn(self.concept)
|
|
172
177
|
dt, facets, codes, ab = _get_representation(
|
|
173
178
|
self.id, self.representation, cls, cons
|
|
174
179
|
)
|
|
175
|
-
desc = c.
|
|
180
|
+
desc = c.description if isinstance(c, Concept) else None
|
|
176
181
|
if self.representation and self.representation.representation:
|
|
177
182
|
local_enum_ref = self.representation.representation
|
|
178
183
|
else:
|
|
@@ -181,10 +186,10 @@ class FusionDimension(Struct, frozen=True):
|
|
|
181
186
|
id=self.id,
|
|
182
187
|
required=True,
|
|
183
188
|
role=Role.DIMENSION,
|
|
184
|
-
concept=c
|
|
189
|
+
concept=c,
|
|
185
190
|
local_dtype=dt,
|
|
186
191
|
local_facets=facets,
|
|
187
|
-
name=c.
|
|
192
|
+
name=c.name if isinstance(c, Concept) else None,
|
|
188
193
|
description=desc,
|
|
189
194
|
local_codes=codes,
|
|
190
195
|
array_def=ab,
|
|
@@ -222,11 +227,12 @@ class FusionMeasure(Struct, frozen=True):
|
|
|
222
227
|
cons: Dict[str, Sequence[str]],
|
|
223
228
|
) -> Component:
|
|
224
229
|
"""Returns a measure."""
|
|
225
|
-
|
|
230
|
+
m = _find_concept(cs, self.concept) if cs else None
|
|
231
|
+
c = m.to_model(cls) if m else parse_item_urn(self.concept)
|
|
226
232
|
dt, facets, codes, ab = _get_representation(
|
|
227
233
|
self.id, self.representation, cls, cons
|
|
228
234
|
)
|
|
229
|
-
desc = c.
|
|
235
|
+
desc = c.description if isinstance(c, Concept) else None
|
|
230
236
|
if self.representation and self.representation.representation:
|
|
231
237
|
local_enum_ref = self.representation.representation
|
|
232
238
|
else:
|
|
@@ -235,10 +241,10 @@ class FusionMeasure(Struct, frozen=True):
|
|
|
235
241
|
id=self.id,
|
|
236
242
|
required=self.mandatory,
|
|
237
243
|
role=Role.MEASURE,
|
|
238
|
-
concept=c
|
|
244
|
+
concept=c,
|
|
239
245
|
local_dtype=dt,
|
|
240
246
|
local_facets=facets,
|
|
241
|
-
name=c.
|
|
247
|
+
name=c.name if isinstance(c, Concept) else None,
|
|
242
248
|
description=desc,
|
|
243
249
|
local_codes=codes,
|
|
244
250
|
array_def=ab,
|
|
@@ -14,13 +14,9 @@ from pysdmx.io.json.fusion.messages.dsd import (
|
|
|
14
14
|
_find_concept,
|
|
15
15
|
_get_representation,
|
|
16
16
|
)
|
|
17
|
-
from pysdmx.model import
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
)
|
|
21
|
-
from pysdmx.model import (
|
|
22
|
-
MetadataStructure as MSD,
|
|
23
|
-
)
|
|
17
|
+
from pysdmx.model import ArrayBoundaries, MetadataComponent
|
|
18
|
+
from pysdmx.model import MetadataStructure as MSD
|
|
19
|
+
from pysdmx.util import parse_item_urn
|
|
24
20
|
|
|
25
21
|
|
|
26
22
|
class FusionMetadataAttribute(Struct, frozen=True):
|
|
@@ -40,7 +36,8 @@ class FusionMetadataAttribute(Struct, frozen=True):
|
|
|
40
36
|
cls: Sequence[FusionCodelist],
|
|
41
37
|
) -> MetadataComponent:
|
|
42
38
|
"""Returns an attribute."""
|
|
43
|
-
|
|
39
|
+
m = _find_concept(cs, self.concept) if cs else None
|
|
40
|
+
c = m.to_model(cls) if m else parse_item_urn(self.concept)
|
|
44
41
|
dt, facets, codes, _ = _get_representation(
|
|
45
42
|
self.id, self.representation, cls, {}
|
|
46
43
|
)
|
|
@@ -60,7 +57,7 @@ class FusionMetadataAttribute(Struct, frozen=True):
|
|
|
60
57
|
return MetadataComponent(
|
|
61
58
|
self.id,
|
|
62
59
|
is_presentational=self.presentational, # type: ignore[arg-type]
|
|
63
|
-
concept=c
|
|
60
|
+
concept=c,
|
|
64
61
|
local_dtype=dt,
|
|
65
62
|
local_facets=facets,
|
|
66
63
|
local_codes=codes,
|
|
@@ -65,6 +65,25 @@ class FusionSchemaMessage(msgspec.Struct, frozen=True):
|
|
|
65
65
|
mapped_grps = [
|
|
66
66
|
Group(g.id, dimensions=g.dimensionReferences) for g in grps
|
|
67
67
|
]
|
|
68
|
+
keys: List[str] = []
|
|
69
|
+
for dc in self.DataConstraint:
|
|
70
|
+
keys.extend(
|
|
71
|
+
dc.get_series(
|
|
72
|
+
[
|
|
73
|
+
d.id
|
|
74
|
+
for d in self.DataStructure[0].dimensionList.dimensions
|
|
75
|
+
if not d.isTimeDimension
|
|
76
|
+
]
|
|
77
|
+
)
|
|
78
|
+
)
|
|
79
|
+
keys = list(set(keys)) if keys else None # type: ignore[assignment]
|
|
68
80
|
return Schema(
|
|
69
|
-
context,
|
|
81
|
+
context,
|
|
82
|
+
agency,
|
|
83
|
+
id_,
|
|
84
|
+
comps,
|
|
85
|
+
version,
|
|
86
|
+
urns,
|
|
87
|
+
groups=mapped_grps,
|
|
88
|
+
keys=keys,
|
|
70
89
|
)
|
|
@@ -307,17 +307,24 @@ class JsonHeader(msgspec.Struct, frozen=True, omit_defaults=True):
|
|
|
307
307
|
self,
|
|
308
308
|
header: Header,
|
|
309
309
|
msg_type: Literal["structure", "metadata"] = "structure",
|
|
310
|
+
msg_version: Literal["2.0.0", "2.1"] = "2.0.0",
|
|
310
311
|
) -> "JsonHeader":
|
|
311
312
|
"""Create an SDMX-JSON header from a pysdmx Header."""
|
|
313
|
+
if msg_version == "2.0.0":
|
|
314
|
+
schema = (
|
|
315
|
+
"https://raw.githubusercontent.com/sdmx-twg/sdmx-json/"
|
|
316
|
+
f"develop/structure-message/tools/schemas/{msg_version}/"
|
|
317
|
+
f"sdmx-json-{msg_type}-schema.json"
|
|
318
|
+
)
|
|
319
|
+
else:
|
|
320
|
+
schema = (
|
|
321
|
+
f"https://json.sdmx.org/2.1/sdmx-json-{msg_type}-schema.json"
|
|
322
|
+
)
|
|
312
323
|
return JsonHeader(
|
|
313
324
|
header.id,
|
|
314
325
|
header.prepared,
|
|
315
326
|
header.sender,
|
|
316
327
|
header.test,
|
|
317
328
|
receivers=header.receiver if header.receiver else None,
|
|
318
|
-
schema=
|
|
319
|
-
"https://raw.githubusercontent.com/sdmx-twg/sdmx-json/"
|
|
320
|
-
"develop/structure-message/tools/schemas/2.0.0/"
|
|
321
|
-
f"sdmx-json-{msg_type}-schema.json"
|
|
322
|
-
),
|
|
329
|
+
schema=schema,
|
|
323
330
|
)
|
|
@@ -34,14 +34,17 @@ from pysdmx.model.dataflow import Group
|
|
|
34
34
|
from pysdmx.util import parse_item_urn
|
|
35
35
|
|
|
36
36
|
|
|
37
|
-
def _find_concept(
|
|
37
|
+
def _find_concept(
|
|
38
|
+
cs: Sequence[JsonConceptScheme], urn: str
|
|
39
|
+
) -> Optional[JsonConcept]:
|
|
38
40
|
r = parse_item_urn(urn)
|
|
39
41
|
f = [
|
|
40
42
|
m
|
|
41
43
|
for m in cs
|
|
42
44
|
if (m.agency == r.agency and m.id == r.id and m.version == r.version)
|
|
43
45
|
]
|
|
44
|
-
|
|
46
|
+
m = [c for c in f[0].concepts if c.id == r.item_id]
|
|
47
|
+
return m[0] if m else None
|
|
45
48
|
|
|
46
49
|
|
|
47
50
|
def _get_type(repr_: JsonRepresentation) -> Optional[str]:
|
|
@@ -168,11 +171,8 @@ class JsonDimension(Struct, frozen=True, omit_defaults=True):
|
|
|
168
171
|
cons: Dict[str, Sequence[str]],
|
|
169
172
|
) -> Component:
|
|
170
173
|
"""Returns a component."""
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
if cs
|
|
174
|
-
else parse_item_urn(self.conceptIdentity)
|
|
175
|
-
)
|
|
174
|
+
m = _find_concept(cs, self.conceptIdentity) if cs else None
|
|
175
|
+
c = m.to_model(cls) if m else parse_item_urn(self.conceptIdentity)
|
|
176
176
|
name = c.name if isinstance(c, Concept) else None
|
|
177
177
|
desc = c.description if isinstance(c, Concept) else None
|
|
178
178
|
dt, facets, codes, ab = _get_representation(
|
|
@@ -227,11 +227,8 @@ class JsonAttribute(Struct, frozen=True, omit_defaults=True):
|
|
|
227
227
|
groups: Sequence[JsonGroup],
|
|
228
228
|
) -> Component:
|
|
229
229
|
"""Returns a component."""
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
if cs
|
|
233
|
-
else parse_item_urn(self.conceptIdentity)
|
|
234
|
-
)
|
|
230
|
+
m = _find_concept(cs, self.conceptIdentity) if cs else None
|
|
231
|
+
c = m.to_model(cls) if m else parse_item_urn(self.conceptIdentity)
|
|
235
232
|
name = c.name if isinstance(c, Concept) else None
|
|
236
233
|
desc = c.description if isinstance(c, Concept) else None
|
|
237
234
|
dt, facets, codes, ab = _get_representation(
|
|
@@ -312,11 +309,8 @@ class JsonMeasure(Struct, frozen=True, omit_defaults=True):
|
|
|
312
309
|
cons: Dict[str, Sequence[str]],
|
|
313
310
|
) -> Component:
|
|
314
311
|
"""Returns a component."""
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
if cs
|
|
318
|
-
else parse_item_urn(self.conceptIdentity)
|
|
319
|
-
)
|
|
312
|
+
m = _find_concept(cs, self.conceptIdentity) if cs else None
|
|
313
|
+
c = m.to_model(cls) if m else parse_item_urn(self.conceptIdentity)
|
|
320
314
|
name = c.name if isinstance(c, Concept) else None
|
|
321
315
|
desc = c.description if isinstance(c, Concept) else None
|
|
322
316
|
dt, facets, codes, ab = _get_representation(
|
|
@@ -49,11 +49,8 @@ class JsonMetadataAttribute(Struct, frozen=True, omit_defaults=True):
|
|
|
49
49
|
self, cs: Sequence[JsonConceptScheme], cls: Sequence[Codelist]
|
|
50
50
|
) -> MetadataComponent:
|
|
51
51
|
"""Returns a metadata component."""
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
if cs
|
|
55
|
-
else parse_item_urn(self.conceptIdentity)
|
|
56
|
-
)
|
|
52
|
+
m = _find_concept(cs, self.conceptIdentity) if cs else None
|
|
53
|
+
c = m.to_model(cls) if m else parse_item_urn(self.conceptIdentity)
|
|
57
54
|
dt, facets, codes, _ = _get_representation(
|
|
58
55
|
self.id, self.localRepresentation, cls, {}
|
|
59
56
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Collection of SDMX-JSON schemas for reference metadata queries."""
|
|
2
2
|
|
|
3
|
-
from typing import Any, Optional, Sequence
|
|
3
|
+
from typing import Any, Literal, Optional, Sequence
|
|
4
4
|
|
|
5
5
|
from msgspec import Struct
|
|
6
6
|
|
|
@@ -162,7 +162,11 @@ class JsonMetadataMessage(Struct, frozen=True, omit_defaults=True):
|
|
|
162
162
|
return MetadataMessage(header, reports)
|
|
163
163
|
|
|
164
164
|
@classmethod
|
|
165
|
-
def from_model(
|
|
165
|
+
def from_model(
|
|
166
|
+
self,
|
|
167
|
+
msg: MetadataMessage,
|
|
168
|
+
msg_version: Literal["2.0.0", "2.1"] = "2.0.0",
|
|
169
|
+
) -> "JsonMetadataMessage":
|
|
166
170
|
"""Converts a pysdmx metadata message to an SDMX-JSON one."""
|
|
167
171
|
if not msg.header:
|
|
168
172
|
raise errors.Invalid(
|
|
@@ -175,6 +179,6 @@ class JsonMetadataMessage(Struct, frozen=True, omit_defaults=True):
|
|
|
175
179
|
"SDMX-JSON metadata messages must have metadata reports.",
|
|
176
180
|
)
|
|
177
181
|
|
|
178
|
-
header = JsonHeader.from_model(msg.header, "metadata")
|
|
182
|
+
header = JsonHeader.from_model(msg.header, "metadata", msg_version)
|
|
179
183
|
reports = [JsonMetadataReport.from_model(r) for r in msg.reports]
|
|
180
184
|
return JsonMetadataMessage(header, JsonMetadataSets(reports))
|
|
@@ -1,12 +1,15 @@
|
|
|
1
1
|
"""Collection of SDMX-JSON schemas for SDMX-REST schema queries."""
|
|
2
2
|
|
|
3
|
-
from typing import Literal, Optional, Sequence, Tuple
|
|
3
|
+
from typing import Dict, Literal, Optional, Sequence, Tuple
|
|
4
4
|
|
|
5
5
|
import msgspec
|
|
6
6
|
|
|
7
7
|
from pysdmx.io.json.sdmxjson2.messages.code import JsonCodelist, JsonValuelist
|
|
8
8
|
from pysdmx.io.json.sdmxjson2.messages.concept import JsonConceptScheme
|
|
9
|
-
from pysdmx.io.json.sdmxjson2.messages.constraint import
|
|
9
|
+
from pysdmx.io.json.sdmxjson2.messages.constraint import (
|
|
10
|
+
JsonDataConstraint,
|
|
11
|
+
JsonKeySet,
|
|
12
|
+
)
|
|
10
13
|
from pysdmx.io.json.sdmxjson2.messages.core import JsonHeader
|
|
11
14
|
from pysdmx.io.json.sdmxjson2.messages.dsd import JsonDataStructure
|
|
12
15
|
from pysdmx.model import Components, HierarchyAssociation, Schema
|
|
@@ -25,7 +28,7 @@ class JsonSchemas(msgspec.Struct, frozen=True, omit_defaults=True):
|
|
|
25
28
|
|
|
26
29
|
def to_model(
|
|
27
30
|
self,
|
|
28
|
-
) -> Tuple[Components, Optional[Sequence[Group]]]:
|
|
31
|
+
) -> Tuple[Components, Optional[Sequence[Group]], Optional[Sequence[str]]]:
|
|
29
32
|
"""Returns the requested schema."""
|
|
30
33
|
comps = self.dataStructures[0].dataStructureComponents
|
|
31
34
|
comps, grps = comps.to_model( # type: ignore[union-attr,assignment]
|
|
@@ -34,7 +37,36 @@ class JsonSchemas(msgspec.Struct, frozen=True, omit_defaults=True):
|
|
|
34
37
|
self.valuelists,
|
|
35
38
|
self.dataConstraints,
|
|
36
39
|
)
|
|
37
|
-
|
|
40
|
+
keys = self.__process_keys()
|
|
41
|
+
return comps, grps, keys # type: ignore[return-value]
|
|
42
|
+
|
|
43
|
+
def __extract_keys_dict(
|
|
44
|
+
self, keysets: Sequence[JsonKeySet]
|
|
45
|
+
) -> Sequence[Dict[str, str]]:
|
|
46
|
+
keys = []
|
|
47
|
+
for ks in keysets:
|
|
48
|
+
keys.extend(
|
|
49
|
+
[{kv.id: kv.value for kv in k.keyValues} for k in ks.keys]
|
|
50
|
+
)
|
|
51
|
+
return keys
|
|
52
|
+
|
|
53
|
+
def __infer_keys(self, keys_dict: Dict[str, str]) -> str:
|
|
54
|
+
dimensions = [
|
|
55
|
+
d.id
|
|
56
|
+
for d in self.dataStructures[ # type: ignore[union-attr]
|
|
57
|
+
0
|
|
58
|
+
].dataStructureComponents.dimensionList.dimensions
|
|
59
|
+
]
|
|
60
|
+
dim_values = [keys_dict.get(d, "*") for d in dimensions]
|
|
61
|
+
return ".".join(dim_values)
|
|
62
|
+
|
|
63
|
+
def __process_keys(self) -> Optional[Sequence[str]]:
|
|
64
|
+
keys = []
|
|
65
|
+
for c in self.dataConstraints:
|
|
66
|
+
if c.dataKeySets:
|
|
67
|
+
keys_dicts = self.__extract_keys_dict(c.dataKeySets)
|
|
68
|
+
keys.extend([self.__infer_keys(d) for d in keys_dicts])
|
|
69
|
+
return list(set(keys))
|
|
38
70
|
|
|
39
71
|
|
|
40
72
|
class JsonSchemaMessage(msgspec.Struct, frozen=True, omit_defaults=True):
|
|
@@ -52,7 +84,7 @@ class JsonSchemaMessage(msgspec.Struct, frozen=True, omit_defaults=True):
|
|
|
52
84
|
hierarchies: Sequence[HierarchyAssociation],
|
|
53
85
|
) -> Schema:
|
|
54
86
|
"""Returns the requested schema."""
|
|
55
|
-
components, groups = self.data.to_model()
|
|
87
|
+
components, groups, keys = self.data.to_model()
|
|
56
88
|
comp_dict = {c.id: c for c in components}
|
|
57
89
|
urns = [a.urn for a in self.meta.links]
|
|
58
90
|
for ha in hierarchies:
|
|
@@ -77,4 +109,5 @@ class JsonSchemaMessage(msgspec.Struct, frozen=True, omit_defaults=True):
|
|
|
77
109
|
version,
|
|
78
110
|
urns, # type: ignore[arg-type]
|
|
79
111
|
groups=groups,
|
|
112
|
+
keys=keys,
|
|
80
113
|
)
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Collection of SDMX-JSON schemas for generic structure messages."""
|
|
2
2
|
|
|
3
|
-
from typing import Sequence
|
|
3
|
+
from typing import Literal, Sequence
|
|
4
4
|
|
|
5
5
|
from msgspec import Struct
|
|
6
6
|
|
|
@@ -338,12 +338,16 @@ class JsonStructureMessage(Struct, frozen=True, omit_defaults=True):
|
|
|
338
338
|
return StructureMessage(header, structures)
|
|
339
339
|
|
|
340
340
|
@classmethod
|
|
341
|
-
def from_model(
|
|
341
|
+
def from_model(
|
|
342
|
+
cls,
|
|
343
|
+
message: StructureMessage,
|
|
344
|
+
msg_version: Literal["2.0.0", "2.1"] = "2.0.0",
|
|
345
|
+
) -> "JsonStructureMessage":
|
|
342
346
|
"""Creates an SDMX-JSON payload from a pysdmx StructureMessage."""
|
|
343
347
|
if not message.header:
|
|
344
348
|
raise errors.Invalid(
|
|
345
349
|
"Invalid input", "SDMX-JSON messages must have a header."
|
|
346
350
|
)
|
|
347
|
-
header = JsonHeader.from_model(message.header)
|
|
351
|
+
header = JsonHeader.from_model(message.header, msg_version=msg_version)
|
|
348
352
|
structs = JsonStructures.from_model(message)
|
|
349
353
|
return JsonStructureMessage(header, structs)
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""
|
|
1
|
+
"""Reader interface for SDMX-JSON 2.0.0 and 2.1.0 Reference Metadata."""
|
|
2
2
|
|
|
3
3
|
import msgspec
|
|
4
4
|
|
|
@@ -11,7 +11,7 @@ from pysdmx.model.message import MetadataMessage
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def read(input_str: str, validate: bool = True) -> MetadataMessage:
|
|
14
|
-
"""Read
|
|
14
|
+
"""Read SDMX-JSON 2.0.0 and 2.1.0 Metadata messages.
|
|
15
15
|
|
|
16
16
|
Args:
|
|
17
17
|
input_str: SDMX-JSON reference metadata message to read.
|
|
@@ -34,6 +34,6 @@ def read(input_str: str, validate: bool = True) -> MetadataMessage:
|
|
|
34
34
|
"Invalid message",
|
|
35
35
|
(
|
|
36
36
|
"The supplied file could not be read as SDMX-JSON 2.0.0 "
|
|
37
|
-
"reference metadata message."
|
|
37
|
+
"or 2.1.0 reference metadata message."
|
|
38
38
|
),
|
|
39
39
|
) from de
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
"""Reader interface for SDMX-JSON 2.0.0 Structure messages."""
|
|
1
|
+
"""Reader interface for SDMX-JSON 2.0.0 and 2.1.0 Structure messages."""
|
|
2
2
|
|
|
3
3
|
import msgspec
|
|
4
4
|
|
|
@@ -11,7 +11,7 @@ from pysdmx.model.message import StructureMessage
|
|
|
11
11
|
|
|
12
12
|
|
|
13
13
|
def read(input_str: str, validate: bool = True) -> StructureMessage:
|
|
14
|
-
"""Read
|
|
14
|
+
"""Read SDMX-JSON 2.0.0 and 2.1.0 Structure messages.
|
|
15
15
|
|
|
16
16
|
Args:
|
|
17
17
|
input_str: SDMX-JSON structure message to read.
|
|
@@ -34,6 +34,6 @@ def read(input_str: str, validate: bool = True) -> StructureMessage:
|
|
|
34
34
|
"Invalid message",
|
|
35
35
|
(
|
|
36
36
|
"The supplied file could not be read as SDMX-JSON 2.0.0 "
|
|
37
|
-
"structure message."
|
|
37
|
+
"or 2.1.0 structure message."
|
|
38
38
|
),
|
|
39
39
|
) from de
|