vtlengine 1.2.1rc1__py3-none-any.whl → 1.3.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.
Potentially problematic release.
This version of vtlengine might be problematic. Click here for more details.
- vtlengine/API/_InternalApi.py +35 -12
- vtlengine/API/__init__.py +52 -13
- vtlengine/API/data/schema/external_routines_schema.json +34 -0
- vtlengine/API/data/schema/value_domain_schema.json +97 -0
- vtlengine/AST/ASTConstructorModules/Terminals.py +1 -5
- vtlengine/AST/Grammar/lexer.py +1112 -19758
- vtlengine/AST/Grammar/parser.py +3224 -17981
- vtlengine/AST/__init__.py +3 -3
- vtlengine/DataTypes/TimeHandling.py +12 -7
- vtlengine/DataTypes/__init__.py +92 -0
- vtlengine/{files/parser → DataTypes}/_time_checking.py +8 -3
- vtlengine/Exceptions/messages.py +13 -0
- vtlengine/Interpreter/__init__.py +60 -16
- vtlengine/Model/__init__.py +47 -3
- vtlengine/Operators/Aggregation.py +10 -2
- vtlengine/Operators/Conditional.py +52 -34
- vtlengine/Operators/General.py +1 -1
- vtlengine/Operators/Validation.py +33 -5
- vtlengine/Operators/__init__.py +10 -4
- vtlengine/__init__.py +1 -1
- vtlengine/files/parser/__init__.py +17 -7
- {vtlengine-1.2.1rc1.dist-info → vtlengine-1.3.0rc1.dist-info}/METADATA +7 -6
- {vtlengine-1.2.1rc1.dist-info → vtlengine-1.3.0rc1.dist-info}/RECORD +25 -23
- {vtlengine-1.2.1rc1.dist-info → vtlengine-1.3.0rc1.dist-info}/WHEEL +1 -1
- {vtlengine-1.2.1rc1.dist-info → vtlengine-1.3.0rc1.dist-info/licenses}/LICENSE.md +0 -0
vtlengine/API/_InternalApi.py
CHANGED
|
@@ -46,6 +46,10 @@ schema_path = base_path / "data" / "schema"
|
|
|
46
46
|
sdmx_csv_path = base_path / "data" / "sdmx_csv"
|
|
47
47
|
with open(schema_path / "json_schema_2.1.json", "r") as file:
|
|
48
48
|
schema = json.load(file)
|
|
49
|
+
with open(schema_path / "value_domain_schema.json", "r") as file:
|
|
50
|
+
vd_schema = json.load(file)
|
|
51
|
+
with open(schema_path / "external_routines_schema.json", "r") as file:
|
|
52
|
+
external_routine_schema = json.load(file)
|
|
49
53
|
|
|
50
54
|
|
|
51
55
|
def _load_dataset_from_structure(
|
|
@@ -112,6 +116,7 @@ def _load_dataset_from_structure(
|
|
|
112
116
|
if "scalars" in structures:
|
|
113
117
|
for scalar_json in structures["scalars"]:
|
|
114
118
|
scalar_name = scalar_json["name"]
|
|
119
|
+
check_key("type", SCALAR_TYPES.keys(), scalar_json["type"])
|
|
115
120
|
scalar = Scalar(
|
|
116
121
|
name=scalar_name,
|
|
117
122
|
data_type=SCALAR_TYPES[scalar_json["type"]],
|
|
@@ -235,8 +240,16 @@ def _handle_scalars_values(
|
|
|
235
240
|
# Handling scalar values with the scalar dict
|
|
236
241
|
for name, value in scalar_values.items():
|
|
237
242
|
if name not in scalars:
|
|
238
|
-
raise
|
|
243
|
+
raise InputValidationException(code="0-1-2-6", name=name)
|
|
239
244
|
# Casting value to scalar data type
|
|
245
|
+
if not scalars[name].data_type.check(value):
|
|
246
|
+
raise InputValidationException(
|
|
247
|
+
code="0-1-2-7",
|
|
248
|
+
value=value,
|
|
249
|
+
type_=scalars[name].data_type.__name__,
|
|
250
|
+
op_type=type(scalars[name]).__name__,
|
|
251
|
+
name=name,
|
|
252
|
+
)
|
|
240
253
|
scalars[name].value = scalars[name].data_type.cast(value)
|
|
241
254
|
|
|
242
255
|
|
|
@@ -321,11 +334,20 @@ def load_vtl(input: Union[str, Path]) -> str:
|
|
|
321
334
|
return f.read()
|
|
322
335
|
|
|
323
336
|
|
|
337
|
+
def _validate_json(data: Dict[str, Any], schema: Dict[str, Any]) -> None:
|
|
338
|
+
try:
|
|
339
|
+
jsonschema.validate(instance=data, schema=schema)
|
|
340
|
+
except jsonschema.ValidationError:
|
|
341
|
+
raise Exception("The given json does not follow the schema.")
|
|
342
|
+
|
|
343
|
+
|
|
324
344
|
def _load_single_value_domain(input: Path) -> Dict[str, ValueDomain]:
|
|
325
345
|
if input.suffix != ".json":
|
|
326
346
|
raise Exception("Invalid Value Domain file. Must have .json extension")
|
|
327
347
|
with open(input, "r") as f:
|
|
328
|
-
|
|
348
|
+
data = json.load(f)
|
|
349
|
+
_validate_json(data, vd_schema)
|
|
350
|
+
vd = ValueDomain.from_dict(data)
|
|
329
351
|
return {vd.name: vd}
|
|
330
352
|
|
|
331
353
|
|
|
@@ -344,6 +366,7 @@ def load_value_domains(input: Union[Dict[str, Any], Path]) -> Dict[str, ValueDom
|
|
|
344
366
|
or the value domains file does not exist.
|
|
345
367
|
"""
|
|
346
368
|
if isinstance(input, dict):
|
|
369
|
+
_validate_json(input, vd_schema)
|
|
347
370
|
vd = ValueDomain.from_dict(input)
|
|
348
371
|
return {vd.name: vd}
|
|
349
372
|
if not isinstance(input, Path):
|
|
@@ -377,12 +400,12 @@ def load_external_routines(input: Union[Dict[str, Any], Path, str]) -> Any:
|
|
|
377
400
|
"""
|
|
378
401
|
external_routines = {}
|
|
379
402
|
if isinstance(input, dict):
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
403
|
+
_validate_json(input, external_routine_schema)
|
|
404
|
+
ext_routine = ExternalRoutine.from_sql_query(input["name"], input["query"])
|
|
405
|
+
external_routines[ext_routine.name] = ext_routine
|
|
383
406
|
return external_routines
|
|
384
407
|
if not isinstance(input, Path):
|
|
385
|
-
raise Exception("Input invalid. Input must be a
|
|
408
|
+
raise Exception("Input invalid. Input must be a json file.")
|
|
386
409
|
if not input.exists():
|
|
387
410
|
raise Exception("Input invalid. Input does not exist")
|
|
388
411
|
if input.is_dir():
|
|
@@ -411,17 +434,17 @@ def _return_only_persistent_datasets(
|
|
|
411
434
|
|
|
412
435
|
|
|
413
436
|
def _load_single_external_routine_from_file(input: Path) -> Any:
|
|
414
|
-
"""
|
|
415
|
-
Returns a single external routine.
|
|
416
|
-
"""
|
|
417
437
|
if not isinstance(input, Path):
|
|
418
438
|
raise Exception("Input invalid")
|
|
419
439
|
if not input.exists():
|
|
420
440
|
raise Exception("Input does not exist")
|
|
421
|
-
if input.suffix != ".
|
|
422
|
-
raise Exception("Input must be a
|
|
441
|
+
if input.suffix != ".json":
|
|
442
|
+
raise Exception("Input must be a json file")
|
|
443
|
+
routine_name = input.stem
|
|
423
444
|
with open(input, "r") as f:
|
|
424
|
-
|
|
445
|
+
data = json.load(f)
|
|
446
|
+
_validate_json(data, external_routine_schema)
|
|
447
|
+
ext_rout = ExternalRoutine.from_sql_query(routine_name, data["query"])
|
|
425
448
|
return ext_rout
|
|
426
449
|
|
|
427
450
|
|
vtlengine/API/__init__.py
CHANGED
|
@@ -163,8 +163,17 @@ def semantic_analysis(
|
|
|
163
163
|
that holds the vtl script.
|
|
164
164
|
data_structures: Dict or Path (file or folder), \
|
|
165
165
|
or List of Dicts or Paths with the data structures JSON files.
|
|
166
|
-
value_domains: Dict or Path of
|
|
167
|
-
|
|
166
|
+
value_domains: Dict or Path, or List of Dicts or Paths of the \
|
|
167
|
+
value domains JSON files. (default:None) It is passed as an object, that can be read from \
|
|
168
|
+
a Path or from a dictionary. Furthermore, a list of those objects can be passed. \
|
|
169
|
+
Check the following example: \
|
|
170
|
+
:ref:`Example 6 <example_6_run_with_multiple_value_domains_and_external_routines>`.
|
|
171
|
+
|
|
172
|
+
external_routines: String or Path, or List of Strings or Paths of the \
|
|
173
|
+
external routines SQL files. (default: None) It is passed as an object, that can be read \
|
|
174
|
+
from a Path or from a dictionary. Furthermore, a list of those objects can be passed. \
|
|
175
|
+
Check the following example: \
|
|
176
|
+
:ref:`Example 6 <example_6_run_with_multiple_value_domains_and_external_routines>`.
|
|
168
177
|
|
|
169
178
|
Returns:
|
|
170
179
|
The computed datasets.
|
|
@@ -206,8 +215,10 @@ def run(
|
|
|
206
215
|
script: Union[str, TransformationScheme, Path],
|
|
207
216
|
data_structures: Union[Dict[str, Any], Path, List[Dict[str, Any]], List[Path]],
|
|
208
217
|
datapoints: Union[Dict[str, pd.DataFrame], str, Path, List[Dict[str, Any]], List[Path]],
|
|
209
|
-
value_domains: Optional[Union[Dict[str, Any], Path]] = None,
|
|
210
|
-
external_routines: Optional[
|
|
218
|
+
value_domains: Optional[Union[Dict[str, Any], Path, List[Union[Dict[str, Any], Path]]]] = None,
|
|
219
|
+
external_routines: Optional[
|
|
220
|
+
Union[Dict[str, Any], Path, List[Union[Dict[str, Any], Path]]]
|
|
221
|
+
] = None,
|
|
211
222
|
time_period_output_format: str = "vtl",
|
|
212
223
|
return_only_persistent: bool = True,
|
|
213
224
|
output_folder: Optional[Union[str, Path]] = None,
|
|
@@ -265,9 +276,17 @@ def run(
|
|
|
265
276
|
|
|
266
277
|
datapoints: Dict, Path, S3 URI or List of S3 URIs or Paths with data.
|
|
267
278
|
|
|
268
|
-
value_domains: Dict or Path of
|
|
279
|
+
value_domains: Dict or Path, or List of Dicts or Paths of the \
|
|
280
|
+
value domains JSON files. (default:None) It is passed as an object, that can be read from \
|
|
281
|
+
a Path or from a dictionary. Furthermore, a list of those objects can be passed. \
|
|
282
|
+
Check the following example: \
|
|
283
|
+
:ref:`Example 6 <example_6_run_with_multiple_value_domains_and_external_routines>`.
|
|
269
284
|
|
|
270
|
-
external_routines: String or Path of
|
|
285
|
+
external_routines: String or Path, or List of Strings or Paths of the \
|
|
286
|
+
external routines JSON files. (default: None) It is passed as an object, that can be read \
|
|
287
|
+
from a Path or from a dictionary. Furthermore, a list of those objects can be passed. \
|
|
288
|
+
Check the following example: \
|
|
289
|
+
:ref:`Example 6 <example_6_run_with_multiple_value_domains_and_external_routines>`.
|
|
271
290
|
|
|
272
291
|
time_period_output_format: String with the possible values \
|
|
273
292
|
("sdmx_gregorian", "sdmx_reporting", "vtl") for the representation of the \
|
|
@@ -303,10 +322,20 @@ def run(
|
|
|
303
322
|
# Handling of library items
|
|
304
323
|
vd = None
|
|
305
324
|
if value_domains is not None:
|
|
306
|
-
|
|
325
|
+
if isinstance(value_domains, list):
|
|
326
|
+
vd = {}
|
|
327
|
+
for item in value_domains:
|
|
328
|
+
vd.update(load_value_domains(item))
|
|
329
|
+
else:
|
|
330
|
+
vd = load_value_domains(value_domains)
|
|
307
331
|
ext_routines = None
|
|
308
332
|
if external_routines is not None:
|
|
309
|
-
|
|
333
|
+
if isinstance(external_routines, list):
|
|
334
|
+
ext_routines = {}
|
|
335
|
+
for item in external_routines:
|
|
336
|
+
ext_routines.update(load_external_routines(item))
|
|
337
|
+
else:
|
|
338
|
+
ext_routines = load_external_routines(external_routines)
|
|
310
339
|
|
|
311
340
|
# Checking time period output format value
|
|
312
341
|
time_period_representation = TimePeriodRepresentation.check_value(time_period_output_format)
|
|
@@ -348,8 +377,10 @@ def run_sdmx( # noqa: C901
|
|
|
348
377
|
script: Union[str, TransformationScheme, Path],
|
|
349
378
|
datasets: Sequence[PandasDataset],
|
|
350
379
|
mappings: Optional[Union[VtlDataflowMapping, Dict[str, str]]] = None,
|
|
351
|
-
value_domains: Optional[Union[Dict[str, Any], Path]] = None,
|
|
352
|
-
external_routines: Optional[
|
|
380
|
+
value_domains: Optional[Union[Dict[str, Any], Path, List[Union[Dict[str, Any], Path]]]] = None,
|
|
381
|
+
external_routines: Optional[
|
|
382
|
+
Union[Dict[str, Any], Path, List[Union[Dict[str, Any], Path]]]
|
|
383
|
+
] = None,
|
|
353
384
|
time_period_output_format: str = "vtl",
|
|
354
385
|
return_only_persistent: bool = True,
|
|
355
386
|
output_folder: Optional[Union[str, Path]] = None,
|
|
@@ -388,9 +419,17 @@ def run_sdmx( # noqa: C901
|
|
|
388
419
|
|
|
389
420
|
mappings: A dictionary or VtlDataflowMapping object that maps the dataset names.
|
|
390
421
|
|
|
391
|
-
value_domains: Dict or Path of
|
|
392
|
-
|
|
393
|
-
|
|
422
|
+
value_domains: Dict or Path, or List of Dicts or Paths of the \
|
|
423
|
+
value domains JSON files. (default:None) It is passed as an object, that can be read from \
|
|
424
|
+
a Path or from a dictionary. Furthermore, a list of those objects can be passed. \
|
|
425
|
+
Check the following example: \
|
|
426
|
+
:ref:`Example 6 <example_6_run_with_multiple_value_domains_and_external_routines>`.
|
|
427
|
+
|
|
428
|
+
external_routines: String or Path, or List of Strings or Paths of the \
|
|
429
|
+
external routines JSON files. (default: None) It is passed as an object, that can be read \
|
|
430
|
+
from a Path or from a dictionary. Furthermore, a list of those objects can be passed. \
|
|
431
|
+
Check the following example: \
|
|
432
|
+
:ref:`Example 6 <example_6_run_with_multiple_value_domains_and_external_routines>`.
|
|
394
433
|
|
|
395
434
|
time_period_output_format: String with the possible values \
|
|
396
435
|
("sdmx_gregorian", "sdmx_reporting", "vtl") for the representation of the \
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"definitions": {
|
|
4
|
+
"sqlQuery": {
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"name": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"minLength": 1,
|
|
10
|
+
"description": "Identifier for the SQL query"
|
|
11
|
+
},
|
|
12
|
+
"query": {
|
|
13
|
+
"type": "string",
|
|
14
|
+
"minLength": 1,
|
|
15
|
+
"description": "SQL query statement"
|
|
16
|
+
}
|
|
17
|
+
},
|
|
18
|
+
"required": ["name", "query"],
|
|
19
|
+
"additionalProperties": false
|
|
20
|
+
}
|
|
21
|
+
},
|
|
22
|
+
"oneOf": [
|
|
23
|
+
{
|
|
24
|
+
"$ref": "#/definitions/sqlQuery"
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
"type": "array",
|
|
28
|
+
"items": {
|
|
29
|
+
"$ref": "#/definitions/sqlQuery"
|
|
30
|
+
},
|
|
31
|
+
"minItems": 1
|
|
32
|
+
}
|
|
33
|
+
]
|
|
34
|
+
}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
{
|
|
2
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
3
|
+
"definitions": {
|
|
4
|
+
"ValueDomain": {
|
|
5
|
+
"type": "object",
|
|
6
|
+
"properties": {
|
|
7
|
+
"name": {
|
|
8
|
+
"type": "string",
|
|
9
|
+
"description": "Name identifier for the Value Domain"
|
|
10
|
+
},
|
|
11
|
+
"setlist": {
|
|
12
|
+
"type": "array",
|
|
13
|
+
"minItems": 1,
|
|
14
|
+
"uniqueItems": true,
|
|
15
|
+
"description": "List of unique values of the Value Domain"
|
|
16
|
+
},
|
|
17
|
+
"type": {
|
|
18
|
+
"type": "string",
|
|
19
|
+
"enum": ["Integer", "Number", "String", "Boolean", "Date", "Time_Period", "Time", "Duration"],
|
|
20
|
+
"description": "Data type of the Value Domain"
|
|
21
|
+
}
|
|
22
|
+
},
|
|
23
|
+
"required": ["name", "setlist", "type"],
|
|
24
|
+
"additionalProperties": false,
|
|
25
|
+
"allOf": [
|
|
26
|
+
{
|
|
27
|
+
"if": {
|
|
28
|
+
"properties": {
|
|
29
|
+
"type": {"const": "Integer"}
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"then": {
|
|
33
|
+
"properties": {
|
|
34
|
+
"setlist": {
|
|
35
|
+
"items": {"type": "integer"}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
},
|
|
40
|
+
{
|
|
41
|
+
"if": {
|
|
42
|
+
"properties": {
|
|
43
|
+
"type": {"const": "Number"}
|
|
44
|
+
}
|
|
45
|
+
},
|
|
46
|
+
"then": {
|
|
47
|
+
"properties": {
|
|
48
|
+
"setlist": {
|
|
49
|
+
"items": {"type": "number"}
|
|
50
|
+
}
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"if": {
|
|
56
|
+
"properties": {
|
|
57
|
+
"type": {"const": "Boolean"}
|
|
58
|
+
}
|
|
59
|
+
},
|
|
60
|
+
"then": {
|
|
61
|
+
"properties": {
|
|
62
|
+
"setlist": {
|
|
63
|
+
"items": {"type": "boolean"}
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
"if": {
|
|
70
|
+
"properties": {
|
|
71
|
+
"type": {"enum": ["String", "Date", "Time_Period", "Time", "Duration"]}
|
|
72
|
+
}
|
|
73
|
+
},
|
|
74
|
+
"then": {
|
|
75
|
+
"properties": {
|
|
76
|
+
"setlist": {
|
|
77
|
+
"items": {"type": "string"}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
]
|
|
83
|
+
}
|
|
84
|
+
},
|
|
85
|
+
"oneOf": [
|
|
86
|
+
{
|
|
87
|
+
"$ref": "#/definitions/ValueDomain"
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
"type": "array",
|
|
91
|
+
"items": {
|
|
92
|
+
"$ref": "#/definitions/ValueDomain"
|
|
93
|
+
},
|
|
94
|
+
"minItems": 1
|
|
95
|
+
}
|
|
96
|
+
]
|
|
97
|
+
}
|
|
@@ -622,11 +622,7 @@ class Terminals(VtlVisitor):
|
|
|
622
622
|
erLevel: ERRORLEVEL constant;
|
|
623
623
|
"""
|
|
624
624
|
ctx_list = list(ctx.getChildren())
|
|
625
|
-
|
|
626
|
-
try:
|
|
627
|
-
return int(self.visitConstant(ctx_list[1]).value)
|
|
628
|
-
except Exception:
|
|
629
|
-
raise Exception(f"Error level must be an integer, line {ctx_list[1].start.line}")
|
|
625
|
+
return self.visitConstant(ctx_list[1]).value
|
|
630
626
|
|
|
631
627
|
def visitSignature(self, ctx: Parser.SignatureContext, kind="ComponentID"):
|
|
632
628
|
"""
|