vtlengine 1.4.0rc2__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 (66) hide show
  1. vtlengine/API/_InternalApi.py +791 -0
  2. vtlengine/API/__init__.py +612 -0
  3. vtlengine/API/data/schema/external_routines_schema.json +34 -0
  4. vtlengine/API/data/schema/json_schema_2.1.json +116 -0
  5. vtlengine/API/data/schema/value_domain_schema.json +97 -0
  6. vtlengine/AST/ASTComment.py +57 -0
  7. vtlengine/AST/ASTConstructor.py +598 -0
  8. vtlengine/AST/ASTConstructorModules/Expr.py +1928 -0
  9. vtlengine/AST/ASTConstructorModules/ExprComponents.py +995 -0
  10. vtlengine/AST/ASTConstructorModules/Terminals.py +790 -0
  11. vtlengine/AST/ASTConstructorModules/__init__.py +50 -0
  12. vtlengine/AST/ASTDataExchange.py +10 -0
  13. vtlengine/AST/ASTEncoders.py +32 -0
  14. vtlengine/AST/ASTString.py +675 -0
  15. vtlengine/AST/ASTTemplate.py +558 -0
  16. vtlengine/AST/ASTVisitor.py +25 -0
  17. vtlengine/AST/DAG/__init__.py +479 -0
  18. vtlengine/AST/DAG/_words.py +10 -0
  19. vtlengine/AST/Grammar/Vtl.g4 +705 -0
  20. vtlengine/AST/Grammar/VtlTokens.g4 +409 -0
  21. vtlengine/AST/Grammar/__init__.py +0 -0
  22. vtlengine/AST/Grammar/lexer.py +2139 -0
  23. vtlengine/AST/Grammar/parser.py +16597 -0
  24. vtlengine/AST/Grammar/tokens.py +169 -0
  25. vtlengine/AST/VtlVisitor.py +824 -0
  26. vtlengine/AST/__init__.py +674 -0
  27. vtlengine/DataTypes/TimeHandling.py +562 -0
  28. vtlengine/DataTypes/__init__.py +863 -0
  29. vtlengine/DataTypes/_time_checking.py +135 -0
  30. vtlengine/Exceptions/__exception_file_generator.py +96 -0
  31. vtlengine/Exceptions/__init__.py +159 -0
  32. vtlengine/Exceptions/messages.py +1004 -0
  33. vtlengine/Interpreter/__init__.py +2048 -0
  34. vtlengine/Model/__init__.py +501 -0
  35. vtlengine/Operators/Aggregation.py +357 -0
  36. vtlengine/Operators/Analytic.py +455 -0
  37. vtlengine/Operators/Assignment.py +23 -0
  38. vtlengine/Operators/Boolean.py +106 -0
  39. vtlengine/Operators/CastOperator.py +451 -0
  40. vtlengine/Operators/Clause.py +366 -0
  41. vtlengine/Operators/Comparison.py +488 -0
  42. vtlengine/Operators/Conditional.py +495 -0
  43. vtlengine/Operators/General.py +191 -0
  44. vtlengine/Operators/HROperators.py +254 -0
  45. vtlengine/Operators/Join.py +447 -0
  46. vtlengine/Operators/Numeric.py +422 -0
  47. vtlengine/Operators/RoleSetter.py +77 -0
  48. vtlengine/Operators/Set.py +176 -0
  49. vtlengine/Operators/String.py +578 -0
  50. vtlengine/Operators/Time.py +1144 -0
  51. vtlengine/Operators/Validation.py +275 -0
  52. vtlengine/Operators/__init__.py +900 -0
  53. vtlengine/Utils/__Virtual_Assets.py +34 -0
  54. vtlengine/Utils/__init__.py +479 -0
  55. vtlengine/__extras_check.py +17 -0
  56. vtlengine/__init__.py +27 -0
  57. vtlengine/files/__init__.py +0 -0
  58. vtlengine/files/output/__init__.py +35 -0
  59. vtlengine/files/output/_time_period_representation.py +55 -0
  60. vtlengine/files/parser/__init__.py +240 -0
  61. vtlengine/files/parser/_rfc_dialect.py +22 -0
  62. vtlengine/py.typed +0 -0
  63. vtlengine-1.4.0rc2.dist-info/METADATA +89 -0
  64. vtlengine-1.4.0rc2.dist-info/RECORD +66 -0
  65. vtlengine-1.4.0rc2.dist-info/WHEEL +4 -0
  66. vtlengine-1.4.0rc2.dist-info/licenses/LICENSE.md +661 -0
@@ -0,0 +1,451 @@
1
+ from copy import copy
2
+ from typing import Any, Optional, Type, Union
3
+
4
+ import pandas as pd
5
+
6
+ import vtlengine.Operators as Operator
7
+ from vtlengine.AST.Grammar.tokens import CAST
8
+ from vtlengine.DataTypes import (
9
+ COMP_NAME_MAPPING,
10
+ EXPLICIT_WITH_MASK_TYPE_PROMOTION_MAPPING,
11
+ EXPLICIT_WITHOUT_MASK_TYPE_PROMOTION_MAPPING,
12
+ IMPLICIT_TYPE_PROMOTION_MAPPING,
13
+ SCALAR_TYPES_CLASS_REVERSE,
14
+ Date,
15
+ Duration,
16
+ Number,
17
+ ScalarType,
18
+ String,
19
+ TimeInterval,
20
+ TimePeriod,
21
+ )
22
+ from vtlengine.DataTypes.TimeHandling import str_period_to_date
23
+ from vtlengine.Exceptions import SemanticError
24
+ from vtlengine.Model import Component, DataComponent, Dataset, Role, Scalar
25
+ from vtlengine.Utils.__Virtual_Assets import VirtualCounter
26
+
27
+ duration_mapping = {"A": 6, "S": 5, "Q": 4, "M": 3, "W": 2, "D": 1}
28
+
29
+ ALL_MODEL_DATA_TYPES = Union[Dataset, Scalar, DataComponent]
30
+
31
+
32
+ class Cast(Operator.Unary):
33
+ op = CAST
34
+
35
+ # CASTS VALUES
36
+ # Converts the value from one type to another in a way that is according to the mask
37
+ @classmethod
38
+ def cast_string_to_number(cls, value: Any, mask: str) -> Any:
39
+ """
40
+ This method casts a string to a number, according to the mask.
41
+
42
+ """
43
+
44
+ raise NotImplementedError("How this mask should be implemented is not yet defined.")
45
+
46
+ @classmethod
47
+ def cast_string_to_date(cls, value: Any, mask: str) -> Any:
48
+ """
49
+ This method casts a string to a number, according to the mask.
50
+
51
+ """
52
+
53
+ raise NotImplementedError("How this mask should be implemented is not yet defined.")
54
+
55
+ @classmethod
56
+ def cast_string_to_duration(cls, value: Any, mask: str) -> Any:
57
+ """
58
+ This method casts a string to a duration, according to the mask.
59
+
60
+ """
61
+
62
+ raise NotImplementedError("How this mask should be implemented is not yet defined.")
63
+
64
+ @classmethod
65
+ def cast_string_to_time_period(cls, value: Any, mask: str) -> Any:
66
+ """
67
+ This method casts a string to a time period, according to the mask.
68
+
69
+ """
70
+
71
+ raise NotImplementedError("How this mask should be implemented is not yet defined.")
72
+
73
+ @classmethod
74
+ def cast_string_to_time(cls, value: Any, mask: str) -> Any:
75
+ """
76
+ This method casts a string to a time, according to the mask.
77
+
78
+ """
79
+
80
+ raise NotImplementedError("How this cast should be implemented is not yet defined.")
81
+
82
+ #
83
+ # @classmethod
84
+ # def cast_date_to_string(cls, value: Any, mask: str) -> Any:
85
+ # """ """
86
+ # return NotImplementedError("How this cast should be implemented is not yet defined.")
87
+ #
88
+ # @classmethod
89
+ # def cast_duration_to_string(cls, value: Any, mask: str) -> Any:
90
+ # """ """
91
+ # return NotImplementedError("How this cast should be implemented is not yet defined.")
92
+ #
93
+ # @classmethod
94
+ # def cast_time_to_string(cls, value: Any, mask: str) -> Any:
95
+ # """ """
96
+ # return NotImplementedError("How this cast should be implemented is not yet defined.")
97
+
98
+ @classmethod
99
+ def cast_time_period_to_date(cls, value: Any, mask_value: str) -> Any:
100
+ """ """
101
+
102
+ start = mask_value == "START"
103
+ return str_period_to_date(value, start)
104
+
105
+ invalid_mask_message = "At op {op}: Invalid mask to cast from type {type_1} to {type_2}."
106
+
107
+ @classmethod
108
+ def check_mask_value(
109
+ cls, from_type: Type[ScalarType], to_type: Type[ScalarType], mask_value: str
110
+ ) -> None:
111
+ """
112
+ This method checks if the mask value is valid for the cast operation.
113
+ """
114
+
115
+ if from_type == TimeInterval and to_type == String:
116
+ return cls.check_mask_value_from_time_to_string(mask_value)
117
+ # from = Date
118
+ if from_type == Date and to_type == String:
119
+ return cls.check_mask_value_from_date_to_string(mask_value)
120
+ # from = Time_Period
121
+ if from_type == TimePeriod and to_type == Date:
122
+ return cls.check_mask_value_from_time_period_to_date(mask_value)
123
+ # from = String
124
+ if from_type == String and to_type == Number:
125
+ return cls.check_mask_value_from_string_to_number(mask_value)
126
+ if from_type == String and to_type == TimeInterval:
127
+ return cls.check_mask_value_from_string_to_time(mask_value)
128
+ if from_type == String and to_type == Date:
129
+ return cls.check_mask_value_from_string_to_date(mask_value)
130
+ if from_type == String and to_type == TimePeriod:
131
+ return cls.check_mask_value_from_string_to_time_period(mask_value)
132
+ if from_type == String and to_type == Duration:
133
+ return cls.check_mask_value_from_string_to_duration(mask_value)
134
+ # from = Duration
135
+ if from_type == Duration and to_type == String:
136
+ return cls.check_mask_value_from_duration_to_string(mask_value)
137
+ raise SemanticError(
138
+ "1-1-5-5",
139
+ op=cls.op,
140
+ type_1=SCALAR_TYPES_CLASS_REVERSE[from_type],
141
+ type_2=SCALAR_TYPES_CLASS_REVERSE[to_type],
142
+ mask_value=mask_value,
143
+ )
144
+
145
+ @classmethod
146
+ def check_mask_value_from_time_period_to_date(cls, mask_value: str) -> None:
147
+ if mask_value not in ["START", "END"]:
148
+ raise SemanticError("1-1-5-4", op=cls.op, type_1="Time_Period", type_2="Date")
149
+
150
+ @classmethod
151
+ def check_mask_value_from_time_to_string(cls, *args: Any) -> None:
152
+ raise NotImplementedError("How this mask should be implemented is not yet defined.")
153
+
154
+ @classmethod
155
+ def check_mask_value_from_date_to_string(cls, *args: Any) -> None:
156
+ raise NotImplementedError("How this mask should be implemented is not yet defined.")
157
+
158
+ @classmethod
159
+ def check_mask_value_from_string_to_number(cls, *args: Any) -> None:
160
+ raise NotImplementedError("How this mask should be implemented is not yet defined.")
161
+
162
+ @classmethod
163
+ def check_mask_value_from_string_to_time(cls, *args: Any) -> None:
164
+ raise NotImplementedError("How this mask should be implemented is not yet defined.")
165
+
166
+ @classmethod
167
+ def check_mask_value_from_string_to_date(cls, *args: Any) -> None:
168
+ raise NotImplementedError("How this mask should be implemented is not yet defined.")
169
+
170
+ @classmethod
171
+ def check_mask_value_from_string_to_time_period(cls, *args: Any) -> None:
172
+ raise NotImplementedError("How this mask should be implemented is not yet defined.")
173
+
174
+ @classmethod
175
+ def check_mask_value_from_string_to_duration(cls, *args: Any) -> None:
176
+ raise NotImplementedError("How this mask should be implemented is not yet defined.")
177
+
178
+ @classmethod
179
+ def check_mask_value_from_duration_to_string(cls, *args: Any) -> None:
180
+ raise NotImplementedError("How this mask should be implemented is not yet defined.")
181
+
182
+ @classmethod
183
+ def check_cast(
184
+ cls,
185
+ from_type: Type[ScalarType],
186
+ to_type: Type[ScalarType],
187
+ mask_value: Optional[str],
188
+ ) -> None:
189
+ if mask_value is not None:
190
+ cls.check_with_mask(from_type, to_type, mask_value)
191
+ else:
192
+ cls.check_without_mask(from_type, to_type)
193
+
194
+ @classmethod
195
+ def check_with_mask(
196
+ cls, from_type: Type[ScalarType], to_type: Type[ScalarType], mask_value: str
197
+ ) -> None:
198
+ explicit_promotion = EXPLICIT_WITH_MASK_TYPE_PROMOTION_MAPPING[from_type]
199
+ if to_type.is_included(explicit_promotion):
200
+ return cls.check_mask_value(from_type, to_type, mask_value)
201
+
202
+ raise SemanticError(
203
+ "1-1-5-5",
204
+ op=cls.op,
205
+ type_1=SCALAR_TYPES_CLASS_REVERSE[from_type],
206
+ type_2=SCALAR_TYPES_CLASS_REVERSE[to_type],
207
+ mask_value=mask_value,
208
+ )
209
+
210
+ @classmethod
211
+ def check_without_mask(cls, from_type: Type[ScalarType], to_type: Type[ScalarType]) -> None:
212
+ explicit_promotion = EXPLICIT_WITHOUT_MASK_TYPE_PROMOTION_MAPPING[from_type]
213
+ implicit_promotion = IMPLICIT_TYPE_PROMOTION_MAPPING[from_type]
214
+ if not (to_type.is_included(explicit_promotion) or to_type.is_included(implicit_promotion)):
215
+ explicit_with_mask = EXPLICIT_WITH_MASK_TYPE_PROMOTION_MAPPING[from_type]
216
+ if to_type.is_included(explicit_with_mask):
217
+ raise SemanticError(
218
+ "1-1-5-3",
219
+ op=cls.op,
220
+ type_1=SCALAR_TYPES_CLASS_REVERSE[from_type],
221
+ type_2=SCALAR_TYPES_CLASS_REVERSE[to_type],
222
+ )
223
+ raise SemanticError(
224
+ "1-1-5-4",
225
+ op=cls.op,
226
+ type_1=SCALAR_TYPES_CLASS_REVERSE[from_type],
227
+ type_2=SCALAR_TYPES_CLASS_REVERSE[to_type],
228
+ )
229
+
230
+ @classmethod
231
+ def cast_component(
232
+ cls, data: Any, from_type: Type[ScalarType], to_type: Type[ScalarType]
233
+ ) -> Any:
234
+ """
235
+ Cast the component to the type to_type without mask
236
+ """
237
+
238
+ if to_type.is_included(IMPLICIT_TYPE_PROMOTION_MAPPING[from_type]):
239
+ result = data.map(lambda x: to_type.implicit_cast(x, from_type), na_action="ignore")
240
+ else:
241
+ result = data.map(lambda x: to_type.explicit_cast(x, from_type), na_action="ignore")
242
+ return result
243
+
244
+ @classmethod
245
+ def cast_mask_component(cls, data: Any, from_type: Any, to_type: Any, mask: str) -> Any:
246
+ result = data.map(lambda x: cls.cast_value(x, from_type, to_type, mask), na_action="ignore")
247
+ return result
248
+
249
+ @classmethod
250
+ def cast_value(
251
+ cls,
252
+ value: Any,
253
+ provided_type: Type[ScalarType],
254
+ to_type: Type[ScalarType],
255
+ mask_value: str,
256
+ ) -> Any:
257
+ if provided_type == String and to_type == Number:
258
+ return cls.cast_string_to_number(value, mask_value)
259
+ if provided_type == String and to_type == Date:
260
+ return cls.cast_string_to_date(value, mask_value)
261
+ if provided_type == String and to_type == Duration:
262
+ return cls.cast_string_to_duration(value, mask_value)
263
+ if provided_type == String and to_type == TimePeriod:
264
+ return cls.cast_string_to_time_period(value, mask_value)
265
+ if provided_type == String and to_type == TimeInterval:
266
+ return cls.cast_string_to_time(value, mask_value)
267
+ # if provided_type == Date and to_type == String:
268
+ # return cls.cast_date_to_string(value, mask_value)
269
+ # if provided_type == Duration and to_type == String:
270
+ # return cls.cast_duration_to_string(value, mask_value)
271
+ # if provided_type == TimeInterval and to_type == String:
272
+ # return cls.cast_time_to_string(value, mask_value)
273
+ if provided_type == TimePeriod and to_type == Date:
274
+ return cls.cast_time_period_to_date(value, mask_value)
275
+
276
+ raise SemanticError(
277
+ "2-1-5-1",
278
+ op=cls.op,
279
+ value=value,
280
+ type_1=SCALAR_TYPES_CLASS_REVERSE[provided_type],
281
+ type_2=SCALAR_TYPES_CLASS_REVERSE[to_type],
282
+ )
283
+
284
+ @classmethod
285
+ def validate( # type: ignore[override]
286
+ cls,
287
+ operand: ALL_MODEL_DATA_TYPES,
288
+ scalarType: Type[ScalarType],
289
+ mask: Optional[str] = None,
290
+ ) -> Any:
291
+ if mask is not None and not isinstance(mask, str):
292
+ raise Exception(f"{cls.op} mask must be a string")
293
+
294
+ if isinstance(operand, Dataset):
295
+ return cls.dataset_validation(operand, scalarType, mask)
296
+ elif isinstance(operand, DataComponent):
297
+ return cls.component_validation(operand, scalarType, mask)
298
+ elif isinstance(operand, Scalar):
299
+ return cls.scalar_validation(operand, scalarType, mask)
300
+
301
+ @classmethod
302
+ def dataset_validation( # type: ignore[override]
303
+ cls,
304
+ operand: Dataset,
305
+ to_type: Type[ScalarType],
306
+ mask: Optional[str] = None,
307
+ ) -> Dataset:
308
+ """
309
+ This method validates the operation when the operand is a Dataset.
310
+ """
311
+
312
+ # monomeasure
313
+ if len(operand.get_measures()) != 1:
314
+ raise Exception(f"{cls.op} can only be applied to a Dataset with one measure")
315
+ measure = operand.get_measures()[0]
316
+ from_type = measure.data_type
317
+
318
+ cls.check_cast(from_type, to_type, mask)
319
+ result_components = {
320
+ comp_name: copy(comp)
321
+ for comp_name, comp in operand.components.items()
322
+ if comp.role != Role.MEASURE
323
+ }
324
+
325
+ if not to_type.is_included(IMPLICIT_TYPE_PROMOTION_MAPPING[from_type]):
326
+ measure_name = COMP_NAME_MAPPING[to_type]
327
+ else:
328
+ measure_name = measure.name
329
+ result_components[measure_name] = Component(
330
+ name=measure_name,
331
+ data_type=to_type,
332
+ role=Role.MEASURE,
333
+ nullable=measure.nullable,
334
+ )
335
+ dataset_name = VirtualCounter._new_ds_name()
336
+ return Dataset(name=dataset_name, components=result_components, data=None)
337
+
338
+ @classmethod
339
+ def component_validation( # type: ignore[override]
340
+ cls,
341
+ operand: DataComponent,
342
+ to_type: Type[ScalarType],
343
+ mask: Optional[str] = None,
344
+ ) -> DataComponent:
345
+ """
346
+ This method validates the operation when the operand is a DataComponent.
347
+ """
348
+
349
+ from_type = operand.data_type
350
+ cls.check_cast(from_type, to_type, mask)
351
+ comp_name = VirtualCounter._new_dc_name()
352
+ return DataComponent(
353
+ name=comp_name,
354
+ data=None,
355
+ data_type=to_type,
356
+ role=operand.role,
357
+ nullable=operand.nullable,
358
+ )
359
+
360
+ @classmethod
361
+ def scalar_validation( # type: ignore[override]
362
+ cls,
363
+ operand: Scalar,
364
+ to_type: Type[ScalarType],
365
+ mask: Optional[str] = None,
366
+ ) -> Scalar:
367
+ """
368
+ This method validates the operation when the operand is a DataComponent.
369
+ """
370
+
371
+ from_type = operand.data_type
372
+ cls.check_cast(from_type, to_type, mask)
373
+ return Scalar(name=operand.name, data_type=to_type, value=None)
374
+
375
+ @classmethod
376
+ def evaluate( # type: ignore[override]
377
+ cls,
378
+ operand: ALL_MODEL_DATA_TYPES,
379
+ scalarType: Type[ScalarType],
380
+ mask: Optional[str] = None,
381
+ ) -> Any:
382
+ if isinstance(operand, Dataset):
383
+ return cls.dataset_evaluation(operand, scalarType, mask)
384
+ if isinstance(operand, Scalar):
385
+ return cls.scalar_evaluation(operand, scalarType, mask)
386
+ if isinstance(operand, DataComponent):
387
+ return cls.component_evaluation(operand, scalarType, mask)
388
+
389
+ @classmethod
390
+ def dataset_evaluation( # type: ignore[override]
391
+ cls,
392
+ operand: Dataset,
393
+ to_type: Type[ScalarType],
394
+ mask: Optional[str] = None,
395
+ ) -> Dataset:
396
+ from_type = operand.get_measures()[0].data_type
397
+ original_measure = operand.get_measures()[0]
398
+ result_dataset = cls.dataset_validation(operand, to_type, mask)
399
+ new_measure = result_dataset.get_measures()[0]
400
+ result_dataset.data = operand.data.copy() if operand.data is not None else pd.DataFrame()
401
+
402
+ if original_measure.name != new_measure.name:
403
+ result_dataset.data.rename(
404
+ columns={original_measure.name: new_measure.name}, inplace=True
405
+ )
406
+ measure_data = result_dataset.data[new_measure.name]
407
+ if mask:
408
+ result_dataset.data[new_measure.name] = cls.cast_mask_component(
409
+ measure_data, from_type, to_type, mask
410
+ )
411
+ else:
412
+ result_dataset.data[new_measure.name] = cls.cast_component(
413
+ measure_data, from_type, to_type
414
+ )
415
+ return result_dataset
416
+
417
+ @classmethod
418
+ def scalar_evaluation( # type: ignore[override]
419
+ cls,
420
+ operand: Scalar,
421
+ to_type: Type[ScalarType],
422
+ mask: Optional[str] = None,
423
+ ) -> Scalar:
424
+ from_type = operand.data_type
425
+ result_scalar = cls.scalar_validation(operand, to_type, mask)
426
+ if pd.isna(operand.value):
427
+ return Scalar(name=result_scalar.name, data_type=to_type, value=None)
428
+ if mask:
429
+ casted_data = cls.cast_value(operand.value, operand.data_type, to_type, mask)
430
+ else:
431
+ if to_type.is_included(IMPLICIT_TYPE_PROMOTION_MAPPING[from_type]):
432
+ casted_data = to_type.implicit_cast(operand.value, from_type)
433
+ else:
434
+ casted_data = to_type.explicit_cast(operand.value, from_type)
435
+ return Scalar(name=result_scalar.name, data_type=to_type, value=casted_data)
436
+
437
+ @classmethod
438
+ def component_evaluation( # type: ignore[override]
439
+ cls,
440
+ operand: DataComponent,
441
+ to_type: Type[ScalarType],
442
+ mask: Optional[str] = None,
443
+ ) -> DataComponent:
444
+ from_type = operand.data_type
445
+ result_component = cls.component_validation(operand, to_type, mask)
446
+ if mask:
447
+ casted_data = cls.cast_mask_component(operand.data, from_type, to_type, mask)
448
+ else:
449
+ casted_data = cls.cast_component(operand.data, from_type, to_type)
450
+ result_component.data = casted_data
451
+ return result_component