vtlengine 1.0.3rc3__py3-none-any.whl → 1.0.4__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 +55 -20
- vtlengine/API/__init__.py +11 -2
- vtlengine/API/data/schema/json_schema_2.1.json +116 -0
- vtlengine/AST/ASTConstructor.py +5 -4
- vtlengine/AST/ASTConstructorModules/Expr.py +47 -48
- vtlengine/AST/ASTConstructorModules/ExprComponents.py +45 -23
- vtlengine/AST/ASTConstructorModules/Terminals.py +21 -11
- vtlengine/AST/ASTEncoders.py +1 -1
- vtlengine/AST/DAG/__init__.py +0 -3
- vtlengine/AST/Grammar/lexer.py +0 -1
- vtlengine/AST/Grammar/parser.py +185 -440
- vtlengine/AST/VtlVisitor.py +0 -1
- vtlengine/DataTypes/TimeHandling.py +50 -15
- vtlengine/DataTypes/__init__.py +79 -7
- vtlengine/Exceptions/__init__.py +3 -5
- vtlengine/Exceptions/messages.py +65 -105
- vtlengine/Interpreter/__init__.py +83 -38
- vtlengine/Model/__init__.py +7 -9
- vtlengine/Operators/Aggregation.py +13 -7
- vtlengine/Operators/Analytic.py +48 -9
- vtlengine/Operators/Assignment.py +0 -1
- vtlengine/Operators/CastOperator.py +44 -44
- vtlengine/Operators/Clause.py +16 -10
- vtlengine/Operators/Comparison.py +20 -12
- vtlengine/Operators/Conditional.py +30 -13
- vtlengine/Operators/General.py +9 -4
- vtlengine/Operators/HROperators.py +4 -14
- vtlengine/Operators/Join.py +15 -14
- vtlengine/Operators/Numeric.py +32 -26
- vtlengine/Operators/RoleSetter.py +6 -2
- vtlengine/Operators/Set.py +12 -8
- vtlengine/Operators/String.py +9 -9
- vtlengine/Operators/Time.py +136 -116
- vtlengine/Operators/Validation.py +10 -4
- vtlengine/Operators/__init__.py +56 -69
- vtlengine/Utils/__init__.py +6 -1
- vtlengine/files/output/__init__.py +0 -1
- vtlengine/files/output/_time_period_representation.py +2 -1
- vtlengine/files/parser/__init__.py +44 -10
- vtlengine/files/parser/_rfc_dialect.py +1 -1
- vtlengine/files/parser/_time_checking.py +4 -4
- {vtlengine-1.0.3rc3.dist-info → vtlengine-1.0.4.dist-info}/METADATA +7 -6
- vtlengine-1.0.4.dist-info/RECORD +58 -0
- {vtlengine-1.0.3rc3.dist-info → vtlengine-1.0.4.dist-info}/WHEEL +1 -1
- vtlengine/DataTypes/NumericTypesHandling.py +0 -38
- vtlengine-1.0.3rc3.dist-info/RECORD +0 -58
- {vtlengine-1.0.3rc3.dist-info → vtlengine-1.0.4.dist-info}/LICENSE.md +0 -0
|
@@ -34,43 +34,43 @@ class Cast(Operator.Unary):
|
|
|
34
34
|
# CASTS VALUES
|
|
35
35
|
# Converts the value from one type to another in a way that is according to the mask
|
|
36
36
|
@classmethod
|
|
37
|
-
def cast_string_to_number(cls,
|
|
37
|
+
def cast_string_to_number(cls, value: Any, mask: str) -> Any:
|
|
38
38
|
"""
|
|
39
39
|
This method casts a string to a number, according to the mask.
|
|
40
40
|
|
|
41
41
|
"""
|
|
42
42
|
|
|
43
|
-
raise NotImplementedError("How this
|
|
43
|
+
raise NotImplementedError("How this mask should be implemented is not yet defined.")
|
|
44
44
|
|
|
45
45
|
@classmethod
|
|
46
|
-
def cast_string_to_date(cls,
|
|
46
|
+
def cast_string_to_date(cls, value: Any, mask: str) -> Any:
|
|
47
47
|
"""
|
|
48
48
|
This method casts a string to a number, according to the mask.
|
|
49
49
|
|
|
50
50
|
"""
|
|
51
51
|
|
|
52
|
-
raise NotImplementedError("How this
|
|
52
|
+
raise NotImplementedError("How this mask should be implemented is not yet defined.")
|
|
53
53
|
|
|
54
54
|
@classmethod
|
|
55
|
-
def cast_string_to_duration(cls,
|
|
55
|
+
def cast_string_to_duration(cls, value: Any, mask: str) -> Any:
|
|
56
56
|
"""
|
|
57
57
|
This method casts a string to a duration, according to the mask.
|
|
58
58
|
|
|
59
59
|
"""
|
|
60
60
|
|
|
61
|
-
raise NotImplementedError("How this
|
|
61
|
+
raise NotImplementedError("How this mask should be implemented is not yet defined.")
|
|
62
62
|
|
|
63
63
|
@classmethod
|
|
64
|
-
def cast_string_to_time_period(cls,
|
|
64
|
+
def cast_string_to_time_period(cls, value: Any, mask: str) -> Any:
|
|
65
65
|
"""
|
|
66
66
|
This method casts a string to a time period, according to the mask.
|
|
67
67
|
|
|
68
68
|
"""
|
|
69
69
|
|
|
70
|
-
raise NotImplementedError("How this
|
|
70
|
+
raise NotImplementedError("How this mask should be implemented is not yet defined.")
|
|
71
71
|
|
|
72
72
|
@classmethod
|
|
73
|
-
def cast_string_to_time(cls,
|
|
73
|
+
def cast_string_to_time(cls, value: Any, mask: str) -> Any:
|
|
74
74
|
"""
|
|
75
75
|
This method casts a string to a time, according to the mask.
|
|
76
76
|
|
|
@@ -78,20 +78,21 @@ class Cast(Operator.Unary):
|
|
|
78
78
|
|
|
79
79
|
raise NotImplementedError("How this cast should be implemented is not yet defined.")
|
|
80
80
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
81
|
+
#
|
|
82
|
+
# @classmethod
|
|
83
|
+
# def cast_date_to_string(cls, value: Any, mask: str) -> Any:
|
|
84
|
+
# """ """
|
|
85
|
+
# return NotImplementedError("How this cast should be implemented is not yet defined.")
|
|
86
|
+
#
|
|
87
|
+
# @classmethod
|
|
88
|
+
# def cast_duration_to_string(cls, value: Any, mask: str) -> Any:
|
|
89
|
+
# """ """
|
|
90
|
+
# return NotImplementedError("How this cast should be implemented is not yet defined.")
|
|
91
|
+
#
|
|
92
|
+
# @classmethod
|
|
93
|
+
# def cast_time_to_string(cls, value: Any, mask: str) -> Any:
|
|
94
|
+
# """ """
|
|
95
|
+
# return NotImplementedError("How this cast should be implemented is not yet defined.")
|
|
95
96
|
|
|
96
97
|
@classmethod
|
|
97
98
|
def cast_time_period_to_date(cls, value: Any, mask_value: str) -> Any:
|
|
@@ -142,7 +143,6 @@ class Cast(Operator.Unary):
|
|
|
142
143
|
|
|
143
144
|
@classmethod
|
|
144
145
|
def check_mask_value_from_time_period_to_date(cls, mask_value: str) -> None:
|
|
145
|
-
|
|
146
146
|
if mask_value not in ["START", "END"]:
|
|
147
147
|
raise SemanticError("1-1-5-4", op=cls.op, type_1="Time_Period", type_2="Date")
|
|
148
148
|
|
|
@@ -180,9 +180,11 @@ class Cast(Operator.Unary):
|
|
|
180
180
|
|
|
181
181
|
@classmethod
|
|
182
182
|
def check_cast(
|
|
183
|
-
cls,
|
|
183
|
+
cls,
|
|
184
|
+
from_type: Type[ScalarType],
|
|
185
|
+
to_type: Type[ScalarType],
|
|
186
|
+
mask_value: Optional[str],
|
|
184
187
|
) -> None:
|
|
185
|
-
|
|
186
188
|
if mask_value is not None:
|
|
187
189
|
cls.check_with_mask(from_type, to_type, mask_value)
|
|
188
190
|
else:
|
|
@@ -192,7 +194,6 @@ class Cast(Operator.Unary):
|
|
|
192
194
|
def check_with_mask(
|
|
193
195
|
cls, from_type: Type[ScalarType], to_type: Type[ScalarType], mask_value: str
|
|
194
196
|
) -> None:
|
|
195
|
-
|
|
196
197
|
explicit_promotion = EXPLICIT_WITH_MASK_TYPE_PROMOTION_MAPPING[from_type]
|
|
197
198
|
if to_type.is_included(explicit_promotion):
|
|
198
199
|
return cls.check_mask_value(from_type, to_type, mask_value)
|
|
@@ -207,7 +208,6 @@ class Cast(Operator.Unary):
|
|
|
207
208
|
|
|
208
209
|
@classmethod
|
|
209
210
|
def check_without_mask(cls, from_type: Type[ScalarType], to_type: Type[ScalarType]) -> None:
|
|
210
|
-
|
|
211
211
|
explicit_promotion = EXPLICIT_WITHOUT_MASK_TYPE_PROMOTION_MAPPING[from_type]
|
|
212
212
|
implicit_promotion = IMPLICIT_TYPE_PROMOTION_MAPPING[from_type]
|
|
213
213
|
if not (to_type.is_included(explicit_promotion) or to_type.is_included(implicit_promotion)):
|
|
@@ -231,7 +231,7 @@ class Cast(Operator.Unary):
|
|
|
231
231
|
cls, data: Any, from_type: Type[ScalarType], to_type: Type[ScalarType]
|
|
232
232
|
) -> Any:
|
|
233
233
|
"""
|
|
234
|
-
|
|
234
|
+
Cast the component to the type to_type without mask
|
|
235
235
|
"""
|
|
236
236
|
|
|
237
237
|
if to_type.is_included(IMPLICIT_TYPE_PROMOTION_MAPPING[from_type]):
|
|
@@ -242,15 +242,17 @@ class Cast(Operator.Unary):
|
|
|
242
242
|
|
|
243
243
|
@classmethod
|
|
244
244
|
def cast_mask_component(cls, data: Any, from_type: Any, to_type: Any, mask: str) -> Any:
|
|
245
|
-
|
|
246
245
|
result = data.map(lambda x: cls.cast_value(x, from_type, to_type, mask), na_action="ignore")
|
|
247
246
|
return result
|
|
248
247
|
|
|
249
248
|
@classmethod
|
|
250
249
|
def cast_value(
|
|
251
|
-
cls,
|
|
250
|
+
cls,
|
|
251
|
+
value: Any,
|
|
252
|
+
provided_type: Type[ScalarType],
|
|
253
|
+
to_type: Type[ScalarType],
|
|
254
|
+
mask_value: str,
|
|
252
255
|
) -> Any:
|
|
253
|
-
|
|
254
256
|
if provided_type == String and to_type == Number:
|
|
255
257
|
return cls.cast_string_to_number(value, mask_value)
|
|
256
258
|
if provided_type == String and to_type == Date:
|
|
@@ -261,12 +263,12 @@ class Cast(Operator.Unary):
|
|
|
261
263
|
return cls.cast_string_to_time_period(value, mask_value)
|
|
262
264
|
if provided_type == String and to_type == TimeInterval:
|
|
263
265
|
return cls.cast_string_to_time(value, mask_value)
|
|
264
|
-
if provided_type == Date and to_type == String:
|
|
265
|
-
|
|
266
|
-
if provided_type == Duration and to_type == String:
|
|
267
|
-
|
|
268
|
-
if provided_type == TimeInterval and to_type == String:
|
|
269
|
-
|
|
266
|
+
# if provided_type == Date and to_type == String:
|
|
267
|
+
# return cls.cast_date_to_string(value, mask_value)
|
|
268
|
+
# if provided_type == Duration and to_type == String:
|
|
269
|
+
# return cls.cast_duration_to_string(value, mask_value)
|
|
270
|
+
# if provided_type == TimeInterval and to_type == String:
|
|
271
|
+
# return cls.cast_time_to_string(value, mask_value)
|
|
270
272
|
if provided_type == TimePeriod and to_type == Date:
|
|
271
273
|
return cls.cast_time_period_to_date(value, mask_value)
|
|
272
274
|
|
|
@@ -285,7 +287,6 @@ class Cast(Operator.Unary):
|
|
|
285
287
|
scalarType: Type[ScalarType],
|
|
286
288
|
mask: Optional[str] = None,
|
|
287
289
|
) -> Any:
|
|
288
|
-
|
|
289
290
|
if mask is not None and not isinstance(mask, str):
|
|
290
291
|
raise Exception(f"{cls.op} mask must be a string")
|
|
291
292
|
|
|
@@ -325,7 +326,10 @@ class Cast(Operator.Unary):
|
|
|
325
326
|
else:
|
|
326
327
|
measure_name = measure.name
|
|
327
328
|
result_components[measure_name] = Component(
|
|
328
|
-
name=measure_name,
|
|
329
|
+
name=measure_name,
|
|
330
|
+
data_type=to_type,
|
|
331
|
+
role=Role.MEASURE,
|
|
332
|
+
nullable=measure.nullable,
|
|
329
333
|
)
|
|
330
334
|
return Dataset(name="result", components=result_components, data=None)
|
|
331
335
|
|
|
@@ -366,7 +370,6 @@ class Cast(Operator.Unary):
|
|
|
366
370
|
scalarType: Type[ScalarType],
|
|
367
371
|
mask: Optional[str] = None,
|
|
368
372
|
) -> Any:
|
|
369
|
-
|
|
370
373
|
if isinstance(operand, Dataset):
|
|
371
374
|
return cls.dataset_evaluation(operand, scalarType, mask)
|
|
372
375
|
if isinstance(operand, Scalar):
|
|
@@ -381,7 +384,6 @@ class Cast(Operator.Unary):
|
|
|
381
384
|
to_type: Type[ScalarType],
|
|
382
385
|
mask: Optional[str] = None,
|
|
383
386
|
) -> Dataset:
|
|
384
|
-
|
|
385
387
|
from_type = operand.get_measures()[0].data_type
|
|
386
388
|
original_measure = operand.get_measures()[0]
|
|
387
389
|
result_dataset = cls.dataset_validation(operand, to_type, mask)
|
|
@@ -410,7 +412,6 @@ class Cast(Operator.Unary):
|
|
|
410
412
|
to_type: Type[ScalarType],
|
|
411
413
|
mask: Optional[str] = None,
|
|
412
414
|
) -> Scalar:
|
|
413
|
-
|
|
414
415
|
from_type = operand.data_type
|
|
415
416
|
result_scalar = cls.scalar_validation(operand, to_type, mask)
|
|
416
417
|
if pd.isna(operand.value):
|
|
@@ -431,7 +432,6 @@ class Cast(Operator.Unary):
|
|
|
431
432
|
to_type: Type[ScalarType],
|
|
432
433
|
mask: Optional[str] = None,
|
|
433
434
|
) -> DataComponent:
|
|
434
|
-
|
|
435
435
|
from_type = operand.data_type
|
|
436
436
|
result_component = cls.component_validation(operand, to_type, mask)
|
|
437
437
|
if mask:
|
vtlengine/Operators/Clause.py
CHANGED
|
@@ -22,12 +22,10 @@ class Calc(Operator):
|
|
|
22
22
|
|
|
23
23
|
@classmethod
|
|
24
24
|
def validate(cls, operands: List[Union[DataComponent, Scalar]], dataset: Dataset) -> Dataset:
|
|
25
|
-
|
|
26
25
|
result_components = {name: copy(comp) for name, comp in dataset.components.items()}
|
|
27
26
|
result_dataset = Dataset(name=dataset.name, components=result_components, data=None)
|
|
28
27
|
|
|
29
28
|
for operand in operands:
|
|
30
|
-
|
|
31
29
|
if operand.name in result_dataset.components:
|
|
32
30
|
if result_dataset.components[operand.name].role == Role.IDENTIFIER:
|
|
33
31
|
raise SemanticError("1-1-6-13", op=cls.op, comp_name=operand.name)
|
|
@@ -72,7 +70,6 @@ class Aggregate(Operator):
|
|
|
72
70
|
|
|
73
71
|
@classmethod
|
|
74
72
|
def validate(cls, operands: List[Union[DataComponent, Scalar]], dataset: Dataset) -> Dataset:
|
|
75
|
-
|
|
76
73
|
result_dataset = Dataset(name=dataset.name, components=dataset.components, data=None)
|
|
77
74
|
|
|
78
75
|
for operand in operands:
|
|
@@ -121,7 +118,6 @@ class Aggregate(Operator):
|
|
|
121
118
|
|
|
122
119
|
|
|
123
120
|
class Filter(Operator):
|
|
124
|
-
|
|
125
121
|
@classmethod
|
|
126
122
|
def validate(cls, condition: DataComponent, dataset: Dataset) -> Dataset:
|
|
127
123
|
if condition.data_type != Boolean:
|
|
@@ -212,11 +208,17 @@ class Rename(Operator):
|
|
|
212
208
|
for operand in operands:
|
|
213
209
|
if operand.old_name not in dataset.components:
|
|
214
210
|
raise SemanticError(
|
|
215
|
-
"1-1-1-10",
|
|
211
|
+
"1-1-1-10",
|
|
212
|
+
op=cls.op,
|
|
213
|
+
comp_name=operand.old_name,
|
|
214
|
+
dataset_name=dataset.name,
|
|
216
215
|
)
|
|
217
216
|
if operand.new_name in dataset.components:
|
|
218
217
|
raise SemanticError(
|
|
219
|
-
"1-1-6-8",
|
|
218
|
+
"1-1-6-8",
|
|
219
|
+
op=cls.op,
|
|
220
|
+
comp_name=operand.new_name,
|
|
221
|
+
dataset_name=dataset.name,
|
|
220
222
|
)
|
|
221
223
|
|
|
222
224
|
result_components = {comp.name: comp for comp in dataset.components.values()}
|
|
@@ -242,7 +244,6 @@ class Rename(Operator):
|
|
|
242
244
|
|
|
243
245
|
|
|
244
246
|
class Pivot(Operator):
|
|
245
|
-
|
|
246
247
|
@classmethod
|
|
247
248
|
def validate(cls, operands: List[str], dataset: Dataset) -> Dataset:
|
|
248
249
|
raise NotImplementedError
|
|
@@ -253,7 +254,6 @@ class Pivot(Operator):
|
|
|
253
254
|
|
|
254
255
|
|
|
255
256
|
class Unpivot(Operator):
|
|
256
|
-
|
|
257
257
|
@classmethod
|
|
258
258
|
def validate(cls, operands: List[str], dataset: Dataset) -> Dataset:
|
|
259
259
|
if len(operands) != 2:
|
|
@@ -311,11 +311,17 @@ class Sub(Operator):
|
|
|
311
311
|
for operand in operands:
|
|
312
312
|
if operand.name not in dataset.components:
|
|
313
313
|
raise SemanticError(
|
|
314
|
-
"1-1-1-10",
|
|
314
|
+
"1-1-1-10",
|
|
315
|
+
op=cls.op,
|
|
316
|
+
comp_name=operand.name,
|
|
317
|
+
dataset_name=dataset.name,
|
|
315
318
|
)
|
|
316
319
|
if operand.role != Role.IDENTIFIER:
|
|
317
320
|
raise SemanticError(
|
|
318
|
-
"1-1-6-10",
|
|
321
|
+
"1-1-6-10",
|
|
322
|
+
op=cls.op,
|
|
323
|
+
operand=operand.name,
|
|
324
|
+
dataset_name=dataset.name,
|
|
319
325
|
)
|
|
320
326
|
if isinstance(operand, Scalar):
|
|
321
327
|
raise SemanticError("1-1-6-5", op=cls.op, name=operand.name)
|
|
@@ -74,10 +74,11 @@ class Binary(Operator.Binary):
|
|
|
74
74
|
return_type = Boolean
|
|
75
75
|
|
|
76
76
|
@classmethod
|
|
77
|
-
def _cast_values(
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
77
|
+
def _cast_values(
|
|
78
|
+
cls,
|
|
79
|
+
x: Optional[Union[int, float, str, bool]],
|
|
80
|
+
y: Optional[Union[int, float, str, bool]],
|
|
81
|
+
) -> Any:
|
|
81
82
|
# Cast values to compatible types for comparison
|
|
82
83
|
try:
|
|
83
84
|
if isinstance(x, str) and isinstance(y, bool):
|
|
@@ -247,9 +248,7 @@ class Between(Operator.Operator):
|
|
|
247
248
|
z: Optional[Union[int, float, bool, str]],
|
|
248
249
|
) -> Optional[bool]:
|
|
249
250
|
return (
|
|
250
|
-
None
|
|
251
|
-
if (pd.isnull(x) or pd.isnull(y) or pd.isnull(z))
|
|
252
|
-
else y <= x <= z # type: ignore[operator]
|
|
251
|
+
None if (pd.isnull(x) or pd.isnull(y) or pd.isnull(z)) else y <= x <= z # type: ignore[operator]
|
|
253
252
|
)
|
|
254
253
|
|
|
255
254
|
@classmethod
|
|
@@ -264,7 +263,8 @@ class Between(Operator.Operator):
|
|
|
264
263
|
to_data = pd.Series(to_data, index=series.index)
|
|
265
264
|
df = pd.DataFrame({"operand": series, "from_data": from_data, "to_data": to_data})
|
|
266
265
|
return df.apply(
|
|
267
|
-
lambda x: cls.op_func(x["operand"], x["from_data"], x["to_data"]),
|
|
266
|
+
lambda x: cls.op_func(x["operand"], x["from_data"], x["to_data"]),
|
|
267
|
+
axis=1,
|
|
268
268
|
)
|
|
269
269
|
|
|
270
270
|
return series.map(lambda x: cls.op_func(x, from_data, to_data))
|
|
@@ -310,13 +310,19 @@ class Between(Operator.Operator):
|
|
|
310
310
|
result = Dataset(name=operand.name, components=result_components, data=None)
|
|
311
311
|
elif isinstance(operand, DataComponent):
|
|
312
312
|
result = DataComponent(
|
|
313
|
-
name=operand.name,
|
|
313
|
+
name=operand.name,
|
|
314
|
+
data=None,
|
|
315
|
+
data_type=cls.return_type,
|
|
316
|
+
role=operand.role,
|
|
314
317
|
)
|
|
315
318
|
elif isinstance(from_, Scalar) and isinstance(to, Scalar):
|
|
316
319
|
result = Scalar(name=operand.name, value=None, data_type=cls.return_type)
|
|
317
320
|
else: # From or To is a DataComponent, or both
|
|
318
321
|
result = DataComponent(
|
|
319
|
-
name=operand.name,
|
|
322
|
+
name=operand.name,
|
|
323
|
+
data=None,
|
|
324
|
+
data_type=cls.return_type,
|
|
325
|
+
role=Role.MEASURE,
|
|
320
326
|
)
|
|
321
327
|
|
|
322
328
|
if isinstance(operand, Dataset):
|
|
@@ -369,14 +375,16 @@ class Between(Operator.Operator):
|
|
|
369
375
|
elif isinstance(operand, Scalar) and (
|
|
370
376
|
isinstance(from_data, pd.Series) or isinstance(to_data, pd.Series)
|
|
371
377
|
): # From or To is a DataComponent, or both
|
|
372
|
-
|
|
373
378
|
if isinstance(from_data, pd.Series):
|
|
374
379
|
series = pd.Series(operand.value, index=from_data.index, dtype=object)
|
|
375
380
|
elif isinstance(to_data, pd.Series):
|
|
376
381
|
series = pd.Series(operand.value, index=to_data.index, dtype=object)
|
|
377
382
|
result_series = cls.apply_operation_component(series, from_data, to_data)
|
|
378
383
|
result = DataComponent(
|
|
379
|
-
name=operand.name,
|
|
384
|
+
name=operand.name,
|
|
385
|
+
data=result_series,
|
|
386
|
+
data_type=cls.return_type,
|
|
387
|
+
role=Role.MEASURE,
|
|
380
388
|
)
|
|
381
389
|
return result
|
|
382
390
|
|
|
@@ -39,7 +39,7 @@ class If(Operator):
|
|
|
39
39
|
validate: Class method that has two branches so datacomponent and datasets can be validated. With datacomponent,
|
|
40
40
|
the code reviews if it is actually a Measure and if it is a binary operation. Dataset branch reviews if the
|
|
41
41
|
identifiers are the same in 'if', 'then' and 'else'.
|
|
42
|
-
"""
|
|
42
|
+
""" # noqa E501
|
|
43
43
|
|
|
44
44
|
@classmethod
|
|
45
45
|
def evaluate(cls, condition: Any, true_branch: Any, false_branch: Any) -> Any:
|
|
@@ -66,7 +66,8 @@ class If(Operator):
|
|
|
66
66
|
else:
|
|
67
67
|
false_data = false_branch.data.reindex(condition.data.index)
|
|
68
68
|
result = np.where(condition.data, true_data, false_data)
|
|
69
|
-
|
|
69
|
+
|
|
70
|
+
return pd.Series(result, index=condition.data.index) # type: ignore[union-attr]
|
|
70
71
|
|
|
71
72
|
@classmethod
|
|
72
73
|
def dataset_level_evaluation(
|
|
@@ -80,7 +81,11 @@ class If(Operator):
|
|
|
80
81
|
if isinstance(true_branch, Dataset):
|
|
81
82
|
if len(true_data) > 0 and true_branch.data is not None:
|
|
82
83
|
true_data = pd.merge(
|
|
83
|
-
true_data,
|
|
84
|
+
true_data,
|
|
85
|
+
true_branch.data,
|
|
86
|
+
on=ids,
|
|
87
|
+
how="right",
|
|
88
|
+
suffixes=("_condition", ""),
|
|
84
89
|
)
|
|
85
90
|
else:
|
|
86
91
|
true_data = pd.DataFrame(columns=true_branch.get_components_names())
|
|
@@ -91,7 +96,11 @@ class If(Operator):
|
|
|
91
96
|
if isinstance(false_branch, Dataset):
|
|
92
97
|
if len(false_data) > 0 and false_branch.data is not None:
|
|
93
98
|
false_data = pd.merge(
|
|
94
|
-
false_data,
|
|
99
|
+
false_data,
|
|
100
|
+
false_branch.data,
|
|
101
|
+
on=ids,
|
|
102
|
+
how="right",
|
|
103
|
+
suffixes=("_condition", ""),
|
|
95
104
|
)
|
|
96
105
|
else:
|
|
97
106
|
false_data = pd.DataFrame(columns=false_branch.get_components_names())
|
|
@@ -141,7 +150,9 @@ class If(Operator):
|
|
|
141
150
|
if isinstance(condition, DataComponent):
|
|
142
151
|
if not condition.data_type == Boolean:
|
|
143
152
|
raise SemanticError(
|
|
144
|
-
"1-1-9-11",
|
|
153
|
+
"1-1-9-11",
|
|
154
|
+
op=cls.op,
|
|
155
|
+
type=SCALAR_TYPES_CLASS_REVERSE[condition.data_type],
|
|
145
156
|
)
|
|
146
157
|
if not isinstance(left, Scalar) or not isinstance(right, Scalar):
|
|
147
158
|
nullable = condition.nullable
|
|
@@ -191,7 +202,8 @@ class If(Operator):
|
|
|
191
202
|
if component.data_type != right.components[component.name].data_type:
|
|
192
203
|
component.data_type = right.components[component.name].data_type = (
|
|
193
204
|
binary_implicit_promotion(
|
|
194
|
-
component.data_type,
|
|
205
|
+
component.data_type,
|
|
206
|
+
right.components[component.name].data_type,
|
|
195
207
|
)
|
|
196
208
|
)
|
|
197
209
|
if isinstance(condition, Dataset):
|
|
@@ -219,7 +231,7 @@ class Nvl(Binary):
|
|
|
219
231
|
Validate: Class method that validates if the operation at scalar,
|
|
220
232
|
datacomponent or dataset level can be performed.
|
|
221
233
|
Evaluate: Evaluates the actual operation, returning the result.
|
|
222
|
-
"""
|
|
234
|
+
""" # noqa E501
|
|
223
235
|
|
|
224
236
|
@classmethod
|
|
225
237
|
def evaluate(cls, left: Any, right: Any) -> Union[Scalar, DataComponent, Dataset]:
|
|
@@ -290,13 +302,16 @@ class Nvl(Binary):
|
|
|
290
302
|
|
|
291
303
|
|
|
292
304
|
class Case(Operator):
|
|
293
|
-
|
|
294
305
|
@classmethod
|
|
295
306
|
def evaluate(
|
|
296
307
|
cls, conditions: List[Any], thenOps: List[Any], elseOp: Any
|
|
297
308
|
) -> Union[Scalar, DataComponent, Dataset]:
|
|
298
|
-
|
|
299
309
|
result = cls.validate(conditions, thenOps, elseOp)
|
|
310
|
+
for condition in conditions:
|
|
311
|
+
if isinstance(condition, (DataComponent, Dataset)) and condition.data is not None:
|
|
312
|
+
condition.data.fillna(False, inplace=True)
|
|
313
|
+
elif isinstance(condition, Scalar) and condition.value is None:
|
|
314
|
+
condition.value = False
|
|
300
315
|
|
|
301
316
|
if isinstance(result, Scalar):
|
|
302
317
|
result.value = elseOp.value
|
|
@@ -309,8 +324,10 @@ class Case(Operator):
|
|
|
309
324
|
|
|
310
325
|
for i, condition in enumerate(conditions):
|
|
311
326
|
value = thenOps[i].value if isinstance(thenOps[i], Scalar) else thenOps[i].data
|
|
312
|
-
result.data = np.where(
|
|
313
|
-
condition.data,
|
|
327
|
+
result.data = np.where( # type: ignore[call-overload]
|
|
328
|
+
condition.data.notna(),
|
|
329
|
+
np.where(condition.data, value, result.data), # type: ignore[call-overload]
|
|
330
|
+
result.data,
|
|
314
331
|
)
|
|
315
332
|
|
|
316
333
|
condition_mask_else = ~np.any([condition.data for condition in conditions], axis=0)
|
|
@@ -361,7 +378,6 @@ class Case(Operator):
|
|
|
361
378
|
def validate(
|
|
362
379
|
cls, conditions: List[Any], thenOps: List[Any], elseOp: Any
|
|
363
380
|
) -> Union[Scalar, DataComponent, Dataset]:
|
|
364
|
-
|
|
365
381
|
if len(set(map(type, conditions))) > 1:
|
|
366
382
|
raise SemanticError("2-1-9-1", op=cls.op)
|
|
367
383
|
|
|
@@ -395,9 +411,10 @@ class Case(Operator):
|
|
|
395
411
|
raise SemanticError("2-1-9-4", op=cls.op, name=condition.name)
|
|
396
412
|
|
|
397
413
|
nullable = any(
|
|
398
|
-
thenOp.nullable if isinstance(thenOp, DataComponent) else thenOp.data_type == Null
|
|
414
|
+
(thenOp.nullable if isinstance(thenOp, DataComponent) else thenOp.data_type == Null)
|
|
399
415
|
for thenOp in ops
|
|
400
416
|
)
|
|
417
|
+
nullable |= any(condition.nullable for condition in conditions)
|
|
401
418
|
|
|
402
419
|
data_type = ops[0].data_type
|
|
403
420
|
for op in ops[1:]:
|
vtlengine/Operators/General.py
CHANGED
|
@@ -23,7 +23,10 @@ class Membership(Binary):
|
|
|
23
23
|
def validate(cls, left_operand: Any, right_operand: Any) -> Dataset:
|
|
24
24
|
if right_operand not in left_operand.components:
|
|
25
25
|
raise SemanticError(
|
|
26
|
-
"1-1-1-10",
|
|
26
|
+
"1-1-1-10",
|
|
27
|
+
op=cls.op,
|
|
28
|
+
comp_name=right_operand,
|
|
29
|
+
dataset_name=left_operand.name,
|
|
27
30
|
)
|
|
28
31
|
|
|
29
32
|
component = left_operand.components[right_operand]
|
|
@@ -48,7 +51,10 @@ class Membership(Binary):
|
|
|
48
51
|
|
|
49
52
|
@classmethod
|
|
50
53
|
def evaluate(
|
|
51
|
-
cls,
|
|
54
|
+
cls,
|
|
55
|
+
left_operand: Dataset,
|
|
56
|
+
right_operand: str,
|
|
57
|
+
is_from_component_assignment: bool = False,
|
|
52
58
|
) -> Union[DataComponent, Dataset]:
|
|
53
59
|
result_dataset = cls.validate(left_operand, right_operand)
|
|
54
60
|
if left_operand.data is not None:
|
|
@@ -128,12 +134,11 @@ class Eval(Unary):
|
|
|
128
134
|
external_routine: ExternalRoutine,
|
|
129
135
|
output: Dataset,
|
|
130
136
|
) -> Dataset:
|
|
131
|
-
|
|
132
137
|
empty_data_dict = {}
|
|
133
138
|
for ds_name in external_routine.dataset_names:
|
|
134
139
|
if ds_name not in operands:
|
|
135
140
|
raise ValueError(
|
|
136
|
-
f"External Routine dataset {ds_name}
|
|
141
|
+
f"External Routine dataset {ds_name} is not present in Eval operands"
|
|
137
142
|
)
|
|
138
143
|
empty_data = pd.DataFrame(
|
|
139
144
|
columns=[comp.name for comp in operands[ds_name].components.values()]
|
|
@@ -24,12 +24,9 @@ def get_measure_from_dataset(dataset: Dataset, code_item: str) -> DataComponent:
|
|
|
24
24
|
|
|
25
25
|
|
|
26
26
|
class HRComparison(Operators.Binary):
|
|
27
|
-
|
|
28
27
|
@classmethod
|
|
29
28
|
def imbalance_func(cls, x: Any, y: Any) -> Any:
|
|
30
|
-
if pd.isnull(x) or pd.isnull(y)
|
|
31
|
-
return None
|
|
32
|
-
return x - y
|
|
29
|
+
return None if pd.isnull(x) or pd.isnull(y) else x - y
|
|
33
30
|
|
|
34
31
|
@staticmethod
|
|
35
32
|
def hr_func(left_series: Any, right_series: Any, hr_mode: str) -> Any:
|
|
@@ -44,8 +41,8 @@ class HRComparison(Operators.Binary):
|
|
|
44
41
|
result[mask_remove] = "REMOVE_VALUE"
|
|
45
42
|
result[mask_null] = None
|
|
46
43
|
elif hr_mode == "non_null":
|
|
47
|
-
|
|
48
|
-
|
|
44
|
+
mask_remove = left_series.isnull() | right_series.isnull()
|
|
45
|
+
result[mask_remove] = "REMOVE_VALUE"
|
|
49
46
|
elif hr_mode == "non_zero":
|
|
50
47
|
mask_remove = (left_series == 0) & (right_series == 0)
|
|
51
48
|
result[mask_remove] = "REMOVE_VALUE"
|
|
@@ -66,11 +63,7 @@ class HRComparison(Operators.Binary):
|
|
|
66
63
|
return result
|
|
67
64
|
|
|
68
65
|
@classmethod
|
|
69
|
-
def validate(cls,
|
|
70
|
-
left_operand: Dataset,
|
|
71
|
-
right_operand: DataComponent,
|
|
72
|
-
hr_mode: str
|
|
73
|
-
) -> Dataset:
|
|
66
|
+
def validate(cls, left_operand: Dataset, right_operand: DataComponent, hr_mode: str) -> Dataset:
|
|
74
67
|
result_components = {
|
|
75
68
|
comp_name: copy(comp)
|
|
76
69
|
for comp_name, comp in left_operand.components.items()
|
|
@@ -136,7 +129,6 @@ class HRLessEqual(HRComparison):
|
|
|
136
129
|
|
|
137
130
|
|
|
138
131
|
class HRBinNumeric(Operators.Binary):
|
|
139
|
-
|
|
140
132
|
@classmethod
|
|
141
133
|
def op_func(cls, x: Any, y: Any) -> Any:
|
|
142
134
|
if not pd.isnull(x) and x == "REMOVE_VALUE":
|
|
@@ -166,7 +158,6 @@ class HRBinMinus(HRBinNumeric):
|
|
|
166
158
|
|
|
167
159
|
|
|
168
160
|
class HRUnNumeric(Operators.Unary):
|
|
169
|
-
|
|
170
161
|
@classmethod
|
|
171
162
|
def evaluate(cls, operand: DataComponent) -> DataComponent: # type: ignore[override]
|
|
172
163
|
result_data = cls.apply_operation_component(operand.data)
|
|
@@ -190,7 +181,6 @@ class HRUnMinus(HRUnNumeric):
|
|
|
190
181
|
|
|
191
182
|
|
|
192
183
|
class HAAssignment(Operators.Binary):
|
|
193
|
-
|
|
194
184
|
@classmethod
|
|
195
185
|
def validate(cls, left: Dataset, right: DataComponent, hr_mode: str) -> Dataset:
|
|
196
186
|
result_components = {comp_name: copy(comp) for comp_name, comp in left.components.items()}
|