vtlengine 1.0.3rc3__py3-none-any.whl → 1.1__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 +288 -61
- vtlengine/API/__init__.py +269 -71
- vtlengine/API/data/schema/json_schema_2.1.json +116 -0
- vtlengine/AST/ASTComment.py +56 -0
- vtlengine/AST/ASTConstructor.py +76 -22
- vtlengine/AST/ASTConstructorModules/Expr.py +238 -120
- vtlengine/AST/ASTConstructorModules/ExprComponents.py +126 -61
- vtlengine/AST/ASTConstructorModules/Terminals.py +97 -42
- vtlengine/AST/ASTConstructorModules/__init__.py +50 -0
- vtlengine/AST/ASTEncoders.py +5 -1
- vtlengine/AST/ASTString.py +608 -0
- vtlengine/AST/ASTTemplate.py +28 -2
- vtlengine/AST/DAG/__init__.py +10 -4
- vtlengine/AST/Grammar/lexer.py +0 -1
- vtlengine/AST/Grammar/parser.py +185 -440
- vtlengine/AST/VtlVisitor.py +0 -1
- vtlengine/AST/__init__.py +127 -14
- vtlengine/DataTypes/TimeHandling.py +50 -15
- vtlengine/DataTypes/__init__.py +79 -7
- vtlengine/Exceptions/__init__.py +3 -5
- vtlengine/Exceptions/messages.py +74 -105
- vtlengine/Interpreter/__init__.py +136 -46
- vtlengine/Model/__init__.py +14 -11
- vtlengine/Operators/Aggregation.py +17 -9
- vtlengine/Operators/Analytic.py +64 -20
- 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 +47 -15
- 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 +145 -124
- vtlengine/Operators/Validation.py +10 -4
- vtlengine/Operators/__init__.py +56 -69
- vtlengine/Utils/__init__.py +55 -1
- vtlengine/__extras_check.py +17 -0
- vtlengine/__init__.py +2 -2
- vtlengine/files/output/__init__.py +2 -1
- vtlengine/files/output/_time_period_representation.py +2 -1
- vtlengine/files/parser/__init__.py +52 -46
- vtlengine/files/parser/_time_checking.py +4 -4
- {vtlengine-1.0.3rc3.dist-info → vtlengine-1.1.dist-info}/METADATA +21 -17
- vtlengine-1.1.dist-info/RECORD +61 -0
- {vtlengine-1.0.3rc3.dist-info → vtlengine-1.1.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.1.dist-info}/LICENSE.md +0 -0
vtlengine/Operators/String.py
CHANGED
|
@@ -31,7 +31,6 @@ class Unary(Operator.Unary):
|
|
|
31
31
|
|
|
32
32
|
@classmethod
|
|
33
33
|
def op_func(cls, x: Any) -> Any:
|
|
34
|
-
|
|
35
34
|
x = "" if pd.isnull(x) else str(x)
|
|
36
35
|
return cls.py_op(x)
|
|
37
36
|
|
|
@@ -116,7 +115,6 @@ class Concatenate(Binary):
|
|
|
116
115
|
|
|
117
116
|
|
|
118
117
|
class Parameterized(Unary):
|
|
119
|
-
|
|
120
118
|
@classmethod
|
|
121
119
|
def validate(cls, *args: Any) -> Any:
|
|
122
120
|
operand: Operator.ALL_MODEL_DATA_TYPES
|
|
@@ -353,7 +351,6 @@ class Instr(Parameterized):
|
|
|
353
351
|
param2: Optional[Operator.ALL_MODEL_DATA_TYPES] = None,
|
|
354
352
|
param3: Optional[Operator.ALL_MODEL_DATA_TYPES] = None,
|
|
355
353
|
) -> Any:
|
|
356
|
-
|
|
357
354
|
if (
|
|
358
355
|
isinstance(param1, Dataset)
|
|
359
356
|
or isinstance(param2, Dataset)
|
|
@@ -395,7 +392,10 @@ class Instr(Parameterized):
|
|
|
395
392
|
else:
|
|
396
393
|
if not check_unary_implicit_promotion(data_type, Integer):
|
|
397
394
|
raise SemanticError(
|
|
398
|
-
"1-1-18-4",
|
|
395
|
+
"1-1-18-4",
|
|
396
|
+
op=cls.op,
|
|
397
|
+
param_type="Occurrence",
|
|
398
|
+
correct_type="Integer",
|
|
399
399
|
)
|
|
400
400
|
if isinstance(param, DataComponent):
|
|
401
401
|
if param.data is not None:
|
|
@@ -408,9 +408,7 @@ class Instr(Parameterized):
|
|
|
408
408
|
if position == 2 and not pd.isnull(param) and param < 1:
|
|
409
409
|
raise SemanticError("1-1-18-4", op=cls.op, param_type="Start", correct_type=">= 1")
|
|
410
410
|
elif position == 3 and not pd.isnull(param) and param < 1:
|
|
411
|
-
raise SemanticError(
|
|
412
|
-
"1-1-18-4", op=cls.op, param_type="Occurrence", correct_type=">= 1"
|
|
413
|
-
)
|
|
411
|
+
raise SemanticError("1-1-18-4", op=cls.op, param_type="Occurrence", correct_type=">= 1")
|
|
414
412
|
|
|
415
413
|
@classmethod
|
|
416
414
|
def apply_operation_series_scalar(
|
|
@@ -529,7 +527,6 @@ class Instr(Parameterized):
|
|
|
529
527
|
param2: Optional[Any],
|
|
530
528
|
param3: Optional[Any],
|
|
531
529
|
) -> Any:
|
|
532
|
-
|
|
533
530
|
if pd.isnull(x):
|
|
534
531
|
return None
|
|
535
532
|
return cls.py_op(x, param1, param2, param3)
|
|
@@ -560,7 +557,10 @@ class Instr(Parameterized):
|
|
|
560
557
|
else:
|
|
561
558
|
# OPERATORS_STRINGOPERATORS.93
|
|
562
559
|
raise SemanticError(
|
|
563
|
-
"1-1-18-4",
|
|
560
|
+
"1-1-18-4",
|
|
561
|
+
op=cls.op,
|
|
562
|
+
param_type="Occurrence",
|
|
563
|
+
correct_type="Integer",
|
|
564
564
|
)
|
|
565
565
|
else:
|
|
566
566
|
occurrence = 0
|
vtlengine/Operators/Time.py
CHANGED
|
@@ -33,7 +33,7 @@ from vtlengine.DataTypes import (
|
|
|
33
33
|
unary_implicit_promotion,
|
|
34
34
|
)
|
|
35
35
|
from vtlengine.DataTypes.TimeHandling import (
|
|
36
|
-
|
|
36
|
+
PERIOD_IND_MAPPING,
|
|
37
37
|
TimePeriodHandler,
|
|
38
38
|
date_to_period,
|
|
39
39
|
period_to_date,
|
|
@@ -57,14 +57,17 @@ class Time(Operators.Operator):
|
|
|
57
57
|
op = FLOW_TO_STOCK
|
|
58
58
|
|
|
59
59
|
@classmethod
|
|
60
|
-
def _get_time_id(cls, operand: Dataset) ->
|
|
60
|
+
def _get_time_id(cls, operand: Dataset) -> str:
|
|
61
61
|
reference_id = None
|
|
62
|
+
identifiers = operand.get_identifiers()
|
|
63
|
+
if len(identifiers) == 0:
|
|
64
|
+
raise SemanticError("1-1-19-8", op=cls.op, comp_type="time dataset")
|
|
62
65
|
for id in operand.get_identifiers():
|
|
63
66
|
if id.data_type in cls.TIME_DATA_TYPES:
|
|
64
67
|
if reference_id is not None:
|
|
65
68
|
raise SemanticError("1-1-19-8", op=cls.op, comp_type="time dataset")
|
|
66
69
|
reference_id = id.name
|
|
67
|
-
return reference_id
|
|
70
|
+
return str(reference_id)
|
|
68
71
|
|
|
69
72
|
@classmethod
|
|
70
73
|
def sort_by_time(cls, operand: Dataset) -> Optional[pd.DataFrame]:
|
|
@@ -98,7 +101,8 @@ class Time(Operators.Operator):
|
|
|
98
101
|
months_deltas = differences.apply(lambda x: x.days // 30)
|
|
99
102
|
days_deltas = differences.apply(lambda x: x.days)
|
|
100
103
|
min_months = min(
|
|
101
|
-
(diff for diff in months_deltas if diff > 0 and diff % 12 != 0),
|
|
104
|
+
(diff for diff in months_deltas if diff > 0 and diff % 12 != 0),
|
|
105
|
+
default=None,
|
|
102
106
|
)
|
|
103
107
|
min_days = min(
|
|
104
108
|
(diff for diff in days_deltas if diff > 0 and diff % 365 != 0 and diff % 366 != 0),
|
|
@@ -118,7 +122,6 @@ class Time(Operators.Operator):
|
|
|
118
122
|
|
|
119
123
|
|
|
120
124
|
class Unary(Time):
|
|
121
|
-
|
|
122
125
|
@classmethod
|
|
123
126
|
def validate(cls, operand: Any) -> Any:
|
|
124
127
|
if not isinstance(operand, Dataset):
|
|
@@ -182,7 +185,7 @@ class Period_indicator(Unary):
|
|
|
182
185
|
def validate(cls, operand: Any) -> Any:
|
|
183
186
|
if isinstance(operand, Dataset):
|
|
184
187
|
time_id = cls._get_time_id(operand)
|
|
185
|
-
if
|
|
188
|
+
if operand.components[time_id].data_type != TimePeriod:
|
|
186
189
|
raise SemanticError("1-1-19-8", op=cls.op, comp_type="time period dataset")
|
|
187
190
|
result_components = {
|
|
188
191
|
comp.name: comp
|
|
@@ -190,7 +193,10 @@ class Period_indicator(Unary):
|
|
|
190
193
|
if comp.role == Role.IDENTIFIER
|
|
191
194
|
}
|
|
192
195
|
result_components["duration_var"] = Component(
|
|
193
|
-
name="duration_var",
|
|
196
|
+
name="duration_var",
|
|
197
|
+
data_type=Duration,
|
|
198
|
+
role=Role.MEASURE,
|
|
199
|
+
nullable=True,
|
|
194
200
|
)
|
|
195
201
|
return Dataset(name="result", components=result_components, data=None)
|
|
196
202
|
# DataComponent and Scalar validation
|
|
@@ -202,7 +208,7 @@ class Period_indicator(Unary):
|
|
|
202
208
|
|
|
203
209
|
@classmethod
|
|
204
210
|
def evaluate(
|
|
205
|
-
|
|
211
|
+
cls, operand: Union[Dataset, DataComponent, Scalar, str]
|
|
206
212
|
) -> Union[Dataset, DataComponent, Scalar, str]:
|
|
207
213
|
result = cls.validate(operand)
|
|
208
214
|
if isinstance(operand, str):
|
|
@@ -220,13 +226,12 @@ class Period_indicator(Unary):
|
|
|
220
226
|
if (operand.data is not None)
|
|
221
227
|
else pd.Series()
|
|
222
228
|
)
|
|
223
|
-
period_series: Any = result.data[cls.time_id].map(cls._get_period)
|
|
229
|
+
period_series: Any = result.data[cls.time_id].map(cls._get_period)
|
|
224
230
|
result.data["duration_var"] = period_series
|
|
225
231
|
return result
|
|
226
232
|
|
|
227
233
|
|
|
228
234
|
class Parametrized(Time):
|
|
229
|
-
|
|
230
235
|
@classmethod
|
|
231
236
|
def validate(cls, operand: Any, param: Any) -> Any:
|
|
232
237
|
pass
|
|
@@ -237,14 +242,12 @@ class Parametrized(Time):
|
|
|
237
242
|
|
|
238
243
|
|
|
239
244
|
class Flow_to_stock(Unary):
|
|
240
|
-
|
|
241
245
|
@classmethod
|
|
242
246
|
def py_op(cls, x: Any) -> Any:
|
|
243
247
|
return x.cumsum().fillna(x)
|
|
244
248
|
|
|
245
249
|
|
|
246
250
|
class Stock_to_flow(Unary):
|
|
247
|
-
|
|
248
251
|
@classmethod
|
|
249
252
|
def py_op(cls, x: Any) -> Any:
|
|
250
253
|
return x.diff().fillna(x)
|
|
@@ -299,7 +302,6 @@ class Fill_time_series(Binary):
|
|
|
299
302
|
|
|
300
303
|
@classmethod
|
|
301
304
|
def max_min_from_period(cls, data: pd.DataFrame, mode: str = "all") -> Dict[str, Any]:
|
|
302
|
-
|
|
303
305
|
result_dict: Dict[Any, Any] = {}
|
|
304
306
|
data = data.assign(
|
|
305
307
|
Periods_col=data[cls.time_id].apply(cls._get_period),
|
|
@@ -369,7 +371,10 @@ class Fill_time_series(Binary):
|
|
|
369
371
|
else:
|
|
370
372
|
if period in period_limits["min"] and period in period_limits["max"]:
|
|
371
373
|
vals = list(
|
|
372
|
-
range(
|
|
374
|
+
range(
|
|
375
|
+
period_limits["min"][period],
|
|
376
|
+
period_limits["max"][period] + 1,
|
|
377
|
+
)
|
|
373
378
|
)
|
|
374
379
|
filled_data.extend(
|
|
375
380
|
cls.fill_periods_rows(group_df, period, years, vals=vals)
|
|
@@ -385,7 +390,11 @@ class Fill_time_series(Binary):
|
|
|
385
390
|
|
|
386
391
|
@classmethod
|
|
387
392
|
def fill_periods_rows(
|
|
388
|
-
|
|
393
|
+
cls,
|
|
394
|
+
group_df: Any,
|
|
395
|
+
period: str,
|
|
396
|
+
years: List[int],
|
|
397
|
+
vals: Optional[List[int]] = None,
|
|
389
398
|
) -> List[Any]:
|
|
390
399
|
rows = []
|
|
391
400
|
for year in years:
|
|
@@ -398,7 +407,7 @@ class Fill_time_series(Binary):
|
|
|
398
407
|
|
|
399
408
|
@classmethod
|
|
400
409
|
def create_period_row(
|
|
401
|
-
|
|
410
|
+
cls, group_df: Any, period: str, year: int, val: Optional[int] = None
|
|
402
411
|
) -> Any:
|
|
403
412
|
row = group_df.iloc[0].copy()
|
|
404
413
|
row[cls.time_id] = f"{year}" if period == "A" else f"{year}-{period}{val:d}"
|
|
@@ -436,9 +445,7 @@ class Fill_time_series(Binary):
|
|
|
436
445
|
date_format = None
|
|
437
446
|
filled_data = []
|
|
438
447
|
|
|
439
|
-
def create_filled_dates(
|
|
440
|
-
group: Any, min_max: Dict[str, Any]
|
|
441
|
-
) -> (pd.DataFrame, str): # type: ignore[syntax]
|
|
448
|
+
def create_filled_dates(group: Any, min_max: Dict[str, Any]) -> (pd.DataFrame, str): # type: ignore[syntax]
|
|
442
449
|
date_range = pd.date_range(start=min_max["min"], end=min_max["max"], freq=min_frequency)
|
|
443
450
|
date_df = pd.DataFrame(date_range, columns=[cls.time_id])
|
|
444
451
|
date_df[cls.other_ids] = group.iloc[0][cls.other_ids]
|
|
@@ -480,7 +487,7 @@ class Fill_time_series(Binary):
|
|
|
480
487
|
|
|
481
488
|
@classmethod
|
|
482
489
|
def fill_time_intervals(
|
|
483
|
-
|
|
490
|
+
cls, data: pd.DataFrame, fill_type: str, frequency: str
|
|
484
491
|
) -> pd.DataFrame:
|
|
485
492
|
result_data = cls.time_filler(data, fill_type, frequency)
|
|
486
493
|
not_na = result_data[cls.measures].notna().any(axis=1)
|
|
@@ -540,9 +547,7 @@ class Time_Shift(Binary):
|
|
|
540
547
|
shift_value = int(shift_value.value)
|
|
541
548
|
cls.time_id = cls._get_time_id(result)
|
|
542
549
|
|
|
543
|
-
data_type: Any =
|
|
544
|
-
result.components[cls.time_id].data_type if isinstance(cls.time_id, str) else None
|
|
545
|
-
)
|
|
550
|
+
data_type: Any = result.components[cls.time_id].data_type
|
|
546
551
|
|
|
547
552
|
if data_type == Date:
|
|
548
553
|
freq = cls.find_min_frequency(
|
|
@@ -588,7 +593,7 @@ class Time_Shift(Binary):
|
|
|
588
593
|
|
|
589
594
|
@classmethod
|
|
590
595
|
def shift_period(
|
|
591
|
-
|
|
596
|
+
cls, period_str: str, shift_value: int, frequency: Optional[int] = None
|
|
592
597
|
) -> str:
|
|
593
598
|
period_type = cls._get_period(period_str)
|
|
594
599
|
|
|
@@ -628,7 +633,7 @@ class Time_Aggregation(Time):
|
|
|
628
633
|
|
|
629
634
|
@classmethod
|
|
630
635
|
def _check_duration(cls, value: str) -> None:
|
|
631
|
-
if value not in
|
|
636
|
+
if value not in PERIOD_IND_MAPPING:
|
|
632
637
|
raise SemanticError("1-1-19-3", op=cls.op, param="duration")
|
|
633
638
|
|
|
634
639
|
@classmethod
|
|
@@ -636,13 +641,13 @@ class Time_Aggregation(Time):
|
|
|
636
641
|
cls._check_duration(period_to)
|
|
637
642
|
if period_from is not None:
|
|
638
643
|
cls._check_duration(period_from)
|
|
639
|
-
if
|
|
644
|
+
if PERIOD_IND_MAPPING[period_to] <= PERIOD_IND_MAPPING[period_from]:
|
|
640
645
|
# OPERATORS_TIMEOPERATORS.19
|
|
641
646
|
raise SemanticError("1-1-19-4", op=cls.op, value_1=period_from, value_2=period_to)
|
|
642
647
|
|
|
643
648
|
@classmethod
|
|
644
649
|
def dataset_validation(
|
|
645
|
-
|
|
650
|
+
cls, operand: Dataset, period_from: Optional[str], period_to: str, conf: str
|
|
646
651
|
) -> Dataset:
|
|
647
652
|
# TODO: Review with VTL TF as this makes no sense
|
|
648
653
|
|
|
@@ -661,7 +666,10 @@ class Time_Aggregation(Time):
|
|
|
661
666
|
count_time_types += 1
|
|
662
667
|
if count_time_types != 1:
|
|
663
668
|
raise SemanticError(
|
|
664
|
-
"1-1-19-9",
|
|
669
|
+
"1-1-19-9",
|
|
670
|
+
op=cls.op,
|
|
671
|
+
comp_type="dataset",
|
|
672
|
+
param="single time identifier",
|
|
665
673
|
)
|
|
666
674
|
|
|
667
675
|
if count_time_types != 1:
|
|
@@ -679,7 +687,11 @@ class Time_Aggregation(Time):
|
|
|
679
687
|
|
|
680
688
|
@classmethod
|
|
681
689
|
def component_validation(
|
|
682
|
-
|
|
690
|
+
cls,
|
|
691
|
+
operand: DataComponent,
|
|
692
|
+
period_from: Optional[str],
|
|
693
|
+
period_to: str,
|
|
694
|
+
conf: str,
|
|
683
695
|
) -> DataComponent:
|
|
684
696
|
if operand.data_type not in cls.TIME_DATA_TYPES:
|
|
685
697
|
raise SemanticError("1-1-19-8", op=cls.op, comp_type="time component")
|
|
@@ -692,7 +704,7 @@ class Time_Aggregation(Time):
|
|
|
692
704
|
|
|
693
705
|
@classmethod
|
|
694
706
|
def scalar_validation(
|
|
695
|
-
|
|
707
|
+
cls, operand: Scalar, period_from: Optional[str], period_to: str, conf: str
|
|
696
708
|
) -> Scalar:
|
|
697
709
|
if operand.data_type not in cls.TIME_DATA_TYPES:
|
|
698
710
|
raise SemanticError("1-1-19-8", op=cls.op, comp_type="time scalar")
|
|
@@ -701,12 +713,12 @@ class Time_Aggregation(Time):
|
|
|
701
713
|
|
|
702
714
|
@classmethod
|
|
703
715
|
def _execute_time_aggregation(
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
716
|
+
cls,
|
|
717
|
+
value: str,
|
|
718
|
+
data_type: Type[ScalarType],
|
|
719
|
+
period_from: Optional[str],
|
|
720
|
+
period_to: str,
|
|
721
|
+
conf: str,
|
|
710
722
|
) -> str:
|
|
711
723
|
if data_type == TimePeriod: # Time period
|
|
712
724
|
return _time_period_access(value, period_to)
|
|
@@ -722,7 +734,7 @@ class Time_Aggregation(Time):
|
|
|
722
734
|
|
|
723
735
|
@classmethod
|
|
724
736
|
def dataset_evaluation(
|
|
725
|
-
|
|
737
|
+
cls, operand: Dataset, period_from: Optional[str], period_to: str, conf: str
|
|
726
738
|
) -> Dataset:
|
|
727
739
|
result = cls.dataset_validation(operand, period_from, period_to, conf)
|
|
728
740
|
result.data = operand.data.copy() if operand.data is not None else pd.DataFrame()
|
|
@@ -738,7 +750,11 @@ class Time_Aggregation(Time):
|
|
|
738
750
|
|
|
739
751
|
@classmethod
|
|
740
752
|
def component_evaluation(
|
|
741
|
-
|
|
753
|
+
cls,
|
|
754
|
+
operand: DataComponent,
|
|
755
|
+
period_from: Optional[str],
|
|
756
|
+
period_to: str,
|
|
757
|
+
conf: str,
|
|
742
758
|
) -> DataComponent:
|
|
743
759
|
result = cls.component_validation(operand, period_from, period_to, conf)
|
|
744
760
|
if operand.data is not None:
|
|
@@ -752,7 +768,7 @@ class Time_Aggregation(Time):
|
|
|
752
768
|
|
|
753
769
|
@classmethod
|
|
754
770
|
def scalar_evaluation(
|
|
755
|
-
|
|
771
|
+
cls, operand: Scalar, period_from: Optional[str], period_to: str, conf: str
|
|
756
772
|
) -> Scalar:
|
|
757
773
|
result = cls.scalar_validation(operand, period_from, period_to, conf)
|
|
758
774
|
result.value = cls._execute_time_aggregation(
|
|
@@ -762,11 +778,11 @@ class Time_Aggregation(Time):
|
|
|
762
778
|
|
|
763
779
|
@classmethod
|
|
764
780
|
def validate(
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
781
|
+
cls,
|
|
782
|
+
operand: Union[Dataset, DataComponent, Scalar],
|
|
783
|
+
period_from: Optional[str],
|
|
784
|
+
period_to: str,
|
|
785
|
+
conf: str,
|
|
770
786
|
) -> Union[Dataset, DataComponent, Scalar]:
|
|
771
787
|
cls._check_params(period_from, period_to)
|
|
772
788
|
if isinstance(operand, Dataset):
|
|
@@ -778,11 +794,11 @@ class Time_Aggregation(Time):
|
|
|
778
794
|
|
|
779
795
|
@classmethod
|
|
780
796
|
def evaluate(
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
797
|
+
cls,
|
|
798
|
+
operand: Union[Dataset, DataComponent, Scalar],
|
|
799
|
+
period_from: Optional[str],
|
|
800
|
+
period_to: str,
|
|
801
|
+
conf: str,
|
|
786
802
|
) -> Union[Dataset, DataComponent, Scalar]:
|
|
787
803
|
cls._check_params(period_from, period_to)
|
|
788
804
|
if isinstance(operand, Dataset):
|
|
@@ -809,7 +825,6 @@ def _date_access(v: str, to_param: str, start: bool) -> Any:
|
|
|
809
825
|
|
|
810
826
|
|
|
811
827
|
class Current_Date(Time):
|
|
812
|
-
|
|
813
828
|
@classmethod
|
|
814
829
|
def validate(cls) -> Scalar:
|
|
815
830
|
return Scalar(name="current_date", data_type=Date, value=None)
|
|
@@ -830,26 +845,30 @@ class SimpleBinaryTime(Operators.Binary):
|
|
|
830
845
|
if left == TimePeriod and right == Date:
|
|
831
846
|
return False
|
|
832
847
|
|
|
833
|
-
return
|
|
848
|
+
return not (left == TimePeriod and right == Date)
|
|
834
849
|
|
|
835
850
|
@classmethod
|
|
836
851
|
def validate(
|
|
837
|
-
|
|
838
|
-
|
|
852
|
+
cls,
|
|
853
|
+
left_operand: Union[Dataset, DataComponent, Scalar],
|
|
854
|
+
right_operand: Union[Dataset, DataComponent, Scalar],
|
|
839
855
|
) -> Union[Dataset, DataComponent, Scalar]:
|
|
840
856
|
if isinstance(left_operand, Dataset) or isinstance(right_operand, Dataset):
|
|
841
857
|
raise SemanticError("1-1-19-8", op=cls.op, comp_type="time dataset")
|
|
842
858
|
if not cls.validate_type_compatibility(left_operand.data_type, right_operand.data_type):
|
|
843
859
|
raise SemanticError(
|
|
844
|
-
"1-1-1-2",
|
|
845
|
-
|
|
860
|
+
"1-1-1-2",
|
|
861
|
+
type_1=left_operand.data_type,
|
|
862
|
+
type_2=right_operand.data_type,
|
|
863
|
+
type_check=cls.type_to_check,
|
|
846
864
|
)
|
|
847
865
|
return super().validate(left_operand, right_operand)
|
|
848
866
|
|
|
849
867
|
@classmethod
|
|
850
868
|
def evaluate(
|
|
851
|
-
|
|
852
|
-
|
|
869
|
+
cls,
|
|
870
|
+
left_operand: Union[Dataset, DataComponent, Scalar],
|
|
871
|
+
right_operand: Union[Dataset, DataComponent, Scalar],
|
|
853
872
|
) -> Union[Dataset, DataComponent, Scalar]:
|
|
854
873
|
if isinstance(left_operand, Dataset) or isinstance(right_operand, Dataset):
|
|
855
874
|
raise SemanticError("1-1-19-8", op=cls.op, comp_type="time dataset")
|
|
@@ -869,12 +888,12 @@ class Date_Diff(SimpleBinaryTime):
|
|
|
869
888
|
raise SemanticError("1-1-19-8", op=cls.op, comp_type="time dataset")
|
|
870
889
|
|
|
871
890
|
if x.count("-") == 2:
|
|
872
|
-
fecha1 = datetime.strptime(x,
|
|
891
|
+
fecha1 = datetime.strptime(x, "%Y-%m-%d").date()
|
|
873
892
|
else:
|
|
874
893
|
fecha1 = TimePeriodHandler(x).end_date(as_date=True) # type: ignore[assignment]
|
|
875
894
|
|
|
876
895
|
if y.count("-") == 2:
|
|
877
|
-
fecha2 = datetime.strptime(y,
|
|
896
|
+
fecha2 = datetime.strptime(y, "%Y-%m-%d").date()
|
|
878
897
|
else:
|
|
879
898
|
fecha2 = TimePeriodHandler(y).end_date(as_date=True) # type: ignore[assignment]
|
|
880
899
|
|
|
@@ -885,26 +904,31 @@ class Date_Add(Parametrized):
|
|
|
885
904
|
op = DATE_ADD
|
|
886
905
|
|
|
887
906
|
@classmethod
|
|
888
|
-
def validate(
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
) -> Union[Scalar, DataComponent, Dataset]:
|
|
892
|
-
|
|
907
|
+
def validate(
|
|
908
|
+
cls, operand: Union[Scalar, DataComponent, Dataset], param_list: List[Scalar]
|
|
909
|
+
) -> Union[Scalar, DataComponent, Dataset]:
|
|
893
910
|
expected_types = [Integer, String]
|
|
894
911
|
for i, param in enumerate(param_list):
|
|
895
|
-
error =
|
|
896
|
-
|
|
912
|
+
error = (
|
|
913
|
+
12
|
|
914
|
+
if not isinstance(param, Scalar) # type: ignore[redundant-expr]
|
|
915
|
+
else 13
|
|
916
|
+
if (param.data_type != expected_types[i])
|
|
917
|
+
else None
|
|
918
|
+
)
|
|
897
919
|
if error is not None:
|
|
898
|
-
raise SemanticError(
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
if
|
|
907
|
-
|
|
920
|
+
raise SemanticError(
|
|
921
|
+
f"2-1-19-{error}",
|
|
922
|
+
op=cls.op,
|
|
923
|
+
type=(param.__class__.__name__ if error == 12 else param.data_type.__name__),
|
|
924
|
+
name="shiftNumber" if error == 12 else "periodInd",
|
|
925
|
+
expected="Scalar" if error == 12 else expected_types[i].__name__,
|
|
926
|
+
)
|
|
927
|
+
|
|
928
|
+
if isinstance(operand, (Scalar, DataComponent)) and operand.data_type not in [
|
|
929
|
+
Date,
|
|
930
|
+
TimePeriod,
|
|
931
|
+
]:
|
|
908
932
|
unary_implicit_promotion(operand.data_type, Date)
|
|
909
933
|
|
|
910
934
|
if isinstance(operand, Scalar):
|
|
@@ -914,31 +938,38 @@ class Date_Add(Parametrized):
|
|
|
914
938
|
|
|
915
939
|
if all(comp.data_type not in [Date, TimePeriod] for comp in operand.components.values()):
|
|
916
940
|
raise SemanticError("2-1-19-14", op=cls.op, name=operand.name)
|
|
917
|
-
return Dataset(name=
|
|
941
|
+
return Dataset(name="result", components=operand.components.copy(), data=None)
|
|
918
942
|
|
|
919
943
|
@classmethod
|
|
920
|
-
def evaluate(
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
) -> Union[Scalar, DataComponent, Dataset]:
|
|
944
|
+
def evaluate(
|
|
945
|
+
cls, operand: Union[Scalar, DataComponent, Dataset], param_list: List[Scalar]
|
|
946
|
+
) -> Union[Scalar, DataComponent, Dataset]:
|
|
924
947
|
result = cls.validate(operand, param_list)
|
|
925
948
|
shift, period = param_list[0].value, param_list[1].value
|
|
926
949
|
is_tp = isinstance(operand, (Scalar, DataComponent)) and operand.data_type == TimePeriod
|
|
927
950
|
|
|
928
951
|
if isinstance(result, Scalar) and isinstance(operand, Scalar) and operand.value is not None:
|
|
929
952
|
result.value = cls.py_op(operand.value, shift, period, is_tp)
|
|
930
|
-
elif (
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
953
|
+
elif (
|
|
954
|
+
isinstance(result, DataComponent)
|
|
955
|
+
and isinstance(operand, DataComponent)
|
|
956
|
+
and operand.data is not None
|
|
957
|
+
):
|
|
958
|
+
result.data = operand.data.map(
|
|
959
|
+
lambda x: cls.py_op(x, shift, period, is_tp), na_action="ignore"
|
|
960
|
+
)
|
|
961
|
+
elif (
|
|
962
|
+
isinstance(result, Dataset)
|
|
963
|
+
and isinstance(operand, Dataset)
|
|
964
|
+
and operand.data is not None
|
|
965
|
+
):
|
|
936
966
|
result.data = operand.data.copy()
|
|
937
967
|
for measure in operand.get_measures():
|
|
938
968
|
if measure.data_type in [Date, TimePeriod]:
|
|
939
969
|
result.data[measure.name] = result.data[measure.name].map(
|
|
940
|
-
lambda x: cls.py_op(x, shift, period, measure.data_type == TimePeriod),
|
|
941
|
-
na_action="ignore"
|
|
970
|
+
lambda x: cls.py_op(str(x), shift, period, measure.data_type == TimePeriod),
|
|
971
|
+
na_action="ignore",
|
|
972
|
+
)
|
|
942
973
|
measure.data_type = Date
|
|
943
974
|
|
|
944
975
|
if isinstance(result, (Scalar, DataComponent)):
|
|
@@ -946,47 +977,47 @@ class Date_Add(Parametrized):
|
|
|
946
977
|
return result
|
|
947
978
|
|
|
948
979
|
@classmethod
|
|
949
|
-
def py_op(cls,
|
|
950
|
-
date_str: str,
|
|
951
|
-
shift: int, period: str,
|
|
952
|
-
is_tp: bool = False
|
|
953
|
-
) -> str:
|
|
980
|
+
def py_op(cls, date_str: str, shift: int, period: str, is_tp: bool = False) -> str:
|
|
954
981
|
if is_tp:
|
|
955
982
|
tp_value = TimePeriodHandler(date_str)
|
|
956
983
|
date = period_to_date(tp_value.year, tp_value.period_indicator, tp_value.period_number)
|
|
957
984
|
else:
|
|
958
985
|
date = datetime.strptime(date_str, "%Y-%m-%d")
|
|
959
986
|
|
|
960
|
-
if period in [
|
|
961
|
-
days_shift = shift * (7 if period ==
|
|
987
|
+
if period in ["D", "W"]:
|
|
988
|
+
days_shift = shift * (7 if period == "W" else 1)
|
|
962
989
|
return (date + timedelta(days=days_shift)).strftime("%Y-%m-%d")
|
|
963
990
|
|
|
964
|
-
month_shift = {
|
|
991
|
+
month_shift = {"M": 1, "Q": 3, "S": 6, "A": 12}[period] * shift
|
|
965
992
|
new_year = date.year + (date.month - 1 + month_shift) // 12
|
|
966
993
|
new_month = (date.month - 1 + month_shift) % 12 + 1
|
|
967
994
|
last_day = (datetime(new_year, new_month % 12 + 1, 1) - timedelta(days=1)).day
|
|
968
|
-
return date.replace(year=new_year, month=new_month,
|
|
969
|
-
|
|
995
|
+
return date.replace(year=new_year, month=new_month, day=min(date.day, last_day)).strftime(
|
|
996
|
+
"%Y-%m-%d"
|
|
997
|
+
)
|
|
970
998
|
|
|
971
999
|
|
|
972
1000
|
class SimpleUnaryTime(Operators.Unary):
|
|
973
|
-
|
|
974
1001
|
@classmethod
|
|
975
1002
|
def validate(
|
|
976
|
-
|
|
1003
|
+
cls, operand: Union[Dataset, DataComponent, Scalar]
|
|
977
1004
|
) -> Union[Dataset, DataComponent, Scalar]:
|
|
978
1005
|
if isinstance(operand, Dataset):
|
|
979
1006
|
raise SemanticError("1-1-19-8", op=cls.op, comp_type="time dataset")
|
|
980
1007
|
|
|
981
1008
|
# Limit the operand to Date and TimePeriod (cannot be implemented with type_to_check)
|
|
982
|
-
if operand.data_type == TimeInterval or operand.data_type not in (
|
|
1009
|
+
if operand.data_type == TimeInterval or operand.data_type not in (
|
|
1010
|
+
Date,
|
|
1011
|
+
TimePeriod,
|
|
1012
|
+
Duration,
|
|
1013
|
+
):
|
|
983
1014
|
raise SemanticError("1-1-19-10", op=cls.op)
|
|
984
1015
|
|
|
985
1016
|
return super().validate(operand)
|
|
986
1017
|
|
|
987
1018
|
@classmethod
|
|
988
1019
|
def evaluate(
|
|
989
|
-
|
|
1020
|
+
cls, operand: Union[Dataset, DataComponent, Scalar]
|
|
990
1021
|
) -> Union[Dataset, DataComponent, Scalar]:
|
|
991
1022
|
cls.validate(operand)
|
|
992
1023
|
return super().evaluate(operand)
|
|
@@ -1040,19 +1071,21 @@ class Day_of_Year(SimpleUnaryTime):
|
|
|
1040
1071
|
|
|
1041
1072
|
result = TimePeriodHandler(value).end_date(as_date=True)
|
|
1042
1073
|
datetime_value = datetime(
|
|
1043
|
-
year=result.year,
|
|
1074
|
+
year=result.year, # type: ignore[union-attr]
|
|
1075
|
+
month=result.month, # type: ignore[union-attr]
|
|
1076
|
+
day=result.day, # type: ignore[union-attr]
|
|
1044
1077
|
)
|
|
1045
1078
|
return datetime_value.timetuple().tm_yday
|
|
1046
1079
|
|
|
1047
1080
|
|
|
1048
1081
|
class Day_to_Year(Operators.Unary):
|
|
1049
1082
|
op = DAYTOYEAR
|
|
1050
|
-
return_type =
|
|
1083
|
+
return_type = Duration
|
|
1051
1084
|
|
|
1052
1085
|
@classmethod
|
|
1053
1086
|
def py_op(cls, value: int) -> str:
|
|
1054
1087
|
if value < 0:
|
|
1055
|
-
raise SemanticError("2-1-19-
|
|
1088
|
+
raise SemanticError("2-1-19-16", op=cls.op)
|
|
1056
1089
|
years = 0
|
|
1057
1090
|
days_remaining = value
|
|
1058
1091
|
if value >= 365:
|
|
@@ -1063,12 +1096,12 @@ class Day_to_Year(Operators.Unary):
|
|
|
1063
1096
|
|
|
1064
1097
|
class Day_to_Month(Operators.Unary):
|
|
1065
1098
|
op = DAYTOMONTH
|
|
1066
|
-
return_type =
|
|
1099
|
+
return_type = Duration
|
|
1067
1100
|
|
|
1068
1101
|
@classmethod
|
|
1069
1102
|
def py_op(cls, value: int) -> str:
|
|
1070
1103
|
if value < 0:
|
|
1071
|
-
raise SemanticError("2-1-19-
|
|
1104
|
+
raise SemanticError("2-1-19-16", op=cls.op)
|
|
1072
1105
|
months = 0
|
|
1073
1106
|
days_remaining = value
|
|
1074
1107
|
if value >= 30:
|
|
@@ -1083,14 +1116,8 @@ class Year_to_Day(Operators.Unary):
|
|
|
1083
1116
|
|
|
1084
1117
|
@classmethod
|
|
1085
1118
|
def py_op(cls, value: str) -> int:
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
if "Y" not in value:
|
|
1089
|
-
raise SemanticError("2-1-19-15", op=cls.op)
|
|
1090
|
-
index_y = value.index("Y")
|
|
1091
|
-
years = int(value[1:index_y])
|
|
1092
|
-
days = int(value[(index_y + 1): -1])
|
|
1093
|
-
return years * 365 + days
|
|
1119
|
+
days = Duration.to_days(value)
|
|
1120
|
+
return days
|
|
1094
1121
|
|
|
1095
1122
|
|
|
1096
1123
|
class Month_to_Day(Operators.Unary):
|
|
@@ -1099,11 +1126,5 @@ class Month_to_Day(Operators.Unary):
|
|
|
1099
1126
|
|
|
1100
1127
|
@classmethod
|
|
1101
1128
|
def py_op(cls, value: str) -> int:
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
if "M" not in value:
|
|
1105
|
-
raise SemanticError("2-1-19-16", op=cls.op)
|
|
1106
|
-
index_m = value.index("M")
|
|
1107
|
-
months = int(value[1:index_m])
|
|
1108
|
-
days = int(value[(index_m + 1): -1])
|
|
1109
|
-
return months * 30 + days
|
|
1129
|
+
days = Duration.to_days(value)
|
|
1130
|
+
return days
|