pysdmx 1.10.0rc1__tar.gz → 1.10.0rc2__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.0rc1 → pysdmx-1.10.0rc2}/PKG-INFO +1 -1
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/pyproject.toml +1 -1
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/__init__.py +1 -1
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/fmr/__init__.py +3 -2
- pysdmx-1.10.0rc2/src/pysdmx/io/_pd_utils.py +83 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/csv/__csv_aux_writer.py +23 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/csv/sdmx10/reader/__init__.py +1 -1
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/csv/sdmx10/writer/__init__.py +15 -9
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/csv/sdmx20/reader/__init__.py +1 -1
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/csv/sdmx20/writer/__init__.py +1 -1
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/csv/sdmx21/reader/__init__.py +1 -1
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/csv/sdmx21/writer/__init__.py +1 -1
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/messages/__init__.py +4 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/messages/code.py +16 -6
- pysdmx-1.10.0rc2/src/pysdmx/io/json/sdmxjson2/messages/constraint.py +272 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/messages/dsd.py +35 -7
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/messages/map.py +5 -4
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/messages/metadataflow.py +1 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/messages/msd.py +18 -10
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/messages/schema.py +2 -2
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/messages/structure.py +81 -44
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/messages/vtl.py +13 -9
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/__write_data_aux.py +20 -7
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/__write_structure_specific_aux.py +71 -54
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx21/writer/generic.py +31 -19
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/model/__base.py +46 -1
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/model/__init__.py +18 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/model/category.py +17 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/model/concept.py +16 -0
- pysdmx-1.10.0rc2/src/pysdmx/model/constraint.py +69 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/model/message.py +80 -71
- pysdmx-1.10.0rc1/src/pysdmx/io/json/sdmxjson2/messages/constraint.py +0 -53
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/LICENSE +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/README.rst +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/__extras_check.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/dc/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/dc/_api.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/dc/query/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/dc/query/_model.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/dc/query/_parsing_model.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/dc/query/_parsing_util.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/dc/query/_py_parser.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/dc/query/_sql_parser.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/dc/query/util.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/fmr/maintenance.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/gds/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/qb/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/qb/availability.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/qb/data.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/qb/gds.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/qb/refmeta.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/qb/registration.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/qb/schema.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/qb/service.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/qb/structure.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/api/qb/util.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/errors.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/csv/__csv_aux_reader.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/csv/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/csv/sdmx10/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/csv/sdmx20/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/csv/sdmx21/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/format.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/input_processor.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/fusion/messages/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/fusion/messages/category.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/fusion/messages/code.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/fusion/messages/concept.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/fusion/messages/constraint.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/fusion/messages/core.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/fusion/messages/dataflow.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/fusion/messages/dsd.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/fusion/messages/map.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/fusion/messages/metadataflow.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/fusion/messages/mpa.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/fusion/messages/msd.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/fusion/messages/org.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/fusion/messages/pa.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/fusion/messages/report.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/fusion/messages/schema.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/fusion/messages/vtl.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/fusion/reader/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/gds/messages/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/gds/messages/agencies.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/gds/messages/catalog.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/gds/messages/sdmx_api.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/gds/messages/services.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/gds/messages/urn_resolver.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/gds/reader/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/messages/agency.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/messages/category.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/messages/concept.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/messages/core.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/messages/dataflow.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/messages/mpa.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/messages/pa.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/messages/provider.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/messages/report.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/reader/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/reader/doc_validation.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/reader/metadata.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/reader/structure.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/writer/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/writer/metadata.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/json/sdmxjson2/writer/structure.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/pd.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/reader.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/serde.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/writer.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/__allowed_lxml_errors.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/__data_aux.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/__parse_xml.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/__ss_aux_reader.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/__structure_aux_reader.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/__structure_aux_writer.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/__tokens.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/__write_aux.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/config.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/doc_validation.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/header.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx21/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx21/reader/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx21/reader/error.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx21/reader/generic.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx21/reader/structure.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx21/reader/structure_specific.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx21/reader/submission.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx21/writer/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx21/writer/error.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx21/writer/structure.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx21/writer/structure_specific.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx30/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx30/reader/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx30/reader/structure.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx30/reader/structure_specific.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx30/writer/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx30/writer/structure.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx30/writer/structure_specific.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx31/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx31/reader/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx31/reader/structure.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx31/reader/structure_specific.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx31/writer/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx31/writer/structure.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/sdmx31/writer/structure_specific.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/io/xml/utils.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/model/code.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/model/dataflow.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/model/dataset.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/model/gds.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/model/map.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/model/metadata.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/model/organisation.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/model/submission.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/model/vtl.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/py.typed +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/toolkit/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/toolkit/pd/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/toolkit/pd/_data_utils.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/toolkit/vtl/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/toolkit/vtl/_validations.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/toolkit/vtl/convert.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/toolkit/vtl/script_generation.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/toolkit/vtl/validation.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/util/__init__.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/util/_date_pattern_map.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/util/_model_utils.py +0 -0
- {pysdmx-1.10.0rc1 → pysdmx-1.10.0rc2}/src/pysdmx/util/_net_utils.py +0 -0
|
@@ -44,6 +44,7 @@ from pysdmx.model import (
|
|
|
44
44
|
Metadataflow,
|
|
45
45
|
MetadataProvisionAgreement,
|
|
46
46
|
MetadataReport,
|
|
47
|
+
MetadataStructure,
|
|
47
48
|
MultiRepresentationMap,
|
|
48
49
|
ProvisionAgreement,
|
|
49
50
|
RepresentationMap,
|
|
@@ -767,7 +768,7 @@ class RegistryClient(__BaseRegistryClient):
|
|
|
767
768
|
agency: str = "*",
|
|
768
769
|
id: str = "*",
|
|
769
770
|
version: str = "+",
|
|
770
|
-
) -> Sequence[
|
|
771
|
+
) -> Sequence[MetadataStructure]:
|
|
771
772
|
"""Get the metadata structures (MSD) matching the supplied parameters.
|
|
772
773
|
|
|
773
774
|
Args:
|
|
@@ -1341,7 +1342,7 @@ class AsyncRegistryClient(__BaseRegistryClient):
|
|
|
1341
1342
|
agency: str = "*",
|
|
1342
1343
|
id: str = "*",
|
|
1343
1344
|
version: str = "+",
|
|
1344
|
-
) -> Sequence[
|
|
1345
|
+
) -> Sequence[MetadataStructure]:
|
|
1345
1346
|
"""Get the metadata structures (MSD) matching the supplied parameters.
|
|
1346
1347
|
|
|
1347
1348
|
Args:
|
|
@@ -0,0 +1,83 @@
|
|
|
1
|
+
import pandas as pd
|
|
2
|
+
|
|
3
|
+
from pysdmx.errors import Invalid
|
|
4
|
+
from pysdmx.model.concept import DataType
|
|
5
|
+
from pysdmx.model.dataflow import Schema
|
|
6
|
+
|
|
7
|
+
NUMERIC_TYPES = {
|
|
8
|
+
DataType.BIG_INTEGER,
|
|
9
|
+
DataType.COUNT,
|
|
10
|
+
DataType.DECIMAL,
|
|
11
|
+
DataType.DOUBLE,
|
|
12
|
+
DataType.FLOAT,
|
|
13
|
+
DataType.INCREMENTAL,
|
|
14
|
+
DataType.INTEGER,
|
|
15
|
+
DataType.LONG,
|
|
16
|
+
DataType.SHORT,
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
def _fill_na_values(data: pd.DataFrame, structure: Schema) -> pd.DataFrame:
|
|
21
|
+
"""Fills missing values in the DataFrame based on the component type.
|
|
22
|
+
|
|
23
|
+
Numeric components are filled with "NaN".
|
|
24
|
+
Other components are filled with "#N/A".
|
|
25
|
+
If the structure does not have components,
|
|
26
|
+
all missing values are filled with "".
|
|
27
|
+
|
|
28
|
+
Args:
|
|
29
|
+
data: The DataFrame to fill.
|
|
30
|
+
structure: The structure definition (´Schema´).
|
|
31
|
+
|
|
32
|
+
Returns:
|
|
33
|
+
The DataFrame with filled missing values.
|
|
34
|
+
|
|
35
|
+
Raises:
|
|
36
|
+
Invalid: If the structure does not have components.
|
|
37
|
+
"""
|
|
38
|
+
for component in structure.components:
|
|
39
|
+
if component.id in data.columns:
|
|
40
|
+
if component.dtype in NUMERIC_TYPES:
|
|
41
|
+
data[component.id] = (
|
|
42
|
+
data[component.id].astype(object).fillna("NaN")
|
|
43
|
+
)
|
|
44
|
+
else:
|
|
45
|
+
data[component.id] = (
|
|
46
|
+
data[component.id].astype(object).fillna("#N/A")
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
return data
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
def _validate_explicit_null_values(
|
|
53
|
+
data: pd.DataFrame, structure: Schema
|
|
54
|
+
) -> None:
|
|
55
|
+
"""Validates that explicit null values are correct for the component type.
|
|
56
|
+
|
|
57
|
+
Numeric components must not contain "#N/A".
|
|
58
|
+
Non-numeric components must not contain "NaN".
|
|
59
|
+
|
|
60
|
+
Args:
|
|
61
|
+
data: The DataFrame to validate.
|
|
62
|
+
structure: The structure definition (´Schema´).
|
|
63
|
+
|
|
64
|
+
Raises:
|
|
65
|
+
Invalid: If invalid null values are found.
|
|
66
|
+
"""
|
|
67
|
+
for component in structure.components:
|
|
68
|
+
if component.id in data.columns:
|
|
69
|
+
series = data[component.id].astype(str)
|
|
70
|
+
if component.dtype in NUMERIC_TYPES:
|
|
71
|
+
# Numeric: #N/A is invalid
|
|
72
|
+
if series.isin(["#N/A"]).any():
|
|
73
|
+
raise Invalid(
|
|
74
|
+
f"Invalid null value '#N/A' in numeric component "
|
|
75
|
+
f"'{component.id}'."
|
|
76
|
+
)
|
|
77
|
+
else:
|
|
78
|
+
# Non-numeric: NaN is invalid
|
|
79
|
+
if series.isin(["NaN"]).any():
|
|
80
|
+
raise Invalid(
|
|
81
|
+
f"Invalid null value 'NaN' in non-numeric component "
|
|
82
|
+
f"'{component.id}'."
|
|
83
|
+
)
|
|
@@ -3,6 +3,8 @@ from typing import List, Literal, Optional, Sequence
|
|
|
3
3
|
|
|
4
4
|
import pandas as pd
|
|
5
5
|
|
|
6
|
+
from pysdmx.errors import Invalid
|
|
7
|
+
from pysdmx.io._pd_utils import _fill_na_values
|
|
6
8
|
from pysdmx.io.pd import PandasDataset
|
|
7
9
|
from pysdmx.model import Schema
|
|
8
10
|
from pysdmx.model.dataset import ActionType
|
|
@@ -16,6 +18,25 @@ SDMX_CSV_ACTION_MAPPER = {
|
|
|
16
18
|
}
|
|
17
19
|
|
|
18
20
|
|
|
21
|
+
def _validate_schema_exists(dataset: PandasDataset) -> Schema:
|
|
22
|
+
"""Validates that the dataset has a Schema defined.
|
|
23
|
+
|
|
24
|
+
Args:
|
|
25
|
+
dataset: The dataset to validate.
|
|
26
|
+
|
|
27
|
+
Returns:
|
|
28
|
+
The `Schema` from the dataset.
|
|
29
|
+
|
|
30
|
+
Raises:
|
|
31
|
+
Invalid: If the structure is not a `Schema`.
|
|
32
|
+
"""
|
|
33
|
+
if not isinstance(dataset.structure, Schema):
|
|
34
|
+
raise Invalid(
|
|
35
|
+
"Dataset Structure is not a Schema. Cannot perform operation."
|
|
36
|
+
)
|
|
37
|
+
return dataset.structure
|
|
38
|
+
|
|
39
|
+
|
|
19
40
|
def __write_time_period(df: pd.DataFrame, time_format: str) -> None:
|
|
20
41
|
# TODO: Correct handle of normalized time format
|
|
21
42
|
raise NotImplementedError("Normalized time format is not implemented yet.")
|
|
@@ -70,8 +91,10 @@ def _write_csv_2_aux(
|
|
|
70
91
|
) -> List[pd.DataFrame]:
|
|
71
92
|
dataframes = []
|
|
72
93
|
for dataset in datasets:
|
|
94
|
+
schema = _validate_schema_exists(dataset)
|
|
73
95
|
# Create a copy of the dataset
|
|
74
96
|
df: pd.DataFrame = copy(dataset.data)
|
|
97
|
+
df = _fill_na_values(df, schema)
|
|
75
98
|
structure_ref, unique_id = dataset.short_urn.split("=", maxsplit=1)
|
|
76
99
|
|
|
77
100
|
# Add additional attributes to the dataset
|
|
@@ -24,7 +24,7 @@ def read(input_str: str) -> Sequence[PandasDataset]:
|
|
|
24
24
|
"""
|
|
25
25
|
# Get Dataframe from CSV file
|
|
26
26
|
df_csv = pd.read_csv(
|
|
27
|
-
StringIO(input_str), keep_default_na=False, na_values=[
|
|
27
|
+
StringIO(input_str), keep_default_na=False, na_values=[]
|
|
28
28
|
)
|
|
29
29
|
# Drop empty columns
|
|
30
30
|
df_csv = df_csv.dropna(axis=1, how="all")
|
|
@@ -6,9 +6,12 @@ from typing import Literal, Optional, Sequence, Union
|
|
|
6
6
|
|
|
7
7
|
import pandas as pd
|
|
8
8
|
|
|
9
|
-
from pysdmx.io.
|
|
9
|
+
from pysdmx.io._pd_utils import _fill_na_values
|
|
10
|
+
from pysdmx.io.csv.__csv_aux_writer import (
|
|
11
|
+
__write_time_period,
|
|
12
|
+
_validate_schema_exists,
|
|
13
|
+
)
|
|
10
14
|
from pysdmx.io.pd import PandasDataset
|
|
11
|
-
from pysdmx.model import Schema
|
|
12
15
|
from pysdmx.toolkit.pd._data_utils import format_labels
|
|
13
16
|
|
|
14
17
|
|
|
@@ -44,22 +47,26 @@ def write(
|
|
|
44
47
|
# Create a copy of the dataset
|
|
45
48
|
dataframes = []
|
|
46
49
|
for dataset in datasets:
|
|
50
|
+
# Validate that dataset has a proper Schema
|
|
51
|
+
schema = _validate_schema_exists(dataset)
|
|
52
|
+
|
|
47
53
|
df: pd.DataFrame = copy(dataset.data)
|
|
48
54
|
|
|
55
|
+
# Fill missing values
|
|
56
|
+
df = _fill_na_values(df, schema)
|
|
57
|
+
|
|
49
58
|
# Add additional attributes to the dataset
|
|
50
59
|
for k, v in dataset.attributes.items():
|
|
51
60
|
df[k] = v
|
|
52
61
|
structure_id = dataset.short_urn.split("=")[1]
|
|
53
62
|
if time_format is not None and time_format != "original":
|
|
54
63
|
__write_time_period(df, time_format)
|
|
55
|
-
if labels is not None
|
|
56
|
-
format_labels(df, labels,
|
|
64
|
+
if labels is not None:
|
|
65
|
+
format_labels(df, labels, schema.components)
|
|
57
66
|
if labels == "id":
|
|
58
67
|
df.insert(0, "DATAFLOW", structure_id)
|
|
59
68
|
else:
|
|
60
|
-
df.insert(
|
|
61
|
-
0, "DATAFLOW", f"{structure_id}:{dataset.structure.name}"
|
|
62
|
-
)
|
|
69
|
+
df.insert(0, "DATAFLOW", f"{structure_id}:{schema.name}")
|
|
63
70
|
else:
|
|
64
71
|
df.insert(0, "DATAFLOW", structure_id)
|
|
65
72
|
|
|
@@ -68,8 +75,7 @@ def write(
|
|
|
68
75
|
# Concatenate the dataframes
|
|
69
76
|
all_data = pd.concat(dataframes, ignore_index=True, axis=0)
|
|
70
77
|
|
|
71
|
-
|
|
72
|
-
all_data = all_data.astype(str).replace({"nan": "", "<NA>": ""})
|
|
78
|
+
all_data = all_data.astype(str)
|
|
73
79
|
# If the output path is an empty string we use None
|
|
74
80
|
output_path = (
|
|
75
81
|
None
|
|
@@ -24,7 +24,7 @@ def read(input_str: str) -> Sequence[PandasDataset]:
|
|
|
24
24
|
"""
|
|
25
25
|
# Get Dataframe from CSV file
|
|
26
26
|
df_csv = pd.read_csv(
|
|
27
|
-
StringIO(input_str), keep_default_na=False, na_values=[
|
|
27
|
+
StringIO(input_str), keep_default_na=False, na_values=[]
|
|
28
28
|
)
|
|
29
29
|
# Drop empty columns
|
|
30
30
|
df_csv = df_csv.dropna(axis=1, how="all")
|
|
@@ -24,7 +24,7 @@ def read(input_str: str) -> Sequence[PandasDataset]:
|
|
|
24
24
|
"""
|
|
25
25
|
# Get Dataframe from CSV file
|
|
26
26
|
df_csv = pd.read_csv(
|
|
27
|
-
StringIO(input_str), keep_default_na=False, na_values=[
|
|
27
|
+
StringIO(input_str), keep_default_na=False, na_values=[]
|
|
28
28
|
)
|
|
29
29
|
# Drop empty columns
|
|
30
30
|
df_csv = df_csv.dropna(axis=1, how="all")
|
|
@@ -12,6 +12,9 @@ from pysdmx.io.json.sdmxjson2.messages.code import (
|
|
|
12
12
|
JsonHierarchyMessage,
|
|
13
13
|
)
|
|
14
14
|
from pysdmx.io.json.sdmxjson2.messages.concept import JsonConceptSchemeMessage
|
|
15
|
+
from pysdmx.io.json.sdmxjson2.messages.constraint import (
|
|
16
|
+
JsonDataConstraintMessage,
|
|
17
|
+
)
|
|
15
18
|
from pysdmx.io.json.sdmxjson2.messages.dataflow import (
|
|
16
19
|
JsonDataflowMessage,
|
|
17
20
|
JsonDataflowsMessage,
|
|
@@ -50,6 +53,7 @@ __all__ = [
|
|
|
50
53
|
"JsonCategorySchemeMessage",
|
|
51
54
|
"JsonCodelistMessage",
|
|
52
55
|
"JsonConceptSchemeMessage",
|
|
56
|
+
"JsonDataConstraintMessage",
|
|
53
57
|
"JsonDataflowMessage",
|
|
54
58
|
"JsonDataflowsMessage",
|
|
55
59
|
"JsonDataStructuresMessage",
|
|
@@ -231,7 +231,7 @@ class JsonCodelists(Struct, frozen=True, omit_defaults=True):
|
|
|
231
231
|
"""SDMX-JSON payload for lists of codes."""
|
|
232
232
|
|
|
233
233
|
codelists: Sequence[JsonCodelist] = ()
|
|
234
|
-
|
|
234
|
+
valueLists: Sequence[JsonValuelist] = ()
|
|
235
235
|
|
|
236
236
|
|
|
237
237
|
class JsonCodelistMessage(Struct, frozen=True, omit_defaults=True):
|
|
@@ -244,7 +244,7 @@ class JsonCodelistMessage(Struct, frozen=True, omit_defaults=True):
|
|
|
244
244
|
if self.data.codelists:
|
|
245
245
|
return self.data.codelists[0].to_model()
|
|
246
246
|
else:
|
|
247
|
-
return self.data.
|
|
247
|
+
return self.data.valueLists[0].to_model()
|
|
248
248
|
|
|
249
249
|
|
|
250
250
|
class JsonHierarchicalCode(Struct, frozen=True, omit_defaults=True):
|
|
@@ -329,10 +329,10 @@ class JsonHierarchicalCode(Struct, frozen=True, omit_defaults=True):
|
|
|
329
329
|
code=code.urn,
|
|
330
330
|
validFrom=code.rel_valid_from,
|
|
331
331
|
validTo=code.rel_valid_to,
|
|
332
|
-
annotations=tuple(annotations),
|
|
333
|
-
hierarchicalCodes=
|
|
334
|
-
JsonHierarchicalCode.from_model(c) for c in code.codes
|
|
335
|
-
|
|
332
|
+
annotations=tuple(annotations) if annotations else None,
|
|
333
|
+
hierarchicalCodes=tuple(
|
|
334
|
+
[JsonHierarchicalCode.from_model(c) for c in code.codes]
|
|
335
|
+
),
|
|
336
336
|
)
|
|
337
337
|
|
|
338
338
|
|
|
@@ -475,6 +475,15 @@ class JsonHierarchyAssociation(
|
|
|
475
475
|
"SDMX-JSON hierarchy associations must reference a context",
|
|
476
476
|
{"hierarchy_association": ha.id},
|
|
477
477
|
)
|
|
478
|
+
lnk = (
|
|
479
|
+
JsonLink(
|
|
480
|
+
rel="UserDefinedOperator",
|
|
481
|
+
type="sdmx_artefact",
|
|
482
|
+
urn=ha.operator,
|
|
483
|
+
)
|
|
484
|
+
if ha.operator
|
|
485
|
+
else None
|
|
486
|
+
)
|
|
478
487
|
return JsonHierarchyAssociation(
|
|
479
488
|
agency=(
|
|
480
489
|
ha.agency.id if isinstance(ha.agency, Agency) else ha.agency
|
|
@@ -492,6 +501,7 @@ class JsonHierarchyAssociation(
|
|
|
492
501
|
linkedHierarchy=href,
|
|
493
502
|
linkedObject=ha.component_ref,
|
|
494
503
|
contextObject=ha.context_ref,
|
|
504
|
+
links=[lnk] if lnk else (),
|
|
495
505
|
)
|
|
496
506
|
|
|
497
507
|
|
|
@@ -0,0 +1,272 @@
|
|
|
1
|
+
"""Collection of SDMX-JSON schemas for content constraints."""
|
|
2
|
+
|
|
3
|
+
from datetime import datetime
|
|
4
|
+
from typing import Optional, Sequence
|
|
5
|
+
|
|
6
|
+
from msgspec import Struct
|
|
7
|
+
|
|
8
|
+
from pysdmx import errors
|
|
9
|
+
from pysdmx.io.json.sdmxjson2.messages.core import (
|
|
10
|
+
JsonAnnotation,
|
|
11
|
+
MaintainableType,
|
|
12
|
+
)
|
|
13
|
+
from pysdmx.model import (
|
|
14
|
+
Agency,
|
|
15
|
+
ConstraintAttachment,
|
|
16
|
+
CubeKeyValue,
|
|
17
|
+
CubeRegion,
|
|
18
|
+
CubeValue,
|
|
19
|
+
DataConstraint,
|
|
20
|
+
DataKey,
|
|
21
|
+
DataKeyValue,
|
|
22
|
+
KeySet,
|
|
23
|
+
)
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
class JsonValue(Struct, frozen=True, omit_defaults=True):
|
|
27
|
+
"""SDMX-JSON payload for a cube value."""
|
|
28
|
+
|
|
29
|
+
value: str
|
|
30
|
+
validFrom: Optional[datetime] = None
|
|
31
|
+
validTo: Optional[datetime] = None
|
|
32
|
+
|
|
33
|
+
def to_model(self) -> CubeValue:
|
|
34
|
+
"""Converts a JsonValue to a CubeValue."""
|
|
35
|
+
return CubeValue(self.value, self.validFrom, self.validTo)
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def from_model(self, cv: CubeValue) -> "JsonValue":
|
|
39
|
+
"""Converts a pysdmx cube value to an SDMX-JSON one."""
|
|
40
|
+
return JsonValue(cv.value, cv.valid_from, cv.valid_to)
|
|
41
|
+
|
|
42
|
+
|
|
43
|
+
class JsonKeyValue(Struct, frozen=True, omit_defaults=True):
|
|
44
|
+
"""SDMX-JSON payload for the list of allowed values per component."""
|
|
45
|
+
|
|
46
|
+
id: str
|
|
47
|
+
values: Sequence[JsonValue]
|
|
48
|
+
# Additional properties are supported in the model (include,
|
|
49
|
+
# removePrefix, validFrom, validTo, timeRange) but not by the FMR.
|
|
50
|
+
# Therefore, they are ignored for now.
|
|
51
|
+
|
|
52
|
+
def to_model(self) -> CubeKeyValue:
|
|
53
|
+
"""Converts a JsonKeyValue to a CubeKeyValue."""
|
|
54
|
+
return CubeKeyValue(self.id, [v.to_model() for v in self.values])
|
|
55
|
+
|
|
56
|
+
@classmethod
|
|
57
|
+
def from_model(self, key_value: CubeKeyValue) -> "JsonKeyValue":
|
|
58
|
+
"""Converts a pysdmx cube key value to an SDMX-JSON one."""
|
|
59
|
+
return JsonKeyValue(
|
|
60
|
+
key_value.id, [JsonValue.from_model(v) for v in key_value.values]
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
class JsonCubeRegion(Struct, frozen=True, omit_defaults=True):
|
|
65
|
+
"""SDMX-JSON payload for a cube region."""
|
|
66
|
+
|
|
67
|
+
# The property `components` is ignored as it's not used in the FMR`
|
|
68
|
+
keyValues: Sequence[JsonKeyValue]
|
|
69
|
+
include: bool = True
|
|
70
|
+
|
|
71
|
+
def to_model(self) -> CubeRegion:
|
|
72
|
+
"""Converts a JsonCubeRegion to a CubeRegion."""
|
|
73
|
+
return CubeRegion(
|
|
74
|
+
[kv.to_model() for kv in self.keyValues], self.include
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
@classmethod
|
|
78
|
+
def from_model(self, region: CubeRegion) -> "JsonCubeRegion":
|
|
79
|
+
"""Converts a pysdmx cube region to an SDMX-JSON one."""
|
|
80
|
+
return JsonCubeRegion(
|
|
81
|
+
[JsonKeyValue.from_model(kv) for kv in region.key_values],
|
|
82
|
+
region.is_included,
|
|
83
|
+
)
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
class JsonConstraintAttachment(Struct, frozen=True, omit_defaults=True):
|
|
87
|
+
"""SDMX-JSON payload for a constraint attachment."""
|
|
88
|
+
|
|
89
|
+
dataProvider: Optional[str] = None
|
|
90
|
+
dataStructures: Sequence[str] = ()
|
|
91
|
+
dataflows: Sequence[str] = ()
|
|
92
|
+
provisionAgreements: Sequence[str] = ()
|
|
93
|
+
|
|
94
|
+
def to_model(self) -> ConstraintAttachment:
|
|
95
|
+
"""Converts a JsonConstraintAttachment to a ConstraintAttachment."""
|
|
96
|
+
return ConstraintAttachment(
|
|
97
|
+
self.dataProvider,
|
|
98
|
+
self.dataStructures,
|
|
99
|
+
self.dataflows,
|
|
100
|
+
self.provisionAgreements,
|
|
101
|
+
)
|
|
102
|
+
|
|
103
|
+
@classmethod
|
|
104
|
+
def from_model(
|
|
105
|
+
self, attachment: ConstraintAttachment
|
|
106
|
+
) -> "JsonConstraintAttachment":
|
|
107
|
+
"""Converts a pysdmx constraint attachment to an SDMX-JSON one."""
|
|
108
|
+
ds = attachment.data_structures if attachment.data_structures else ()
|
|
109
|
+
df = attachment.dataflows if attachment.dataflows else ()
|
|
110
|
+
pa = (
|
|
111
|
+
attachment.provision_agreements
|
|
112
|
+
if attachment.provision_agreements
|
|
113
|
+
else ()
|
|
114
|
+
)
|
|
115
|
+
return JsonConstraintAttachment(attachment.data_provider, ds, df, pa)
|
|
116
|
+
|
|
117
|
+
|
|
118
|
+
class JsonDataKeyValue(Struct, frozen=True, omit_defaults=True):
|
|
119
|
+
"""SDMX-JSON payload for a data key value."""
|
|
120
|
+
|
|
121
|
+
id: str
|
|
122
|
+
value: str
|
|
123
|
+
|
|
124
|
+
def to_model(self) -> DataKeyValue:
|
|
125
|
+
"""Converts a JsonDataKeyValue to a DataKeyValue."""
|
|
126
|
+
return DataKeyValue(self.id, self.value)
|
|
127
|
+
|
|
128
|
+
@classmethod
|
|
129
|
+
def from_model(self, kv: DataKeyValue) -> "JsonDataKeyValue":
|
|
130
|
+
"""Converts a pysdmx key value to an SDMX-JSON one."""
|
|
131
|
+
return JsonDataKeyValue(kv.id, kv.value)
|
|
132
|
+
|
|
133
|
+
|
|
134
|
+
class JsonDataKey(Struct, frozen=True, omit_defaults=True):
|
|
135
|
+
"""SDMX-JSON payload for a data key."""
|
|
136
|
+
|
|
137
|
+
keyValues: Sequence[JsonDataKeyValue]
|
|
138
|
+
validFrom: Optional[datetime] = None
|
|
139
|
+
validTo: Optional[datetime] = None
|
|
140
|
+
|
|
141
|
+
def to_model(self) -> DataKey:
|
|
142
|
+
"""Converts a JsonDataKey to a DataKey."""
|
|
143
|
+
return DataKey(
|
|
144
|
+
[kv.to_model() for kv in self.keyValues],
|
|
145
|
+
self.validFrom,
|
|
146
|
+
self.validTo,
|
|
147
|
+
)
|
|
148
|
+
|
|
149
|
+
@classmethod
|
|
150
|
+
def from_model(self, kv: DataKey) -> "JsonDataKey":
|
|
151
|
+
"""Converts a pysdmx key constraint to an SDMX-JSON one."""
|
|
152
|
+
return JsonDataKey(
|
|
153
|
+
[JsonDataKeyValue.from_model(val) for val in kv.keys_values],
|
|
154
|
+
kv.valid_from,
|
|
155
|
+
kv.valid_to,
|
|
156
|
+
)
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
class JsonKeySet(Struct, frozen=True, omit_defaults=True):
|
|
160
|
+
"""SDMX-JSON payload for a keyset."""
|
|
161
|
+
|
|
162
|
+
keys: Sequence[JsonDataKey]
|
|
163
|
+
isIncluded: bool
|
|
164
|
+
|
|
165
|
+
def to_model(self) -> KeySet:
|
|
166
|
+
"""Converts a JsonKeySet to a KeySet."""
|
|
167
|
+
return KeySet([k.to_model() for k in self.keys], self.isIncluded)
|
|
168
|
+
|
|
169
|
+
@classmethod
|
|
170
|
+
def from_model(self, ks: KeySet) -> "JsonKeySet":
|
|
171
|
+
"""Converts a pysdmx key set constraint to an SDMX-JSON one."""
|
|
172
|
+
return JsonKeySet(
|
|
173
|
+
[JsonDataKey.from_model(k) for k in ks.keys], ks.is_included
|
|
174
|
+
)
|
|
175
|
+
|
|
176
|
+
|
|
177
|
+
class JsonDataConstraint(MaintainableType, frozen=True, omit_defaults=True):
|
|
178
|
+
"""SDMX-JSON payload for a content constraint."""
|
|
179
|
+
|
|
180
|
+
constraintAttachment: Optional[JsonConstraintAttachment] = None
|
|
181
|
+
cubeRegions: Optional[Sequence[JsonCubeRegion]] = None
|
|
182
|
+
dataKeySets: Optional[Sequence[JsonKeySet]] = None
|
|
183
|
+
|
|
184
|
+
def to_model(self) -> DataConstraint:
|
|
185
|
+
"""Converts a JsonDataConstraint to a pysdmx Data Constraint."""
|
|
186
|
+
at = self.constraintAttachment.to_model() # type: ignore[union-attr]
|
|
187
|
+
return DataConstraint(
|
|
188
|
+
id=self.id,
|
|
189
|
+
name=self.name,
|
|
190
|
+
agency=self.agency,
|
|
191
|
+
description=self.description,
|
|
192
|
+
version=self.version,
|
|
193
|
+
annotations=tuple([a.to_model() for a in self.annotations]),
|
|
194
|
+
is_external_reference=self.isExternalReference,
|
|
195
|
+
valid_from=self.validFrom,
|
|
196
|
+
valid_to=self.validTo,
|
|
197
|
+
constraint_attachment=at,
|
|
198
|
+
cube_regions=[r.to_model() for r in self.cubeRegions]
|
|
199
|
+
if self.cubeRegions
|
|
200
|
+
else (),
|
|
201
|
+
key_sets=[s.to_model() for s in self.dataKeySets]
|
|
202
|
+
if self.dataKeySets
|
|
203
|
+
else (),
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
@classmethod
|
|
207
|
+
def from_model(self, cons: DataConstraint) -> "JsonDataConstraint":
|
|
208
|
+
"""Converts a pysdmx constraint to an SDMX-JSON one."""
|
|
209
|
+
crs = (
|
|
210
|
+
[JsonCubeRegion.from_model(r) for r in cons.cube_regions]
|
|
211
|
+
if cons.cube_regions
|
|
212
|
+
else None
|
|
213
|
+
)
|
|
214
|
+
dks = (
|
|
215
|
+
[JsonKeySet.from_model(s) for s in cons.key_sets]
|
|
216
|
+
if cons.key_sets
|
|
217
|
+
else None
|
|
218
|
+
)
|
|
219
|
+
if not cons.name:
|
|
220
|
+
raise errors.Invalid(
|
|
221
|
+
"Invalid input",
|
|
222
|
+
"SDMX-JSON data constraints must have a name",
|
|
223
|
+
{"data_constraint": cons.id},
|
|
224
|
+
)
|
|
225
|
+
if not cons.constraint_attachment:
|
|
226
|
+
raise errors.Invalid(
|
|
227
|
+
"Invalid input",
|
|
228
|
+
"SDMX-JSON data constraints must have a constraint attachment",
|
|
229
|
+
{"data_constraint": cons.id},
|
|
230
|
+
)
|
|
231
|
+
return JsonDataConstraint(
|
|
232
|
+
id=cons.id,
|
|
233
|
+
name=cons.name,
|
|
234
|
+
agency=(
|
|
235
|
+
cons.agency.id
|
|
236
|
+
if isinstance(cons.agency, Agency)
|
|
237
|
+
else cons.agency
|
|
238
|
+
),
|
|
239
|
+
description=cons.description,
|
|
240
|
+
version=cons.version,
|
|
241
|
+
annotations=tuple(
|
|
242
|
+
[JsonAnnotation.from_model(a) for a in cons.annotations]
|
|
243
|
+
),
|
|
244
|
+
isExternalReference=cons.is_external_reference,
|
|
245
|
+
validFrom=cons.valid_from,
|
|
246
|
+
validTo=cons.valid_to,
|
|
247
|
+
constraintAttachment=JsonConstraintAttachment.from_model(
|
|
248
|
+
cons.constraint_attachment
|
|
249
|
+
),
|
|
250
|
+
cubeRegions=crs,
|
|
251
|
+
dataKeySets=dks,
|
|
252
|
+
)
|
|
253
|
+
|
|
254
|
+
|
|
255
|
+
class JsonDataConstraints(Struct, frozen=True, omit_defaults=True):
|
|
256
|
+
"""SDMX-JSON payload for data constraints."""
|
|
257
|
+
|
|
258
|
+
dataConstraints: Sequence[JsonDataConstraint] = ()
|
|
259
|
+
|
|
260
|
+
def to_model(self) -> Sequence[DataConstraint]:
|
|
261
|
+
"""Returns the requested data constraints."""
|
|
262
|
+
return [cc.to_model() for cc in self.dataConstraints]
|
|
263
|
+
|
|
264
|
+
|
|
265
|
+
class JsonDataConstraintMessage(Struct, frozen=True, omit_defaults=True):
|
|
266
|
+
"""SDMX-JSON payload for /dataconstraint queries."""
|
|
267
|
+
|
|
268
|
+
data: JsonDataConstraints
|
|
269
|
+
|
|
270
|
+
def to_model(self) -> Sequence[DataConstraint]:
|
|
271
|
+
"""Returns the requested data constraints."""
|
|
272
|
+
return self.data.to_model()
|