pysdmx 1.3.0__py3-none-any.whl → 1.4.0rc1__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (100) hide show
  1. pysdmx/__extras_check.py +3 -2
  2. pysdmx/__init__.py +1 -1
  3. pysdmx/api/fmr/__init__.py +4 -4
  4. pysdmx/api/gds/__init__.py +328 -0
  5. pysdmx/api/qb/gds.py +153 -0
  6. pysdmx/api/qb/service.py +91 -3
  7. pysdmx/api/qb/structure.py +1 -0
  8. pysdmx/api/qb/util.py +1 -0
  9. pysdmx/io/__init__.py +2 -1
  10. pysdmx/io/csv/sdmx10/reader/__init__.py +4 -2
  11. pysdmx/io/csv/sdmx10/writer/__init__.py +15 -2
  12. pysdmx/io/csv/sdmx20/reader/__init__.py +5 -2
  13. pysdmx/io/csv/sdmx20/writer/__init__.py +13 -2
  14. pysdmx/io/format.py +4 -0
  15. pysdmx/io/input_processor.py +12 -3
  16. pysdmx/io/json/fusion/messages/core.py +2 -0
  17. pysdmx/io/json/fusion/messages/report.py +13 -7
  18. pysdmx/io/json/gds/messages/__init__.py +35 -0
  19. pysdmx/io/json/gds/messages/agencies.py +41 -0
  20. pysdmx/io/json/gds/messages/catalog.py +79 -0
  21. pysdmx/io/json/gds/messages/sdmx_api.py +23 -0
  22. pysdmx/io/json/gds/messages/services.py +49 -0
  23. pysdmx/io/json/gds/messages/urn_resolver.py +43 -0
  24. pysdmx/io/json/gds/reader/__init__.py +12 -0
  25. pysdmx/io/json/sdmxjson2/messages/__init__.py +12 -4
  26. pysdmx/io/json/sdmxjson2/messages/agency.py +72 -0
  27. pysdmx/io/json/sdmxjson2/messages/category.py +22 -29
  28. pysdmx/io/json/sdmxjson2/messages/code.py +68 -64
  29. pysdmx/io/json/sdmxjson2/messages/concept.py +9 -18
  30. pysdmx/io/json/sdmxjson2/messages/constraint.py +2 -13
  31. pysdmx/io/json/sdmxjson2/messages/core.py +113 -21
  32. pysdmx/io/json/sdmxjson2/messages/dataflow.py +51 -21
  33. pysdmx/io/json/sdmxjson2/messages/dsd.py +110 -36
  34. pysdmx/io/json/sdmxjson2/messages/map.py +61 -49
  35. pysdmx/io/json/sdmxjson2/messages/pa.py +9 -17
  36. pysdmx/io/json/sdmxjson2/messages/provider.py +88 -0
  37. pysdmx/io/json/sdmxjson2/messages/report.py +84 -14
  38. pysdmx/io/json/sdmxjson2/messages/schema.py +14 -5
  39. pysdmx/io/json/sdmxjson2/messages/structure.py +105 -36
  40. pysdmx/io/json/sdmxjson2/messages/vtl.py +42 -96
  41. pysdmx/io/pd.py +2 -9
  42. pysdmx/io/reader.py +72 -27
  43. pysdmx/io/serde.py +11 -0
  44. pysdmx/io/writer.py +134 -0
  45. pysdmx/io/xml/{sdmx21/reader/__data_aux.py → __data_aux.py} +9 -2
  46. pysdmx/io/xml/{sdmx21/reader/__parse_xml.py → __parse_xml.py} +30 -6
  47. pysdmx/io/xml/__ss_aux_reader.py +96 -0
  48. pysdmx/io/xml/__structure_aux_reader.py +1174 -0
  49. pysdmx/io/xml/__structure_aux_writer.py +1233 -0
  50. pysdmx/io/xml/{sdmx21/__tokens.py → __tokens.py} +33 -1
  51. pysdmx/io/xml/{sdmx21/writer/__write_aux.py → __write_aux.py} +129 -37
  52. pysdmx/io/xml/{sdmx21/writer/__write_data_aux.py → __write_data_aux.py} +1 -1
  53. pysdmx/io/xml/__write_structure_specific_aux.py +254 -0
  54. pysdmx/io/xml/{sdmx21/reader/doc_validation.py → doc_validation.py} +10 -2
  55. pysdmx/io/xml/{sdmx21/reader/header.py → header.py} +11 -3
  56. pysdmx/io/xml/sdmx21/reader/error.py +2 -2
  57. pysdmx/io/xml/sdmx21/reader/generic.py +12 -8
  58. pysdmx/io/xml/sdmx21/reader/structure.py +5 -840
  59. pysdmx/io/xml/sdmx21/reader/structure_specific.py +13 -97
  60. pysdmx/io/xml/sdmx21/reader/submission.py +2 -2
  61. pysdmx/io/xml/sdmx21/writer/error.py +1 -1
  62. pysdmx/io/xml/sdmx21/writer/generic.py +13 -7
  63. pysdmx/io/xml/sdmx21/writer/structure.py +16 -828
  64. pysdmx/io/xml/sdmx21/writer/structure_specific.py +13 -238
  65. pysdmx/io/xml/sdmx30/__init__.py +1 -0
  66. pysdmx/io/xml/sdmx30/reader/__init__.py +1 -0
  67. pysdmx/io/xml/sdmx30/reader/structure.py +39 -0
  68. pysdmx/io/xml/sdmx30/reader/structure_specific.py +39 -0
  69. pysdmx/io/xml/sdmx30/writer/__init__.py +1 -0
  70. pysdmx/io/xml/sdmx30/writer/structure.py +67 -0
  71. pysdmx/io/xml/sdmx30/writer/structure_specific.py +108 -0
  72. pysdmx/model/__base.py +99 -34
  73. pysdmx/model/__init__.py +4 -0
  74. pysdmx/model/category.py +20 -0
  75. pysdmx/model/code.py +29 -8
  76. pysdmx/model/concept.py +52 -11
  77. pysdmx/model/dataflow.py +117 -33
  78. pysdmx/model/dataset.py +66 -14
  79. pysdmx/model/gds.py +161 -0
  80. pysdmx/model/map.py +51 -8
  81. pysdmx/model/message.py +235 -55
  82. pysdmx/model/metadata.py +79 -16
  83. pysdmx/model/submission.py +12 -7
  84. pysdmx/model/vtl.py +30 -13
  85. pysdmx/toolkit/__init__.py +1 -1
  86. pysdmx/toolkit/pd/__init__.py +85 -0
  87. pysdmx/toolkit/vtl/__init__.py +2 -1
  88. pysdmx/toolkit/vtl/_validations.py +1 -1
  89. pysdmx/toolkit/vtl/{generate_vtl_script.py → script_generation.py} +30 -4
  90. pysdmx/toolkit/vtl/validation.py +119 -0
  91. pysdmx/util/_model_utils.py +1 -1
  92. pysdmx-1.4.0rc1.dist-info/METADATA +119 -0
  93. pysdmx-1.4.0rc1.dist-info/RECORD +140 -0
  94. pysdmx/io/json/sdmxjson2/messages/org.py +0 -140
  95. pysdmx/toolkit/vtl/model_validations.py +0 -50
  96. pysdmx-1.3.0.dist-info/METADATA +0 -76
  97. pysdmx-1.3.0.dist-info/RECORD +0 -116
  98. /pysdmx/io/xml/{sdmx21/writer/config.py → config.py} +0 -0
  99. {pysdmx-1.3.0.dist-info → pysdmx-1.4.0rc1.dist-info}/LICENSE +0 -0
  100. {pysdmx-1.3.0.dist-info → pysdmx-1.4.0rc1.dist-info}/WHEEL +0 -0
@@ -1,108 +1,24 @@
1
- """SDMX 2.1 XML StructureSpecificData reader module."""
1
+ """SDMX XML 2.1 StructureSpecificData reader module."""
2
2
 
3
- import itertools
4
- from typing import Any, Dict, Sequence
5
-
6
- import numpy as np
7
- import pandas as pd
3
+ from typing import Sequence
8
4
 
9
5
  from pysdmx.errors import Invalid
10
6
  from pysdmx.io.pd import PandasDataset
11
- from pysdmx.io.xml.sdmx21.__tokens import (
12
- EXCLUDED_ATTRIBUTES,
13
- GROUP,
14
- OBS,
15
- SERIES,
7
+ from pysdmx.io.xml.__data_aux import (
8
+ get_data_objects,
9
+ )
10
+ from pysdmx.io.xml.__parse_xml import parse_xml
11
+ from pysdmx.io.xml.__ss_aux_reader import (
12
+ _parse_structure_specific_data,
13
+ )
14
+ from pysdmx.io.xml.__tokens import (
16
15
  STR_REF,
17
16
  STR_SPE,
18
17
  )
19
- from pysdmx.io.xml.sdmx21.reader.__data_aux import (
20
- __process_df,
21
- get_data_objects,
22
- )
23
- from pysdmx.io.xml.sdmx21.reader.__parse_xml import parse_xml
24
- from pysdmx.io.xml.utils import add_list
25
- from pysdmx.model.dataset import ActionType
26
-
27
-
28
- def __reading_str_series(dataset: Dict[str, Any]) -> pd.DataFrame:
29
- # Structure Specific Series
30
- test_list = []
31
- df = None
32
- dataset[SERIES] = add_list(dataset[SERIES])
33
- for data in dataset[SERIES]:
34
- keys = dict(itertools.islice(data.items(), len(data)))
35
- if OBS in data:
36
- del keys[OBS]
37
- data[OBS] = add_list(data[OBS])
38
- for j in data[OBS]:
39
- test_list.append({**keys, **j})
40
- else:
41
- test_list.append(keys)
42
- test_list, df = __process_df(test_list, df)
43
-
44
- test_list, df = __process_df(test_list, df, is_end=True)
45
-
46
- return df
47
-
48
-
49
- def __reading_group_data(dataset: Dict[str, Any]) -> pd.DataFrame:
50
- # Structure Specific Group Data
51
- test_list = []
52
- df = None
53
- dataset[GROUP] = add_list(dataset[GROUP])
54
- for data in dataset[GROUP]:
55
- test_list.append(dict(data.items()))
56
- test_list, df = __process_df(test_list, df)
57
- test_list, df = __process_df(test_list, df, is_end=True)
58
-
59
- cols_to_delete = [x for x in df.columns if ":type" in x]
60
- for x in cols_to_delete:
61
- del df[x]
62
-
63
- df = df.drop_duplicates(keep="first").reset_index(drop=True)
64
-
65
- return df
66
-
67
-
68
- def __get_at_att_str(dataset: Dict[str, Any]) -> Dict[str, Any]:
69
- """Gets the elements of the dataset if it is Structure Specific Data."""
70
- return {k: dataset[k] for k in dataset if k not in EXCLUDED_ATTRIBUTES}
71
-
72
-
73
- def __parse_structure_specific_data(
74
- dataset: Dict[str, Any], structure_info: Dict[str, Any]
75
- ) -> PandasDataset:
76
- attached_attributes = __get_at_att_str(dataset)
77
-
78
- df = pd.DataFrame()
79
-
80
- # Parsing data
81
- if SERIES in dataset:
82
- # Structure Specific Series
83
- df = __reading_str_series(dataset)
84
- if GROUP in dataset:
85
- df_group = __reading_group_data(dataset)
86
- common_columns = list(
87
- set(df.columns).intersection(set(df_group.columns))
88
- )
89
- df = pd.merge(df, df_group, on=common_columns, how="left")
90
- elif OBS in dataset:
91
- dataset[OBS] = add_list(dataset[OBS])
92
- # Structure Specific All dimensions
93
- df = pd.DataFrame(dataset[OBS]).replace(np.nan, "")
94
-
95
- urn = f"{structure_info['structure_type']}={structure_info['unique_id']}"
96
- action = dataset.get("action", "Information")
97
- action = ActionType(action)
98
-
99
- return PandasDataset(
100
- structure=urn, attributes=attached_attributes, data=df, action=action
101
- )
102
18
 
103
19
 
104
20
  def read(input_str: str, validate: bool = True) -> Sequence[PandasDataset]:
105
- """Reads an SDMX-ML 2.1 Generic file and returns a Sequence of Datasets.
21
+ """Reads an SDMX-ML 2.1 and returns a Sequence of Datasets.
106
22
 
107
23
  Args:
108
24
  input_str: SDMX-ML data to read.
@@ -111,12 +27,12 @@ def read(input_str: str, validate: bool = True) -> Sequence[PandasDataset]:
111
27
  dict_info = parse_xml(input_str, validate=validate)
112
28
  if STR_SPE not in dict_info:
113
29
  raise Invalid(
114
- "This SDMX document is not an SDMX-ML 2.1 StructureSpecificData."
30
+ "This SDMX document is not an SDMX-ML StructureSpecificData."
115
31
  )
116
32
  dataset_info, str_info = get_data_objects(dict_info[STR_SPE])
117
33
  datasets = []
118
34
  for dataset in dataset_info:
119
- ds = __parse_structure_specific_data(
35
+ ds = _parse_structure_specific_data(
120
36
  dataset, str_info[dataset[STR_REF]]
121
37
  )
122
38
  datasets.append(ds)
@@ -3,7 +3,8 @@
3
3
  from typing import Any, Dict, Sequence
4
4
 
5
5
  from pysdmx.errors import Invalid
6
- from pysdmx.io.xml.sdmx21.__tokens import (
6
+ from pysdmx.io.xml.__parse_xml import parse_xml
7
+ from pysdmx.io.xml.__tokens import (
7
8
  ACTION,
8
9
  MAINTAINABLE_OBJECT,
9
10
  REG_INTERFACE,
@@ -14,7 +15,6 @@ from pysdmx.io.xml.sdmx21.__tokens import (
14
15
  SUBMITTED_STRUCTURE,
15
16
  URN,
16
17
  )
17
- from pysdmx.io.xml.sdmx21.reader.__parse_xml import parse_xml
18
18
  from pysdmx.model.submission import SubmissionResult
19
19
  from pysdmx.util import parse_urn
20
20
 
@@ -1,7 +1,7 @@
1
1
  """Module for writing SDMX-ML 2.1 Error messages."""
2
2
 
3
3
  from pysdmx.io.format import Format
4
- from pysdmx.io.xml.sdmx21.writer.__write_aux import __namespaces_from_type
4
+ from pysdmx.io.xml.__write_aux import __namespaces_from_type
5
5
 
6
6
 
7
7
  def write() -> None:
@@ -1,13 +1,14 @@
1
1
  # mypy: disable-error-code="union-attr"
2
2
  """Module for writing SDMX-ML 2.1 Generic data messages."""
3
3
 
4
- from typing import Any, Dict, List, Optional, Sequence, Tuple
4
+ from pathlib import Path
5
+ from typing import Any, Dict, List, Optional, Sequence, Tuple, Union
5
6
 
6
7
  import pandas as pd
7
8
 
8
9
  from pysdmx.io.format import Format
9
10
  from pysdmx.io.pd import PandasDataset
10
- from pysdmx.io.xml.sdmx21.writer.__write_aux import (
11
+ from pysdmx.io.xml.__write_aux import (
11
12
  ABBR_GEN,
12
13
  ABBR_MSG,
13
14
  ALL_DIM,
@@ -17,13 +18,13 @@ from pysdmx.io.xml.sdmx21.writer.__write_aux import (
17
18
  get_end_message,
18
19
  get_structure,
19
20
  )
20
- from pysdmx.io.xml.sdmx21.writer.__write_data_aux import (
21
+ from pysdmx.io.xml.__write_data_aux import (
21
22
  check_content_dataset,
22
23
  check_dimension_at_observation,
23
24
  get_codes,
24
25
  writing_validation,
25
26
  )
26
- from pysdmx.io.xml.sdmx21.writer.config import CHUNKSIZE
27
+ from pysdmx.io.xml.config import CHUNKSIZE
27
28
  from pysdmx.model.message import Header
28
29
  from pysdmx.util import parse_short_urn
29
30
 
@@ -159,6 +160,7 @@ def __write_data_single_dataset(
159
160
  outfile = ""
160
161
  structure_urn = get_structure(dataset)
161
162
  id_structure = parse_short_urn(structure_urn).id
163
+ dataset.data = dataset.data.fillna("").astype(str).replace("nan", "")
162
164
 
163
165
  nl = "\n" if prettyprint else ""
164
166
  child1 = "\t" if prettyprint else ""
@@ -277,7 +279,7 @@ def __series_processing(
277
279
  ) -> str:
278
280
  def __generate_series_str() -> str:
279
281
  out_list: List[str] = []
280
- data.groupby(by=series_codes + series_att_codes).apply(
282
+ data.groupby(by=series_codes + series_att_codes)[data.columns].apply(
281
283
  lambda x: __format_dict_ser(out_list, x)
282
284
  )
283
285
 
@@ -383,7 +385,7 @@ def __format_ser_str(
383
385
 
384
386
  def write(
385
387
  datasets: Sequence[PandasDataset],
386
- output_path: str = "",
388
+ output_path: Optional[Union[str, Path]] = None,
387
389
  prettyprint: bool = True,
388
390
  header: Optional[Header] = None,
389
391
  dimension_at_observation: Optional[Dict[str, str]] = None,
@@ -425,7 +427,11 @@ def write(
425
427
 
426
428
  outfile += get_end_message(type_, prettyprint)
427
429
 
428
- if output_path == "":
430
+ output_path = (
431
+ str(output_path) if isinstance(output_path, Path) else output_path
432
+ )
433
+
434
+ if output_path is None or output_path == "":
429
435
  return outfile
430
436
 
431
437
  with open(output_path, "w", encoding="UTF-8", errors="replace") as f: