vtlengine 1.1rc1__tar.gz → 1.1.1__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.

Potentially problematic release.


This version of vtlengine might be problematic. Click here for more details.

Files changed (66) hide show
  1. vtlengine-1.1.1/PKG-INFO +92 -0
  2. vtlengine-1.1.1/README.md +54 -0
  3. {vtlengine-1.1rc1 → vtlengine-1.1.1}/pyproject.toml +16 -9
  4. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/API/_InternalApi.py +231 -6
  5. vtlengine-1.1.1/src/vtlengine/API/__init__.py +507 -0
  6. vtlengine-1.1.1/src/vtlengine/AST/ASTComment.py +56 -0
  7. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/AST/ASTConstructor.py +71 -18
  8. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/AST/ASTConstructorModules/Expr.py +191 -72
  9. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/AST/ASTConstructorModules/ExprComponents.py +81 -38
  10. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/AST/ASTConstructorModules/Terminals.py +76 -31
  11. vtlengine-1.1.1/src/vtlengine/AST/ASTConstructorModules/__init__.py +50 -0
  12. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/AST/ASTEncoders.py +4 -0
  13. vtlengine-1.1.1/src/vtlengine/AST/ASTString.py +622 -0
  14. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/AST/ASTTemplate.py +28 -2
  15. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/AST/DAG/__init__.py +10 -1
  16. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/AST/__init__.py +127 -14
  17. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Exceptions/messages.py +9 -0
  18. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Interpreter/__init__.py +53 -8
  19. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Model/__init__.py +9 -4
  20. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Operators/Aggregation.py +7 -5
  21. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Operators/Analytic.py +16 -11
  22. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Operators/Conditional.py +20 -5
  23. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Operators/Time.py +11 -10
  24. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Utils/__init__.py +49 -0
  25. vtlengine-1.1.1/src/vtlengine/__init__.py +5 -0
  26. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/files/parser/__init__.py +16 -26
  27. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/files/parser/_rfc_dialect.py +1 -1
  28. vtlengine-1.1rc1/PKG-INFO +0 -248
  29. vtlengine-1.1rc1/README.md +0 -213
  30. vtlengine-1.1rc1/src/vtlengine/API/__init__.py +0 -318
  31. vtlengine-1.1rc1/src/vtlengine/__init__.py +0 -3
  32. {vtlengine-1.1rc1 → vtlengine-1.1.1}/LICENSE.md +0 -0
  33. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/API/data/schema/json_schema_2.1.json +0 -0
  34. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/AST/ASTDataExchange.py +0 -0
  35. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/AST/ASTVisitor.py +0 -0
  36. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/AST/DAG/_words.py +0 -0
  37. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/AST/Grammar/Vtl.g4 +0 -0
  38. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/AST/Grammar/VtlTokens.g4 +0 -0
  39. {vtlengine-1.1rc1/src/vtlengine/AST/ASTConstructorModules → vtlengine-1.1.1/src/vtlengine/AST/Grammar}/__init__.py +0 -0
  40. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/AST/Grammar/lexer.py +0 -0
  41. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/AST/Grammar/parser.py +0 -0
  42. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/AST/Grammar/tokens.py +0 -0
  43. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/AST/VtlVisitor.py +0 -0
  44. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/DataTypes/TimeHandling.py +0 -0
  45. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/DataTypes/__init__.py +0 -0
  46. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Exceptions/__init__.py +0 -0
  47. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Operators/Assignment.py +0 -0
  48. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Operators/Boolean.py +0 -0
  49. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Operators/CastOperator.py +0 -0
  50. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Operators/Clause.py +0 -0
  51. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Operators/Comparison.py +0 -0
  52. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Operators/General.py +0 -0
  53. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Operators/HROperators.py +0 -0
  54. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Operators/Join.py +0 -0
  55. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Operators/Numeric.py +0 -0
  56. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Operators/RoleSetter.py +0 -0
  57. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Operators/Set.py +0 -0
  58. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Operators/String.py +0 -0
  59. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Operators/Validation.py +0 -0
  60. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/Operators/__init__.py +0 -0
  61. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/__extras_check.py +0 -0
  62. {vtlengine-1.1rc1/src/vtlengine/AST/Grammar → vtlengine-1.1.1/src/vtlengine/files}/__init__.py +0 -0
  63. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/files/output/__init__.py +0 -0
  64. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/files/output/_time_period_representation.py +0 -0
  65. {vtlengine-1.1rc1 → vtlengine-1.1.1}/src/vtlengine/files/parser/_time_checking.py +0 -0
  66. /vtlengine-1.1rc1/src/vtlengine/files/__init__.py → /vtlengine-1.1.1/src/vtlengine/py.typed +0 -0
@@ -0,0 +1,92 @@
1
+ Metadata-Version: 2.3
2
+ Name: vtlengine
3
+ Version: 1.1.1
4
+ Summary: Run and Validate VTL Scripts
5
+ License: AGPL-3.0
6
+ Keywords: vtl,sdmx,vtlengine,Validation and Transformation Language
7
+ Author: MeaningfulData
8
+ Author-email: info@meaningfuldata.eu
9
+ Maintainer: Francisco Javier Hernandez del Caño
10
+ Maintainer-email: javier.hernandez@meaningfuldata.eu
11
+ Requires-Python: >=3.9
12
+ Classifier: Development Status :: 5 - Production/Stable
13
+ Classifier: Intended Audience :: Developers
14
+ Classifier: Intended Audience :: Information Technology
15
+ Classifier: Intended Audience :: Science/Research
16
+ Classifier: Typing :: Typed
17
+ Provides-Extra: all
18
+ Provides-Extra: s3
19
+ Requires-Dist: antlr4-python3-runtime (>=4.9.2,<4.10)
20
+ Requires-Dist: duckdb (>=1.1,<1.2)
21
+ Requires-Dist: fsspec (>=2022.11.0,<2023.0) ; extra == "all"
22
+ Requires-Dist: fsspec (>=2022.11.0,<2023.0) ; extra == "s3"
23
+ Requires-Dist: jsonschema (>=3.2.0,<5.0)
24
+ Requires-Dist: networkx (>=2.8,<3.0)
25
+ Requires-Dist: numpy (>=1.23.2,<2) ; python_version < "3.13"
26
+ Requires-Dist: numpy (>=2.1.0) ; python_version >= "3.13"
27
+ Requires-Dist: pandas (>=2.1.4,<3.0)
28
+ Requires-Dist: pysdmx[xml] (>=1.3.0,<2.0)
29
+ Requires-Dist: s3fs (>=2022.11.0,<2023.0) ; extra == "all"
30
+ Requires-Dist: s3fs (>=2022.11.0,<2023.0) ; extra == "s3"
31
+ Requires-Dist: sqlglot (>=22.2.0,<23.0)
32
+ Project-URL: Authors, https://github.com/Meaningful-Data/vtlengine/graphs/contributors
33
+ Project-URL: Documentation, https://docs.vtlengine.meaningfuldata.eu
34
+ Project-URL: IssueTracker, https://github.com/Meaningful-Data/vtlengine/issues
35
+ Project-URL: MeaningfulData, https://www.meaningfuldata.eu/
36
+ Project-URL: Repository, https://github.com/Meaningful-Data/vtlengine
37
+ Description-Content-Type: text/markdown
38
+
39
+ # VTL Engine
40
+
41
+ | | |
42
+ |--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
43
+ | Testing | [![Testing](https://github.com/Meaningful-Data/vtlengine/actions/workflows/testing.yml/badge.svg)](https://github.com/Meaningful-Data/vtlengine/actions/workflows/testing.yml) |
44
+ | Package | [![PyPI Latest Release](https://img.shields.io/pypi/v/vtlengine.svg)](https://pypi.org/project/vtlengine/) |
45
+ | License | [![License - AGPL 3.0](https://img.shields.io/pypi/l/vtlengine.svg)](https://github.com/Meaningful-Data/vtlengine/blob/main/LICENSE.md) |
46
+ | Mentioned in | [![Mentioned in Awesome Official Statistics ](https://awesome.re/mentioned-badge.svg)](http://www.awesomeofficialstatistics.org) |
47
+
48
+ ## Introduction
49
+
50
+ The VTL Engine is a Python library that allows you to validate, format and execute VTL scripts.
51
+
52
+ It is a Python-based library around
53
+ the [VTL Language 2.1](https://sdmx-twg.github.io/vtl/2.1/html/index.html).
54
+
55
+ ## Useful Links
56
+
57
+ - [MeaningfulData: who we are](https://www.meaningfuldata.eu)
58
+ - [Documentation](https://docs.vtlengine.meaningfuldata.eu)
59
+ - [Source Code](https://github.com/Meaningful-Data/vtlengine)
60
+ - [Bug Tracker](https://github.com/Meaningful-Data/vtlengine/issues?q=is%3Aopen+is%3Aissue+label%3Abug)
61
+ - [New features Tracker](https://github.com/Meaningful-Data/vtlengine/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement)
62
+
63
+ ## Installation
64
+
65
+ ### Requirements
66
+
67
+ The VTL Engine requires Python 3.9 or higher.
68
+
69
+ ### Install with pip
70
+
71
+ To install the VTL Engine on any Operating System, you can use pip:
72
+
73
+ ```bash
74
+
75
+ pip install vtlengine
76
+
77
+ ```
78
+
79
+ *Note: it is recommended to install the VTL Engine in a virtual environment.*
80
+
81
+ ### S3 extra
82
+
83
+ If you want to use the S3 functionality, you can install the VTL Engine with the `s3` extra:
84
+
85
+ ```bash
86
+ pip install vtlengine[s3]
87
+ ```
88
+
89
+ ## Documentation
90
+
91
+ The documentation for the VTL Engine is available
92
+ at [docs.vtlengine.meaningfuldata.eu](https://docs.vtlengine.meaningfuldata.eu).
@@ -0,0 +1,54 @@
1
+ # VTL Engine
2
+
3
+ | | |
4
+ |--------------|--------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
5
+ | Testing | [![Testing](https://github.com/Meaningful-Data/vtlengine/actions/workflows/testing.yml/badge.svg)](https://github.com/Meaningful-Data/vtlengine/actions/workflows/testing.yml) |
6
+ | Package | [![PyPI Latest Release](https://img.shields.io/pypi/v/vtlengine.svg)](https://pypi.org/project/vtlengine/) |
7
+ | License | [![License - AGPL 3.0](https://img.shields.io/pypi/l/vtlengine.svg)](https://github.com/Meaningful-Data/vtlengine/blob/main/LICENSE.md) |
8
+ | Mentioned in | [![Mentioned in Awesome Official Statistics ](https://awesome.re/mentioned-badge.svg)](http://www.awesomeofficialstatistics.org) |
9
+
10
+ ## Introduction
11
+
12
+ The VTL Engine is a Python library that allows you to validate, format and execute VTL scripts.
13
+
14
+ It is a Python-based library around
15
+ the [VTL Language 2.1](https://sdmx-twg.github.io/vtl/2.1/html/index.html).
16
+
17
+ ## Useful Links
18
+
19
+ - [MeaningfulData: who we are](https://www.meaningfuldata.eu)
20
+ - [Documentation](https://docs.vtlengine.meaningfuldata.eu)
21
+ - [Source Code](https://github.com/Meaningful-Data/vtlengine)
22
+ - [Bug Tracker](https://github.com/Meaningful-Data/vtlengine/issues?q=is%3Aopen+is%3Aissue+label%3Abug)
23
+ - [New features Tracker](https://github.com/Meaningful-Data/vtlengine/issues?q=is%3Aopen+is%3Aissue+label%3Aenhancement)
24
+
25
+ ## Installation
26
+
27
+ ### Requirements
28
+
29
+ The VTL Engine requires Python 3.9 or higher.
30
+
31
+ ### Install with pip
32
+
33
+ To install the VTL Engine on any Operating System, you can use pip:
34
+
35
+ ```bash
36
+
37
+ pip install vtlengine
38
+
39
+ ```
40
+
41
+ *Note: it is recommended to install the VTL Engine in a virtual environment.*
42
+
43
+ ### S3 extra
44
+
45
+ If you want to use the S3 functionality, you can install the VTL Engine with the `s3` extra:
46
+
47
+ ```bash
48
+ pip install vtlengine[s3]
49
+ ```
50
+
51
+ ## Documentation
52
+
53
+ The documentation for the VTL Engine is available
54
+ at [docs.vtlengine.meaningfuldata.eu](https://docs.vtlengine.meaningfuldata.eu).
@@ -1,10 +1,10 @@
1
1
  [project]
2
2
  name = "vtlengine"
3
- version = "1.1rc1"
3
+ version = "1.1.1"
4
4
  description = "Run and Validate VTL Scripts"
5
5
  license = "AGPL-3.0"
6
6
  readme = "README.md"
7
- requires-python = ">=3.9,<4"
7
+ requires-python = ">=3.9"
8
8
  authors = [
9
9
  {name = "MeaningfulData", email = "info@meaningfuldata.eu"},
10
10
  ]
@@ -25,13 +25,15 @@ keywords = ['vtl', 'sdmx', 'vtlengine', 'Validation and Transformation Language'
25
25
  dependencies = [
26
26
  # PyPi dependencies
27
27
  "duckdb>=1.1,<1.2",
28
-
28
+ "pysdmx[xml]>=1.3.0,<2.0",
29
29
  # APT-supported dependencies
30
30
  "jsonschema>=3.2.0,<5.0",
31
31
  "sqlglot>=22.2.0,<23.0",
32
32
  "antlr4-python3-runtime>=4.9.2,<4.10",
33
- "pandas>=2.1,<3.0",
33
+ "pandas>=2.1.4,<3.0",
34
34
  "networkx>=2.8,<3.0",
35
+ "numpy>=1.23.2,<2 ; python_version < '3.13'",
36
+ "numpy>=2.1.0; python_version >= '3.13'",
35
37
  ]
36
38
 
37
39
  [project.optional-dependencies]
@@ -45,20 +47,25 @@ MeaningfulData = 'https://www.meaningfuldata.eu/'
45
47
  IssueTracker = 'https://github.com/Meaningful-Data/vtlengine/issues'
46
48
  Authors = 'https://github.com/Meaningful-Data/vtlengine/graphs/contributors'
47
49
 
50
+ [tool.poetry.dependencies]
51
+ python = ">=3.9,<4.0"
52
+
48
53
  [tool.poetry.group.dev.dependencies]
49
- pytest = "^8.3"
50
- pytest-cov = "^6.0.0"
54
+ pytest = "^8.4"
55
+ pytest-cov = "^6.1.1"
51
56
  line-profiler-pycharm = "^1.2.0"
52
- mypy = "1.14.1"
57
+ mypy = "1.16.0"
53
58
  pandas-stubs = "^2.1.4.231227"
54
- ruff = "^0.9.4"
55
- types-jsonschema = "4.23.0.20241208"
59
+ ruff = "^0.11.12"
60
+ types-jsonschema = "4.24.0.20250528"
56
61
 
57
62
  [tool.poetry.group.docs.dependencies]
58
63
  sphinx = "^7.4.7"
59
64
  sphinx-rtd-theme = "^3.0.2"
60
65
  toml = "^0.10.2"
61
66
 
67
+
68
+
62
69
  [tool.ruff]
63
70
  line-length = 100
64
71
  lint.mccabe.max-complexity = 20
@@ -1,18 +1,33 @@
1
1
  import json
2
2
  import os
3
3
  from pathlib import Path
4
- from typing import Any, Dict, List, Optional, Union
4
+ from typing import Any, Dict, List, Literal, Optional, Union
5
5
 
6
6
  import jsonschema
7
7
  import pandas as pd
8
+ from pysdmx.model.dataflow import Component as SDMXComponent
9
+ from pysdmx.model.dataflow import DataStructureDefinition, Schema
10
+ from pysdmx.model.dataflow import Role as SDMX_Role
11
+ from pysdmx.model.vtl import (
12
+ Ruleset,
13
+ RulesetScheme,
14
+ Transformation,
15
+ TransformationScheme,
16
+ UserDefinedOperator,
17
+ UserDefinedOperatorScheme,
18
+ )
8
19
 
20
+ from vtlengine import AST as AST
9
21
  from vtlengine.__extras_check import __check_s3_extra
10
- from vtlengine.AST import PersistentAssignment, Start
22
+ from vtlengine.AST import Assignment, DPRuleset, HRuleset, Operator, PersistentAssignment, Start
23
+ from vtlengine.AST.ASTString import ASTString
11
24
  from vtlengine.DataTypes import SCALAR_TYPES
12
25
  from vtlengine.Exceptions import InputValidationException, check_key
13
26
  from vtlengine.files.parser import _fill_dataset_empty_data, _validate_pandas
14
27
  from vtlengine.Model import (
15
- Component,
28
+ Component as VTL_Component,
29
+ )
30
+ from vtlengine.Model import (
16
31
  Dataset,
17
32
  ExternalRoutine,
18
33
  Role,
@@ -20,9 +35,11 @@ from vtlengine.Model import (
20
35
  Scalar,
21
36
  ValueDomain,
22
37
  )
38
+ from vtlengine.Utils import VTL_DTYPES_MAPPING, VTL_ROLE_MAPPING
23
39
 
24
40
  base_path = Path(__file__).parent
25
41
  schema_path = base_path / "data" / "schema"
42
+ sdmx_csv_path = base_path / "data" / "sdmx_csv"
26
43
  with open(schema_path / "json_schema_2.1.json", "r") as file:
27
44
  schema = json.load(file)
28
45
 
@@ -66,7 +83,7 @@ def _load_dataset_from_structure(structures: Dict[str, Any]) -> Dict[str, Any]:
66
83
  else:
67
84
  component["nullable"] = False
68
85
 
69
- components[component["name"]] = Component(
86
+ components[component["name"]] = VTL_Component(
70
87
  name=component["name"],
71
88
  data_type=SCALAR_TYPES[component["data_type"]],
72
89
  role=Role(component["role"]),
@@ -77,7 +94,7 @@ def _load_dataset_from_structure(structures: Dict[str, Any]) -> Dict[str, Any]:
77
94
  for component in dataset_json["DataStructure"]:
78
95
  check_key("data_type", SCALAR_TYPES.keys(), component["type"])
79
96
  check_key("role", Role_keys, component["role"])
80
- components[component["name"]] = Component(
97
+ components[component["name"]] = VTL_Component(
81
98
  name=component["name"],
82
99
  data_type=SCALAR_TYPES[component["type"]],
83
100
  role=Role(component["role"]),
@@ -169,7 +186,7 @@ def _load_datastructure_single(data_structure: Union[Dict[str, Any], Path]) -> D
169
186
 
170
187
 
171
188
  def load_datasets(
172
- data_structure: Union[Dict[str, Any], Path, List[Union[Dict[str, Any], Path]]],
189
+ data_structure: Union[Dict[str, Any], Path, List[Dict[str, Any]], List[Path]],
173
190
  ) -> Dict[str, Dataset]:
174
191
  """
175
192
  Loads multiple datasets.
@@ -393,3 +410,211 @@ def _check_output_folder(output_folder: Union[str, Path]) -> None:
393
410
  if output_folder.suffix != "":
394
411
  raise ValueError("Output folder must be a Path or S3 URI to a directory")
395
412
  os.mkdir(output_folder)
413
+
414
+
415
+ def to_vtl_json(dsd: Union[DataStructureDefinition, Schema], dataset_name: str) -> Dict[str, Any]:
416
+ """
417
+ Converts a pysdmx `DataStructureDefinition` or `Schema` into a VTL-compatible JSON
418
+ representation.
419
+
420
+ This function extracts and transforms the components (dimensions, measures, and attributes)
421
+ from the given SDMX data structure and maps them into a dictionary format that conforms
422
+ to the expected VTL data structure json schema.
423
+
424
+ Args:
425
+ dsd: An instance of `DataStructureDefinition` or `Schema` from the `pysdmx` model.
426
+ dataset_name: The name of the resulting VTL dataset.
427
+
428
+ Returns:
429
+ A dictionary representing the dataset in VTL format, with keys for dataset name and its
430
+ components, including their name, role, data type, and nullability.
431
+ """
432
+ components = []
433
+ NAME = "name"
434
+ ROLE = "role"
435
+ TYPE = "type"
436
+ NULLABLE = "nullable"
437
+
438
+ _components: List[SDMXComponent] = []
439
+ _components.extend(dsd.components.dimensions)
440
+ _components.extend(dsd.components.measures)
441
+ _components.extend(dsd.components.attributes)
442
+
443
+ for c in _components:
444
+ _type = VTL_DTYPES_MAPPING[c.dtype]
445
+ _nullability = c.role != SDMX_Role.DIMENSION
446
+ _role = VTL_ROLE_MAPPING[c.role]
447
+
448
+ component = {
449
+ NAME: c.id,
450
+ ROLE: _role,
451
+ TYPE: _type,
452
+ NULLABLE: _nullability,
453
+ }
454
+
455
+ components.append(component)
456
+
457
+ result = {"datasets": [{"name": dataset_name, "DataStructure": components}]}
458
+
459
+ return result
460
+
461
+
462
+ def __generate_transformation(
463
+ child: Union[Assignment, PersistentAssignment], is_persistent: bool, count: int
464
+ ) -> Transformation:
465
+ expression = ASTString().render(ast=child.right)
466
+ result = child.left.value # type: ignore[attr-defined]
467
+ return Transformation(
468
+ id=f"T{count}",
469
+ expression=expression,
470
+ is_persistent=is_persistent,
471
+ result=result,
472
+ name=f"Transformation {result}",
473
+ )
474
+
475
+
476
+ def __generate_udo(child: Operator, count: int) -> UserDefinedOperator:
477
+ operator_definition = ASTString().render(ast=child)
478
+ return UserDefinedOperator(
479
+ id=f"UDO{count}",
480
+ operator_definition=operator_definition,
481
+ name=f"UserDefinedOperator {child.op}",
482
+ )
483
+
484
+
485
+ def __generate_ruleset(child: Union[DPRuleset, HRuleset], count: int) -> Ruleset:
486
+ ruleset_definition = ASTString().render(ast=child)
487
+ ruleset_type: Literal["datapoint", "hierarchical"] = (
488
+ "datapoint" if isinstance(child, DPRuleset) else "hierarchical"
489
+ )
490
+ ruleset_scope: Literal["variable", "valuedomain"] = (
491
+ "variable" if child.signature_type == "variable" else "valuedomain"
492
+ )
493
+ return Ruleset(
494
+ id=f"R{count}",
495
+ ruleset_definition=ruleset_definition,
496
+ ruleset_type=ruleset_type,
497
+ ruleset_scope=ruleset_scope,
498
+ name=f"{ruleset_type.capitalize()} ruleset {child.name}",
499
+ )
500
+
501
+
502
+ def ast_to_sdmx(ast: AST.Start, agency_id: str, id: str, version: str) -> TransformationScheme:
503
+ """
504
+ Converts a vtl AST into an SDMX compatible `TransformationScheme` object, following
505
+ the pysdmx model.
506
+
507
+ This function iterates over the child nodes of the given AST and categorizes each into one of
508
+ the following types:
509
+ - `PersistentAssignment`: Represents a persistent transformation. These are added to the
510
+ transformation list with a persistence flag.
511
+ - `Assignment`: Represents a temporary (non-persistent) transformation. These are added to the
512
+ transformation list without the persistence flag
513
+ - `DPRuleset` or `HRuleset`: Represent validation rule sets.
514
+ These are collected and wrapped into a `RulesetScheme` object.
515
+ - `Operator`: Defines user-defined operators. These are collected
516
+ into a `UserDefinedOperatorScheme` object.
517
+
518
+ After parsing all AST elements:
519
+ - If any rulesets were found, a `RulesetScheme` is created and added to the references.
520
+ - If any user-defined operators were found, a `UserDefinedOperatorScheme` is created and added
521
+ to the references.
522
+ - A `TransformationScheme` object is constructed with all collected transformations and any
523
+ additional references.
524
+
525
+ Args:
526
+ ast: The root node of the vtl ast representing the set of
527
+ vtl expressions.
528
+ agency_id: The identifier of the agency defining the SDMX structure as a string.
529
+ id: The identifier of the transformation scheme as a string.
530
+ version: The version of the transformation scheme given as a string.
531
+
532
+ Returns:
533
+ TransformationScheme: A fully constructed transformation scheme that includes
534
+ transformations, and optionally rule sets and user-defined operator schemes,
535
+ suitable for SDMX.
536
+
537
+ """
538
+ list_transformation = []
539
+ list_udos = []
540
+ list_rulesets = []
541
+ count_transformation = 0
542
+ count_udo = 0
543
+ count_ruleset = 0
544
+
545
+ for child in ast.children:
546
+ if isinstance(child, PersistentAssignment):
547
+ count_transformation += 1
548
+ list_transformation.append(
549
+ __generate_transformation(
550
+ child=child, is_persistent=True, count=count_transformation
551
+ )
552
+ )
553
+ elif isinstance(child, Assignment):
554
+ count_transformation += 1
555
+ list_transformation.append(
556
+ __generate_transformation(
557
+ child=child, is_persistent=False, count=count_transformation
558
+ )
559
+ )
560
+ elif isinstance(child, (DPRuleset, HRuleset)):
561
+ count_ruleset += 1
562
+ list_rulesets.append(__generate_ruleset(child=child, count=count_ruleset))
563
+ elif isinstance(child, Operator):
564
+ count_udo += 1
565
+ list_udos.append(__generate_udo(child=child, count=count_udo))
566
+
567
+ references: Any = {}
568
+ if list_rulesets:
569
+ references["ruleset_schemes"] = [
570
+ RulesetScheme(
571
+ items=list_rulesets,
572
+ agency=agency_id,
573
+ id="RS1",
574
+ vtl_version="2.1",
575
+ version=version,
576
+ name=f"RulesetScheme {id}-RS",
577
+ )
578
+ ]
579
+ if list_udos:
580
+ references["user_defined_operator_schemes"] = [
581
+ UserDefinedOperatorScheme(
582
+ items=list_udos,
583
+ agency=agency_id,
584
+ id="UDS1",
585
+ vtl_version="2.1",
586
+ version=version,
587
+ name=f"UserDefinedOperatorScheme {id}-UDS",
588
+ )
589
+ ]
590
+
591
+ transformation_scheme = TransformationScheme(
592
+ items=list_transformation,
593
+ agency=agency_id,
594
+ id="TS1",
595
+ vtl_version="2.1",
596
+ version=version,
597
+ name=f"TransformationScheme {id}",
598
+ **references,
599
+ )
600
+
601
+ return transformation_scheme
602
+
603
+
604
+ def _check_script(script: Union[str, TransformationScheme, Path]) -> str:
605
+ """
606
+ Check if the TransformationScheme object is valid to generate a vtl script.
607
+ """
608
+ if not isinstance(script, (str, TransformationScheme, Path)):
609
+ raise Exception(
610
+ "Invalid script format. Input must be a string, TransformationScheme or Path object"
611
+ )
612
+ if isinstance(script, TransformationScheme):
613
+ from pysdmx.toolkit.vtl.generate_vtl_script import (
614
+ generate_vtl_script,
615
+ )
616
+
617
+ vtl_script = generate_vtl_script(script, model_validation=True)
618
+ return vtl_script
619
+ else:
620
+ return str(script)