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.
- vtlengine/API/_InternalApi.py +791 -0
- vtlengine/API/__init__.py +612 -0
- vtlengine/API/data/schema/external_routines_schema.json +34 -0
- vtlengine/API/data/schema/json_schema_2.1.json +116 -0
- vtlengine/API/data/schema/value_domain_schema.json +97 -0
- vtlengine/AST/ASTComment.py +57 -0
- vtlengine/AST/ASTConstructor.py +598 -0
- vtlengine/AST/ASTConstructorModules/Expr.py +1928 -0
- vtlengine/AST/ASTConstructorModules/ExprComponents.py +995 -0
- vtlengine/AST/ASTConstructorModules/Terminals.py +790 -0
- vtlengine/AST/ASTConstructorModules/__init__.py +50 -0
- vtlengine/AST/ASTDataExchange.py +10 -0
- vtlengine/AST/ASTEncoders.py +32 -0
- vtlengine/AST/ASTString.py +675 -0
- vtlengine/AST/ASTTemplate.py +558 -0
- vtlengine/AST/ASTVisitor.py +25 -0
- vtlengine/AST/DAG/__init__.py +479 -0
- vtlengine/AST/DAG/_words.py +10 -0
- vtlengine/AST/Grammar/Vtl.g4 +705 -0
- vtlengine/AST/Grammar/VtlTokens.g4 +409 -0
- vtlengine/AST/Grammar/__init__.py +0 -0
- vtlengine/AST/Grammar/lexer.py +2139 -0
- vtlengine/AST/Grammar/parser.py +16597 -0
- vtlengine/AST/Grammar/tokens.py +169 -0
- vtlengine/AST/VtlVisitor.py +824 -0
- vtlengine/AST/__init__.py +674 -0
- vtlengine/DataTypes/TimeHandling.py +562 -0
- vtlengine/DataTypes/__init__.py +863 -0
- vtlengine/DataTypes/_time_checking.py +135 -0
- vtlengine/Exceptions/__exception_file_generator.py +96 -0
- vtlengine/Exceptions/__init__.py +159 -0
- vtlengine/Exceptions/messages.py +1004 -0
- vtlengine/Interpreter/__init__.py +2048 -0
- vtlengine/Model/__init__.py +501 -0
- vtlengine/Operators/Aggregation.py +357 -0
- vtlengine/Operators/Analytic.py +455 -0
- vtlengine/Operators/Assignment.py +23 -0
- vtlengine/Operators/Boolean.py +106 -0
- vtlengine/Operators/CastOperator.py +451 -0
- vtlengine/Operators/Clause.py +366 -0
- vtlengine/Operators/Comparison.py +488 -0
- vtlengine/Operators/Conditional.py +495 -0
- vtlengine/Operators/General.py +191 -0
- vtlengine/Operators/HROperators.py +254 -0
- vtlengine/Operators/Join.py +447 -0
- vtlengine/Operators/Numeric.py +422 -0
- vtlengine/Operators/RoleSetter.py +77 -0
- vtlengine/Operators/Set.py +176 -0
- vtlengine/Operators/String.py +578 -0
- vtlengine/Operators/Time.py +1144 -0
- vtlengine/Operators/Validation.py +275 -0
- vtlengine/Operators/__init__.py +900 -0
- vtlengine/Utils/__Virtual_Assets.py +34 -0
- vtlengine/Utils/__init__.py +479 -0
- vtlengine/__extras_check.py +17 -0
- vtlengine/__init__.py +27 -0
- vtlengine/files/__init__.py +0 -0
- vtlengine/files/output/__init__.py +35 -0
- vtlengine/files/output/_time_period_representation.py +55 -0
- vtlengine/files/parser/__init__.py +240 -0
- vtlengine/files/parser/_rfc_dialect.py +22 -0
- vtlengine/py.typed +0 -0
- vtlengine-1.4.0rc2.dist-info/METADATA +89 -0
- vtlengine-1.4.0rc2.dist-info/RECORD +66 -0
- vtlengine-1.4.0rc2.dist-info/WHEEL +4 -0
- vtlengine-1.4.0rc2.dist-info/licenses/LICENSE.md +661 -0
|
@@ -0,0 +1,578 @@
|
|
|
1
|
+
import operator
|
|
2
|
+
import re
|
|
3
|
+
from typing import Any, Optional, Union
|
|
4
|
+
|
|
5
|
+
# if os.environ.get("SPARK", False):
|
|
6
|
+
# import pyspark.pandas as pd
|
|
7
|
+
# else:
|
|
8
|
+
# import pandas as pd
|
|
9
|
+
import pandas as pd
|
|
10
|
+
|
|
11
|
+
import vtlengine.Operators as Operator
|
|
12
|
+
from vtlengine.AST.Grammar.tokens import (
|
|
13
|
+
CONCAT,
|
|
14
|
+
INSTR,
|
|
15
|
+
LCASE,
|
|
16
|
+
LEN,
|
|
17
|
+
LTRIM,
|
|
18
|
+
REPLACE,
|
|
19
|
+
RTRIM,
|
|
20
|
+
SUBSTR,
|
|
21
|
+
TRIM,
|
|
22
|
+
UCASE,
|
|
23
|
+
)
|
|
24
|
+
from vtlengine.DataTypes import Integer, String, check_unary_implicit_promotion
|
|
25
|
+
from vtlengine.Exceptions import SemanticError
|
|
26
|
+
from vtlengine.Model import DataComponent, Dataset, Scalar
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class Unary(Operator.Unary):
|
|
30
|
+
type_to_check = String
|
|
31
|
+
|
|
32
|
+
@classmethod
|
|
33
|
+
def op_func(cls, x: Any) -> Any:
|
|
34
|
+
x = "" if pd.isnull(x) else str(x)
|
|
35
|
+
return cls.py_op(x)
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def apply_operation_component(cls, series: Any) -> Any:
|
|
39
|
+
"""Applies the operation to a component"""
|
|
40
|
+
return series.map(lambda x: cls.py_op(str(x)), na_action="ignore")
|
|
41
|
+
|
|
42
|
+
@classmethod
|
|
43
|
+
def validate_dataset(cls, dataset: Dataset) -> None:
|
|
44
|
+
"""
|
|
45
|
+
Validate that the dataset has exactly one measure.
|
|
46
|
+
"""
|
|
47
|
+
measures = dataset.get_measures()
|
|
48
|
+
|
|
49
|
+
if len(measures) != 1:
|
|
50
|
+
raise SemanticError("1-1-18-1", op=cls.op, name=dataset.name)
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
class Length(Unary):
|
|
54
|
+
op = LEN
|
|
55
|
+
return_type = Integer
|
|
56
|
+
py_op = len
|
|
57
|
+
|
|
58
|
+
@classmethod
|
|
59
|
+
def op_func(cls, x: Any) -> Any:
|
|
60
|
+
result = super().op_func(x)
|
|
61
|
+
if pd.isnull(result):
|
|
62
|
+
return 0
|
|
63
|
+
return result
|
|
64
|
+
|
|
65
|
+
@classmethod
|
|
66
|
+
def apply_operation_component(cls, series: Any) -> Any:
|
|
67
|
+
"""Applies the operation to a component"""
|
|
68
|
+
return series.map(cls.op_func)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
class Lower(Unary):
|
|
72
|
+
op = LCASE
|
|
73
|
+
py_op = str.lower
|
|
74
|
+
return_type = String
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
class Upper(Unary):
|
|
78
|
+
op = UCASE
|
|
79
|
+
py_op = str.upper
|
|
80
|
+
return_type = String
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class Trim(Unary):
|
|
84
|
+
op = TRIM
|
|
85
|
+
py_op = str.strip
|
|
86
|
+
return_type = String
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
class Ltrim(Unary):
|
|
90
|
+
op = LTRIM
|
|
91
|
+
py_op = str.lstrip
|
|
92
|
+
return_type = String
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
class Rtrim(Unary):
|
|
96
|
+
op = RTRIM
|
|
97
|
+
py_op = str.rstrip
|
|
98
|
+
return_type = String
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
class Binary(Operator.Binary):
|
|
102
|
+
type_to_check = String
|
|
103
|
+
|
|
104
|
+
@classmethod
|
|
105
|
+
def op_func(cls, x: Any, y: Any) -> Any:
|
|
106
|
+
x = "" if pd.isnull(x) else str(x)
|
|
107
|
+
y = "" if pd.isnull(y) else str(y)
|
|
108
|
+
return cls.py_op(x, y)
|
|
109
|
+
|
|
110
|
+
|
|
111
|
+
class Concatenate(Binary):
|
|
112
|
+
op = CONCAT
|
|
113
|
+
py_op = operator.concat
|
|
114
|
+
return_type = String
|
|
115
|
+
|
|
116
|
+
|
|
117
|
+
class Parameterized(Unary):
|
|
118
|
+
@classmethod
|
|
119
|
+
def validate(cls, *args: Any) -> Any:
|
|
120
|
+
operand: Operator.ALL_MODEL_DATA_TYPES
|
|
121
|
+
param1: Optional[Scalar]
|
|
122
|
+
param2: Optional[Scalar]
|
|
123
|
+
operand, param1, param2 = (args + (None, None))[:3]
|
|
124
|
+
|
|
125
|
+
if param1 is not None:
|
|
126
|
+
cls.check_param(param1, 1)
|
|
127
|
+
if param2 is not None:
|
|
128
|
+
cls.check_param(param2, 2)
|
|
129
|
+
return super().validate(operand)
|
|
130
|
+
|
|
131
|
+
@classmethod
|
|
132
|
+
def op_func(cls, *args: Any) -> Any:
|
|
133
|
+
x: Optional[Any]
|
|
134
|
+
param1: Optional[Any]
|
|
135
|
+
param2: Optional[Any]
|
|
136
|
+
x, param1, param2 = (args + (None, None))[:3]
|
|
137
|
+
|
|
138
|
+
x = "" if pd.isnull(x) else x
|
|
139
|
+
return cls.py_op(x, param1, param2)
|
|
140
|
+
|
|
141
|
+
@classmethod
|
|
142
|
+
def apply_operation_two_series(cls, *args: Any) -> Any:
|
|
143
|
+
left_series, right_series = args
|
|
144
|
+
|
|
145
|
+
return left_series.combine(right_series, cls.op_func)
|
|
146
|
+
|
|
147
|
+
@classmethod
|
|
148
|
+
def apply_operation_series_scalar(cls, *args: Any) -> Any:
|
|
149
|
+
series, param1, param2 = args
|
|
150
|
+
|
|
151
|
+
return series.map(lambda x: cls.op_func(x, param1, param2))
|
|
152
|
+
|
|
153
|
+
@classmethod
|
|
154
|
+
def dataset_evaluation(cls, *args: Any) -> Dataset:
|
|
155
|
+
operand: Dataset
|
|
156
|
+
param1: Optional[Union[DataComponent, Scalar]]
|
|
157
|
+
param2: Optional[Union[DataComponent, Scalar]]
|
|
158
|
+
operand, param1, param2 = (args + (None, None))[:3]
|
|
159
|
+
|
|
160
|
+
result = cls.validate(operand, param1, param2)
|
|
161
|
+
result.data = operand.data.copy() if operand.data is not None else pd.DataFrame()
|
|
162
|
+
for measure_name in operand.get_measures_names():
|
|
163
|
+
if isinstance(param1, DataComponent) or isinstance(param2, DataComponent):
|
|
164
|
+
result.data[measure_name] = cls.apply_operation_series(
|
|
165
|
+
result.data[measure_name], param1, param2
|
|
166
|
+
)
|
|
167
|
+
else:
|
|
168
|
+
param_value1 = None if param1 is None else param1.value
|
|
169
|
+
param_value2 = None if param2 is None else param2.value
|
|
170
|
+
result.data[measure_name] = cls.apply_operation_series_scalar(
|
|
171
|
+
result.data[measure_name], param_value1, param_value2
|
|
172
|
+
)
|
|
173
|
+
|
|
174
|
+
cols_to_keep = operand.get_identifiers_names() + operand.get_measures_names()
|
|
175
|
+
result.data = result.data[cols_to_keep]
|
|
176
|
+
cls.modify_measure_column(result)
|
|
177
|
+
return result
|
|
178
|
+
|
|
179
|
+
@classmethod
|
|
180
|
+
def component_evaluation(cls, *args: Any) -> DataComponent:
|
|
181
|
+
operand: DataComponent
|
|
182
|
+
param1: Optional[Union[DataComponent, Scalar]]
|
|
183
|
+
param2: Optional[Union[DataComponent, Scalar]]
|
|
184
|
+
operand, param1, param2 = (args + (None, None))[:3]
|
|
185
|
+
|
|
186
|
+
result = cls.validate(operand, param1, param2)
|
|
187
|
+
result.data = operand.data.copy() if operand.data is not None else pd.Series()
|
|
188
|
+
if isinstance(param1, DataComponent) or isinstance(param2, DataComponent):
|
|
189
|
+
result.data = cls.apply_operation_series(result.data, param1, param2)
|
|
190
|
+
else:
|
|
191
|
+
param_value1 = None if param1 is None else param1.value
|
|
192
|
+
param_value2 = None if param2 is None else param2.value
|
|
193
|
+
result.data = cls.apply_operation_series_scalar(
|
|
194
|
+
operand.data, param_value1, param_value2
|
|
195
|
+
)
|
|
196
|
+
return result
|
|
197
|
+
|
|
198
|
+
@classmethod
|
|
199
|
+
def scalar_evaluation(cls, *args: Any) -> Scalar:
|
|
200
|
+
operand: Scalar
|
|
201
|
+
param1: Optional[Scalar]
|
|
202
|
+
param2: Optional[Scalar]
|
|
203
|
+
operand, param1, param2 = (args + (None, None))[:3]
|
|
204
|
+
|
|
205
|
+
result = cls.validate(operand, param1, param2)
|
|
206
|
+
param_value1 = None if param1 is None else param1.value
|
|
207
|
+
param_value2 = None if param2 is None else param2.value
|
|
208
|
+
result.value = cls.op_func(operand.value, param_value1, param_value2)
|
|
209
|
+
return result
|
|
210
|
+
|
|
211
|
+
@classmethod
|
|
212
|
+
def evaluate(cls, *args: Any) -> Union[Dataset, DataComponent, Scalar]:
|
|
213
|
+
param1: Optional[Union[DataComponent, Scalar]]
|
|
214
|
+
param2: Optional[Union[DataComponent, Scalar]]
|
|
215
|
+
operand, param1, param2 = (args + (None, None))[:3]
|
|
216
|
+
|
|
217
|
+
if isinstance(operand, Dataset):
|
|
218
|
+
return cls.dataset_evaluation(operand, param1, param2)
|
|
219
|
+
if isinstance(operand, DataComponent):
|
|
220
|
+
return cls.component_evaluation(operand, param1, param2)
|
|
221
|
+
return cls.scalar_evaluation(operand, param1, param2)
|
|
222
|
+
|
|
223
|
+
@classmethod
|
|
224
|
+
def check_param(cls, *args: Any) -> None:
|
|
225
|
+
raise Exception("Method should be implemented by inheritors")
|
|
226
|
+
|
|
227
|
+
@classmethod
|
|
228
|
+
def check_param_value(cls, *args: Any) -> None:
|
|
229
|
+
raise Exception("Method should be implemented by inheritors")
|
|
230
|
+
|
|
231
|
+
@classmethod
|
|
232
|
+
def generate_series_from_param(cls, *args: Any) -> Any:
|
|
233
|
+
param: Optional[Union[DataComponent, Scalar]] = None
|
|
234
|
+
length: int
|
|
235
|
+
if len(args) == 2:
|
|
236
|
+
param, length = args
|
|
237
|
+
else:
|
|
238
|
+
length = args[0]
|
|
239
|
+
|
|
240
|
+
if param is None:
|
|
241
|
+
return pd.Series(index=range(length), dtype=object)
|
|
242
|
+
if isinstance(param, Scalar):
|
|
243
|
+
return pd.Series(data=[param.value], index=range(length))
|
|
244
|
+
return param.data
|
|
245
|
+
|
|
246
|
+
@classmethod
|
|
247
|
+
def apply_operation_series(cls, *args: Any) -> Any:
|
|
248
|
+
param1: Optional[Union[DataComponent, Scalar]]
|
|
249
|
+
param2: Optional[Union[DataComponent, Scalar]]
|
|
250
|
+
data, param1, param2 = (args + (None, None))[:3]
|
|
251
|
+
|
|
252
|
+
param1_data = cls.generate_series_from_param(param1, len(data))
|
|
253
|
+
param2_data = cls.generate_series_from_param(param2, len(data))
|
|
254
|
+
df = pd.DataFrame([data, param1_data, param2_data]).T
|
|
255
|
+
n1, n2, n3 = df.columns
|
|
256
|
+
return df.apply(lambda x: cls.op_func(x[n1], x[n2], x[n3]), axis=1)
|
|
257
|
+
|
|
258
|
+
|
|
259
|
+
class Substr(Parameterized):
|
|
260
|
+
op = SUBSTR
|
|
261
|
+
return_type = String
|
|
262
|
+
|
|
263
|
+
@classmethod
|
|
264
|
+
def validate_params(cls, params: Any) -> None:
|
|
265
|
+
if len(params) != 2:
|
|
266
|
+
raise SemanticError("1-1-18-7", op=cls.op, number=len(params), expected=2)
|
|
267
|
+
|
|
268
|
+
@classmethod
|
|
269
|
+
def py_op(cls, x: str, param1: Any, param2: Any) -> Any:
|
|
270
|
+
x = str(x)
|
|
271
|
+
param1 = None if pd.isnull(param1) else int(param1)
|
|
272
|
+
param2 = None if pd.isnull(param2) else int(param2)
|
|
273
|
+
if param1 is None and param2 is None:
|
|
274
|
+
return x
|
|
275
|
+
if param1 is None:
|
|
276
|
+
param1 = 0
|
|
277
|
+
elif param1 != 0:
|
|
278
|
+
param1 -= 1
|
|
279
|
+
elif param1 > (len(x)):
|
|
280
|
+
return ""
|
|
281
|
+
param2 = len(x) if param2 is None or param1 + param2 > len(x) else param1 + param2
|
|
282
|
+
return x[param1:param2]
|
|
283
|
+
|
|
284
|
+
@classmethod
|
|
285
|
+
def check_param(cls, param: Optional[Union[DataComponent, Scalar]], position: int) -> None:
|
|
286
|
+
if not param:
|
|
287
|
+
return
|
|
288
|
+
if position not in (1, 2):
|
|
289
|
+
raise SemanticError("1-1-18-3", op=cls.op, pos=position)
|
|
290
|
+
data_type: Any = param.data_type
|
|
291
|
+
|
|
292
|
+
if not check_unary_implicit_promotion(data_type, Integer):
|
|
293
|
+
raise SemanticError("1-1-18-4", op=cls.op, param_type=cls.op, correct_type="Integer")
|
|
294
|
+
|
|
295
|
+
if isinstance(param, DataComponent):
|
|
296
|
+
if param.data is not None:
|
|
297
|
+
param.data.map(lambda x: cls.check_param_value(x, position))
|
|
298
|
+
else:
|
|
299
|
+
cls.check_param_value(param.value, position)
|
|
300
|
+
|
|
301
|
+
@classmethod
|
|
302
|
+
def check_param_value(cls, param: Optional[Any], position: int) -> None:
|
|
303
|
+
if param is not None:
|
|
304
|
+
if not pd.isnull(param) and not param >= 1 and position == 1:
|
|
305
|
+
raise SemanticError("1-1-18-4", op=cls.op, param_type="Start", correct_type=">= 1")
|
|
306
|
+
elif not pd.isnull(param) and not param >= 0 and position == 2:
|
|
307
|
+
raise SemanticError("1-1-18-4", op=cls.op, param_type="Length", correct_type=">= 0")
|
|
308
|
+
|
|
309
|
+
|
|
310
|
+
class Replace(Parameterized):
|
|
311
|
+
op = REPLACE
|
|
312
|
+
return_type = String
|
|
313
|
+
|
|
314
|
+
@classmethod
|
|
315
|
+
def py_op(cls, x: str, param1: Optional[Any], param2: Optional[Any]) -> Any:
|
|
316
|
+
if pd.isnull(param1):
|
|
317
|
+
return ""
|
|
318
|
+
elif pd.isnull(param2):
|
|
319
|
+
param2 = ""
|
|
320
|
+
x = str(x)
|
|
321
|
+
if param1 is not None and param2 is not None:
|
|
322
|
+
return x.replace(param1, param2)
|
|
323
|
+
return x
|
|
324
|
+
|
|
325
|
+
@classmethod
|
|
326
|
+
def check_param(cls, param: Optional[Union[DataComponent, Scalar]], position: int) -> None:
|
|
327
|
+
if not param:
|
|
328
|
+
return
|
|
329
|
+
if position not in (1, 2):
|
|
330
|
+
raise SemanticError("1-1-18-3", op=cls.op, pos=position)
|
|
331
|
+
data_type: Any = param.data_type
|
|
332
|
+
|
|
333
|
+
if not check_unary_implicit_promotion(data_type, String):
|
|
334
|
+
raise SemanticError("1-1-18-4", op=cls.op, param_type=cls.op, correct_type="String")
|
|
335
|
+
|
|
336
|
+
@classmethod
|
|
337
|
+
def validate_params(cls, params: Any) -> None:
|
|
338
|
+
if len(params) != 2:
|
|
339
|
+
raise SemanticError("1-1-18-7", op=cls.op, number=len(params), expected=2)
|
|
340
|
+
|
|
341
|
+
|
|
342
|
+
class Instr(Parameterized):
|
|
343
|
+
op = INSTR
|
|
344
|
+
return_type = Integer
|
|
345
|
+
|
|
346
|
+
@classmethod
|
|
347
|
+
def validate(
|
|
348
|
+
cls,
|
|
349
|
+
operand: Operator.ALL_MODEL_DATA_TYPES,
|
|
350
|
+
param1: Optional[Operator.ALL_MODEL_DATA_TYPES] = None,
|
|
351
|
+
param2: Optional[Operator.ALL_MODEL_DATA_TYPES] = None,
|
|
352
|
+
param3: Optional[Operator.ALL_MODEL_DATA_TYPES] = None,
|
|
353
|
+
) -> Any:
|
|
354
|
+
if (
|
|
355
|
+
isinstance(param1, Dataset)
|
|
356
|
+
or isinstance(param2, Dataset)
|
|
357
|
+
or isinstance(param3, Dataset)
|
|
358
|
+
):
|
|
359
|
+
raise SemanticError("1-1-18-10", op=cls.op)
|
|
360
|
+
if param1 is not None:
|
|
361
|
+
cls.check_param(param1, 1)
|
|
362
|
+
if param2 is not None:
|
|
363
|
+
cls.check_param(param2, 2)
|
|
364
|
+
if param3 is not None:
|
|
365
|
+
cls.check_param(param3, 3)
|
|
366
|
+
|
|
367
|
+
return super().validate(operand)
|
|
368
|
+
|
|
369
|
+
@classmethod
|
|
370
|
+
def validate_params(cls, params: Any) -> None:
|
|
371
|
+
if len(params) != 2:
|
|
372
|
+
raise SemanticError("1-1-18-7", op=cls.op, number=len(params), expected=2)
|
|
373
|
+
|
|
374
|
+
@classmethod
|
|
375
|
+
def check_param(cls, param: Optional[Union[DataComponent, Scalar]], position: int) -> None:
|
|
376
|
+
if not param:
|
|
377
|
+
return
|
|
378
|
+
if position not in (1, 2, 3):
|
|
379
|
+
raise SemanticError("1-1-18-9", op=cls.op)
|
|
380
|
+
data_type: Any = param.data_type
|
|
381
|
+
|
|
382
|
+
if position == 1:
|
|
383
|
+
if not check_unary_implicit_promotion(data_type, String):
|
|
384
|
+
raise SemanticError(
|
|
385
|
+
"1-1-18-4", op=cls.op, param_type="Pattern", correct_type="String"
|
|
386
|
+
)
|
|
387
|
+
elif position == 2:
|
|
388
|
+
if not check_unary_implicit_promotion(data_type, Integer):
|
|
389
|
+
raise SemanticError(
|
|
390
|
+
"1-1-18-4", op=cls.op, param_type="Start", correct_type="Integer"
|
|
391
|
+
)
|
|
392
|
+
else:
|
|
393
|
+
if not check_unary_implicit_promotion(data_type, Integer):
|
|
394
|
+
raise SemanticError(
|
|
395
|
+
"1-1-18-4",
|
|
396
|
+
op=cls.op,
|
|
397
|
+
param_type="Occurrence",
|
|
398
|
+
correct_type="Integer",
|
|
399
|
+
)
|
|
400
|
+
if isinstance(param, DataComponent):
|
|
401
|
+
if param.data is not None:
|
|
402
|
+
param.data.map(lambda x: cls.check_param_value(x, position))
|
|
403
|
+
else:
|
|
404
|
+
cls.check_param_value(param.value, position)
|
|
405
|
+
|
|
406
|
+
@classmethod
|
|
407
|
+
def check_param_value(cls, param: Any, position: int) -> None:
|
|
408
|
+
if position == 2 and not pd.isnull(param) and param < 1:
|
|
409
|
+
raise SemanticError("1-1-18-4", op=cls.op, param_type="Start", correct_type=">= 1")
|
|
410
|
+
elif position == 3 and not pd.isnull(param) and param < 1:
|
|
411
|
+
raise SemanticError("1-1-18-4", op=cls.op, param_type="Occurrence", correct_type=">= 1")
|
|
412
|
+
|
|
413
|
+
@classmethod
|
|
414
|
+
def apply_operation_series_scalar(
|
|
415
|
+
cls, series: Any, param1: Any, param2: Any, param3: Any
|
|
416
|
+
) -> Any:
|
|
417
|
+
return series.map(lambda x: cls.op_func(x, param1, param2, param3))
|
|
418
|
+
|
|
419
|
+
@classmethod
|
|
420
|
+
def apply_operation_series(
|
|
421
|
+
cls,
|
|
422
|
+
data: Any,
|
|
423
|
+
param1: Optional[Union[DataComponent, Scalar]],
|
|
424
|
+
param2: Optional[Union[DataComponent, Scalar]],
|
|
425
|
+
param3: Optional[Union[DataComponent, Scalar]],
|
|
426
|
+
) -> Any:
|
|
427
|
+
param1_data = cls.generate_series_from_param(param1, len(data))
|
|
428
|
+
param2_data = cls.generate_series_from_param(param2, len(data))
|
|
429
|
+
param3_data = cls.generate_series_from_param(param3, len(data))
|
|
430
|
+
|
|
431
|
+
df = pd.DataFrame([data, param1_data, param2_data, param3_data]).T
|
|
432
|
+
n1, n2, n3, n4 = df.columns
|
|
433
|
+
return df.apply(lambda x: cls.op_func(x[n1], x[n2], x[n3], x[n4]), axis=1)
|
|
434
|
+
|
|
435
|
+
@classmethod
|
|
436
|
+
def dataset_evaluation( # type: ignore[override]
|
|
437
|
+
cls,
|
|
438
|
+
operand: Dataset,
|
|
439
|
+
param1: Optional[Union[DataComponent, Scalar]],
|
|
440
|
+
param2: Optional[Union[DataComponent, Scalar]],
|
|
441
|
+
param3: Optional[Union[DataComponent, Scalar]],
|
|
442
|
+
) -> Dataset:
|
|
443
|
+
result = cls.validate(operand, param1, param2, param3)
|
|
444
|
+
result.data = operand.data.copy() if operand.data is not None else pd.DataFrame()
|
|
445
|
+
for measure_name in operand.get_measures_names():
|
|
446
|
+
if (
|
|
447
|
+
isinstance(param1, DataComponent)
|
|
448
|
+
or isinstance(param2, DataComponent)
|
|
449
|
+
or isinstance(param3, DataComponent)
|
|
450
|
+
):
|
|
451
|
+
if operand.data is not None:
|
|
452
|
+
result.data[measure_name] = cls.apply_operation_series(
|
|
453
|
+
operand.data[measure_name], param1, param2, param3
|
|
454
|
+
)
|
|
455
|
+
else:
|
|
456
|
+
param_value1 = None if param1 is None else param1.value
|
|
457
|
+
param_value2 = None if param2 is None else param2.value
|
|
458
|
+
param_value3 = None if param3 is None else param3.value
|
|
459
|
+
result.data[measure_name] = cls.apply_operation_series_scalar(
|
|
460
|
+
result.data[measure_name], param_value1, param_value2, param_value3
|
|
461
|
+
)
|
|
462
|
+
cols_to_keep = operand.get_identifiers_names() + operand.get_measures_names()
|
|
463
|
+
result.data = result.data[cols_to_keep]
|
|
464
|
+
cls.modify_measure_column(result)
|
|
465
|
+
return result
|
|
466
|
+
|
|
467
|
+
@classmethod
|
|
468
|
+
def component_evaluation( # type: ignore[override]
|
|
469
|
+
cls,
|
|
470
|
+
operand: DataComponent,
|
|
471
|
+
param1: Optional[Union[DataComponent, Scalar]],
|
|
472
|
+
param2: Optional[Union[DataComponent, Scalar]],
|
|
473
|
+
param3: Optional[Union[DataComponent, Scalar]],
|
|
474
|
+
) -> DataComponent:
|
|
475
|
+
result = cls.validate(operand, param1, param2, param3)
|
|
476
|
+
result.data = operand.data.copy() if operand.data is not None else pd.Series()
|
|
477
|
+
if (
|
|
478
|
+
isinstance(param1, DataComponent)
|
|
479
|
+
or isinstance(param2, DataComponent)
|
|
480
|
+
or isinstance(param3, DataComponent)
|
|
481
|
+
):
|
|
482
|
+
result.data = cls.apply_operation_series(operand.data, param1, param2, param3)
|
|
483
|
+
else:
|
|
484
|
+
param_value1 = None if param1 is None else param1.value
|
|
485
|
+
param_value2 = None if param2 is None else param2.value
|
|
486
|
+
param_value3 = None if param3 is None else param3.value
|
|
487
|
+
result.data = cls.apply_operation_series_scalar(
|
|
488
|
+
operand.data, param_value1, param_value2, param_value3
|
|
489
|
+
)
|
|
490
|
+
return result
|
|
491
|
+
|
|
492
|
+
@classmethod
|
|
493
|
+
def scalar_evaluation( # type: ignore[override]
|
|
494
|
+
cls,
|
|
495
|
+
operand: Scalar,
|
|
496
|
+
param1: Optional[Scalar],
|
|
497
|
+
param2: Optional[Scalar],
|
|
498
|
+
param3: Optional[Scalar],
|
|
499
|
+
) -> Scalar:
|
|
500
|
+
result = cls.validate(operand, param1, param2, param3)
|
|
501
|
+
param_value1 = None if param1 is None else param1.value
|
|
502
|
+
param_value2 = None if param2 is None else param2.value
|
|
503
|
+
param_value3 = None if param3 is None else param3.value
|
|
504
|
+
result.value = cls.op_func(operand.value, param_value1, param_value2, param_value3)
|
|
505
|
+
return result
|
|
506
|
+
|
|
507
|
+
@classmethod
|
|
508
|
+
def evaluate(
|
|
509
|
+
cls,
|
|
510
|
+
operand: Operator.ALL_MODEL_DATA_TYPES,
|
|
511
|
+
param1: Optional[Any] = None,
|
|
512
|
+
param2: Optional[Any] = None,
|
|
513
|
+
param3: Optional[Any] = None,
|
|
514
|
+
) -> Any:
|
|
515
|
+
if isinstance(operand, Dataset):
|
|
516
|
+
return cls.dataset_evaluation(operand, param1, param2, param3)
|
|
517
|
+
if isinstance(operand, DataComponent):
|
|
518
|
+
return cls.component_evaluation(operand, param1, param2, param3)
|
|
519
|
+
if isinstance(operand, Scalar):
|
|
520
|
+
return cls.scalar_evaluation(operand, param1, param2, param3)
|
|
521
|
+
|
|
522
|
+
@classmethod
|
|
523
|
+
def op_func( # type: ignore[override]
|
|
524
|
+
cls,
|
|
525
|
+
x: Any,
|
|
526
|
+
param1: Optional[Any],
|
|
527
|
+
param2: Optional[Any],
|
|
528
|
+
param3: Optional[Any],
|
|
529
|
+
) -> Any:
|
|
530
|
+
if pd.isnull(x):
|
|
531
|
+
return None
|
|
532
|
+
return cls.py_op(x, param1, param2, param3)
|
|
533
|
+
|
|
534
|
+
@classmethod
|
|
535
|
+
def py_op(
|
|
536
|
+
cls,
|
|
537
|
+
str_value: str,
|
|
538
|
+
str_to_find: Optional[str],
|
|
539
|
+
start: Optional[int],
|
|
540
|
+
occurrence: Optional[int],
|
|
541
|
+
) -> Any:
|
|
542
|
+
str_value = str(str_value)
|
|
543
|
+
if not pd.isnull(start):
|
|
544
|
+
if isinstance(start, (int, float)):
|
|
545
|
+
start = int(start - 1)
|
|
546
|
+
else:
|
|
547
|
+
# OPERATORS_STRINGOPERATORS.92
|
|
548
|
+
raise SemanticError(
|
|
549
|
+
"1-1-18-4", op=cls.op, param_type="Start", correct_type="Integer"
|
|
550
|
+
)
|
|
551
|
+
else:
|
|
552
|
+
start = 0
|
|
553
|
+
|
|
554
|
+
if not pd.isnull(occurrence):
|
|
555
|
+
if isinstance(occurrence, (int, float)):
|
|
556
|
+
occurrence = int(occurrence - 1)
|
|
557
|
+
else:
|
|
558
|
+
# OPERATORS_STRINGOPERATORS.93
|
|
559
|
+
raise SemanticError(
|
|
560
|
+
"1-1-18-4",
|
|
561
|
+
op=cls.op,
|
|
562
|
+
param_type="Occurrence",
|
|
563
|
+
correct_type="Integer",
|
|
564
|
+
)
|
|
565
|
+
else:
|
|
566
|
+
occurrence = 0
|
|
567
|
+
if pd.isnull(str_to_find):
|
|
568
|
+
return 0
|
|
569
|
+
else:
|
|
570
|
+
str_to_find = str(str_to_find)
|
|
571
|
+
|
|
572
|
+
occurrences_list = [m.start() for m in re.finditer(str_to_find, str_value[start:])]
|
|
573
|
+
|
|
574
|
+
length = len(occurrences_list)
|
|
575
|
+
|
|
576
|
+
position = 0 if occurrence > length - 1 else int(start + occurrences_list[occurrence] + 1)
|
|
577
|
+
|
|
578
|
+
return position
|