pylegend 0.7.0__py3-none-any.whl → 0.9.0__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.
- pylegend/_typing.py +6 -0
- pylegend/core/database/sql_to_string/db_extension.py +35 -6
- pylegend/core/language/pandas_api/__init__.py +13 -0
- pylegend/core/language/pandas_api/pandas_api_aggregate_specification.py +54 -0
- pylegend/core/language/pandas_api/pandas_api_custom_expressions.py +85 -0
- pylegend/core/language/pandas_api/pandas_api_series.py +174 -0
- pylegend/core/language/pandas_api/pandas_api_tds_row.py +74 -0
- pylegend/core/language/shared/literal_expressions.py +2 -2
- pylegend/core/language/shared/operations/integer_operation_expressions.py +35 -0
- pylegend/core/language/shared/operations/nary_expression.py +104 -0
- pylegend/core/language/shared/operations/primitive_operation_expressions.py +30 -0
- pylegend/core/language/shared/operations/string_operation_expressions.py +624 -1
- pylegend/core/language/shared/pct_helpers.py +56 -0
- pylegend/core/language/shared/primitives/integer.py +6 -0
- pylegend/core/language/shared/primitives/primitive.py +6 -0
- pylegend/core/language/shared/primitives/string.py +129 -1
- pylegend/core/sql/metamodel.py +3 -1
- pylegend/core/sql/metamodel_extension.py +18 -0
- pylegend/core/tds/pandas_api/frames/functions/aggregate_function.py +316 -0
- pylegend/core/tds/pandas_api/frames/functions/assign_function.py +20 -15
- pylegend/core/tds/pandas_api/frames/functions/drop.py +171 -0
- pylegend/core/tds/pandas_api/frames/functions/filter.py +193 -0
- pylegend/core/tds/pandas_api/frames/functions/filtering.py +85 -0
- pylegend/core/tds/pandas_api/frames/functions/sort_values_function.py +189 -0
- pylegend/core/tds/pandas_api/frames/functions/truncate_function.py +120 -0
- pylegend/core/tds/pandas_api/frames/pandas_api_applied_function_tds_frame.py +5 -1
- pylegend/core/tds/pandas_api/frames/pandas_api_base_tds_frame.py +204 -7
- pylegend/core/tds/pandas_api/frames/pandas_api_input_tds_frame.py +5 -3
- pylegend/core/tds/pandas_api/frames/pandas_api_tds_frame.py +90 -3
- {pylegend-0.7.0.dist-info → pylegend-0.9.0.dist-info}/METADATA +1 -1
- {pylegend-0.7.0.dist-info → pylegend-0.9.0.dist-info}/RECORD +35 -22
- {pylegend-0.7.0.dist-info → pylegend-0.9.0.dist-info}/WHEEL +0 -0
- {pylegend-0.7.0.dist-info → pylegend-0.9.0.dist-info}/licenses/LICENSE +0 -0
- {pylegend-0.7.0.dist-info → pylegend-0.9.0.dist-info}/licenses/LICENSE.spdx +0 -0
- {pylegend-0.7.0.dist-info → pylegend-0.9.0.dist-info}/licenses/NOTICE +0 -0
|
@@ -0,0 +1,189 @@
|
|
|
1
|
+
# Copyright 2025 Goldman Sachs
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
from pylegend._typing import (
|
|
16
|
+
PyLegendUnion,
|
|
17
|
+
PyLegendOptional,
|
|
18
|
+
PyLegendCallable,
|
|
19
|
+
PyLegendList,
|
|
20
|
+
PyLegendSequence,
|
|
21
|
+
)
|
|
22
|
+
from pylegend.core.language.shared.tds_row import AbstractTdsRow
|
|
23
|
+
from pylegend.core.sql.metamodel import (
|
|
24
|
+
QuerySpecification,
|
|
25
|
+
SortItemOrdering,
|
|
26
|
+
SortItem,
|
|
27
|
+
SortItemNullOrdering, SingleColumn, Expression,
|
|
28
|
+
)
|
|
29
|
+
from pylegend.core.tds.pandas_api.frames.pandas_api_applied_function_tds_frame import (
|
|
30
|
+
PandasApiAppliedFunction,
|
|
31
|
+
)
|
|
32
|
+
from pylegend.core.tds.pandas_api.frames.pandas_api_base_tds_frame import (
|
|
33
|
+
PandasApiBaseTdsFrame,
|
|
34
|
+
)
|
|
35
|
+
from pylegend.core.tds.sql_query_helpers import create_sub_query, copy_query
|
|
36
|
+
from pylegend.core.tds.tds_frame import FrameToSqlConfig, FrameToPureConfig
|
|
37
|
+
from pylegend.core.tds.tds_column import TdsColumn
|
|
38
|
+
from pylegend.core.language.shared.helpers import escape_column_name
|
|
39
|
+
|
|
40
|
+
|
|
41
|
+
class SortValuesFunction(PandasApiAppliedFunction):
|
|
42
|
+
__base_frame: PandasApiBaseTdsFrame
|
|
43
|
+
__by: PyLegendList[str]
|
|
44
|
+
__axis: PyLegendUnion[str, int]
|
|
45
|
+
__ascending: PyLegendList[bool]
|
|
46
|
+
__inplace: bool
|
|
47
|
+
__kind: PyLegendOptional[str]
|
|
48
|
+
__na_position: str
|
|
49
|
+
__ignore_index: bool
|
|
50
|
+
key: PyLegendOptional[PyLegendCallable[[AbstractTdsRow], AbstractTdsRow]] = None
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def name(cls) -> str:
|
|
54
|
+
return "sort_values" # pragma: no cover
|
|
55
|
+
|
|
56
|
+
def __init__(
|
|
57
|
+
self,
|
|
58
|
+
base_frame: PandasApiBaseTdsFrame,
|
|
59
|
+
by: PyLegendUnion[str, PyLegendList[str]],
|
|
60
|
+
axis: PyLegendUnion[str, int],
|
|
61
|
+
ascending: PyLegendUnion[bool, PyLegendList[bool]],
|
|
62
|
+
inplace: bool,
|
|
63
|
+
kind: PyLegendOptional[str],
|
|
64
|
+
na_position: str,
|
|
65
|
+
ignore_index: bool,
|
|
66
|
+
key: PyLegendOptional[PyLegendCallable[[AbstractTdsRow], AbstractTdsRow]] = None
|
|
67
|
+
) -> None:
|
|
68
|
+
self.__base_frame = base_frame
|
|
69
|
+
self.__by_input = by
|
|
70
|
+
self.__axis = axis
|
|
71
|
+
self.__ascending_input = ascending
|
|
72
|
+
self.__inplace = inplace
|
|
73
|
+
self.__kind = kind
|
|
74
|
+
self.__na_position = na_position
|
|
75
|
+
self.__ignore_index = ignore_index
|
|
76
|
+
self.__key = key
|
|
77
|
+
|
|
78
|
+
def to_sql(self, config: FrameToSqlConfig) -> QuerySpecification:
|
|
79
|
+
base_query: QuerySpecification = self.__base_frame.to_sql_query_object(config)
|
|
80
|
+
should_create_sub_query = (base_query.offset is not None) or (base_query.limit is not None)
|
|
81
|
+
new_query = (
|
|
82
|
+
create_sub_query(base_query, config, "root") if should_create_sub_query
|
|
83
|
+
else copy_query(base_query)
|
|
84
|
+
)
|
|
85
|
+
new_query.orderBy = [
|
|
86
|
+
SortItem(
|
|
87
|
+
sortKey=self.get_expression_from_column_name(new_query, column_name, config),
|
|
88
|
+
ordering=(
|
|
89
|
+
SortItemOrdering.ASCENDING if ascending
|
|
90
|
+
else SortItemOrdering.DESCENDING
|
|
91
|
+
),
|
|
92
|
+
nullOrdering=SortItemNullOrdering.UNDEFINED,
|
|
93
|
+
)
|
|
94
|
+
for column_name, ascending in zip(self.__by, self.__ascending)
|
|
95
|
+
]
|
|
96
|
+
return new_query
|
|
97
|
+
|
|
98
|
+
def get_expression_from_column_name(self, query: QuerySpecification, column_name: str,
|
|
99
|
+
config: FrameToSqlConfig) -> Expression:
|
|
100
|
+
db_extension = config.sql_to_string_generator().get_db_extension()
|
|
101
|
+
filtered = [
|
|
102
|
+
s for s in query.select.selectItems
|
|
103
|
+
if (isinstance(s, SingleColumn) and
|
|
104
|
+
s.alias == db_extension.quote_identifier(column_name))
|
|
105
|
+
]
|
|
106
|
+
if len(filtered) == 0:
|
|
107
|
+
raise RuntimeError("Cannot find column: " + column_name) # pragma: no cover
|
|
108
|
+
return filtered[0].expression
|
|
109
|
+
|
|
110
|
+
def to_pure(self, config: FrameToPureConfig) -> str:
|
|
111
|
+
escaped_columns = []
|
|
112
|
+
for col_name in self.__by:
|
|
113
|
+
escaped_columns.append(escape_column_name(col_name))
|
|
114
|
+
sort_items = [
|
|
115
|
+
f"~{column_name}->ascending()" if ascending else f"~{column_name}->descending()"
|
|
116
|
+
for column_name, ascending in zip(escaped_columns, self.__ascending)
|
|
117
|
+
]
|
|
118
|
+
return (
|
|
119
|
+
f"{self.__base_frame.to_pure(config)}{config.separator(1)}"
|
|
120
|
+
+ f"->sort([{', '.join(sort_items)}])"
|
|
121
|
+
)
|
|
122
|
+
|
|
123
|
+
def base_frame(self) -> PandasApiBaseTdsFrame:
|
|
124
|
+
return self.__base_frame
|
|
125
|
+
|
|
126
|
+
def tds_frame_parameters(self) -> PyLegendList["PandasApiBaseTdsFrame"]:
|
|
127
|
+
return []
|
|
128
|
+
|
|
129
|
+
def calculate_columns(self) -> PyLegendSequence["TdsColumn"]:
|
|
130
|
+
return [c.copy() for c in self.__base_frame.columns()]
|
|
131
|
+
|
|
132
|
+
def validate(self) -> bool:
|
|
133
|
+
if self.__axis not in [0, "index"]:
|
|
134
|
+
raise ValueError(
|
|
135
|
+
"Axis parameter of sort_values function must be 0 or 'index'"
|
|
136
|
+
)
|
|
137
|
+
|
|
138
|
+
if self.__inplace is not False:
|
|
139
|
+
raise ValueError("Inplace parameter of sort_values function must be False")
|
|
140
|
+
|
|
141
|
+
if self.__kind is not None:
|
|
142
|
+
raise NotImplementedError(
|
|
143
|
+
"Kind parameter of sort_values function is not supported"
|
|
144
|
+
)
|
|
145
|
+
|
|
146
|
+
if self.__ignore_index is not True:
|
|
147
|
+
raise ValueError(
|
|
148
|
+
"Ignore_index parameter of sort_values function must be True"
|
|
149
|
+
)
|
|
150
|
+
|
|
151
|
+
if self.__key is not None:
|
|
152
|
+
raise NotImplementedError(
|
|
153
|
+
"Key parameter of sort_values function is not supported"
|
|
154
|
+
)
|
|
155
|
+
|
|
156
|
+
base_frame_columns = [
|
|
157
|
+
column.get_name() for column in self.__base_frame.columns()
|
|
158
|
+
]
|
|
159
|
+
|
|
160
|
+
self.__by = self._build_by_list()
|
|
161
|
+
self.__ascending = self._build_ascending_list()
|
|
162
|
+
|
|
163
|
+
if len(self.__by) != len(self.__ascending):
|
|
164
|
+
raise ValueError(
|
|
165
|
+
"The number of columns in 'by' must equal the number of values in 'ascending' for sort_values function."
|
|
166
|
+
)
|
|
167
|
+
|
|
168
|
+
for column in self.__by:
|
|
169
|
+
if column not in base_frame_columns:
|
|
170
|
+
raise ValueError(
|
|
171
|
+
f"Column - '{column}' in sort_values columns list doesn't exist in the current frame. "
|
|
172
|
+
f"Current frame columns: {base_frame_columns}"
|
|
173
|
+
)
|
|
174
|
+
|
|
175
|
+
return True
|
|
176
|
+
|
|
177
|
+
def _build_by_list(self) -> PyLegendList[str]:
|
|
178
|
+
if isinstance(self.__by_input, str):
|
|
179
|
+
return [self.__by_input]
|
|
180
|
+
else:
|
|
181
|
+
return self.__by_input
|
|
182
|
+
|
|
183
|
+
def _build_ascending_list(self) -> PyLegendList[bool]:
|
|
184
|
+
if self.__ascending_input is True:
|
|
185
|
+
return [True for _ in self.__by]
|
|
186
|
+
elif self.__ascending_input is False:
|
|
187
|
+
return [False for _ in self.__by]
|
|
188
|
+
else:
|
|
189
|
+
return self.__ascending_input
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
# Copyright 2025 Goldman Sachs
|
|
2
|
+
#
|
|
3
|
+
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
4
|
+
# you may not use this file except in compliance with the License.
|
|
5
|
+
# You may obtain a copy of the License at
|
|
6
|
+
#
|
|
7
|
+
# http://www.apache.org/licenses/LICENSE-2.0
|
|
8
|
+
#
|
|
9
|
+
# Unless required by applicable law or agreed to in writing, software
|
|
10
|
+
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
11
|
+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
12
|
+
# See the License for the specific language governing permissions and
|
|
13
|
+
# limitations under the License.
|
|
14
|
+
|
|
15
|
+
|
|
16
|
+
from datetime import date
|
|
17
|
+
from pylegend._typing import (
|
|
18
|
+
PyLegendList,
|
|
19
|
+
PyLegendSequence,
|
|
20
|
+
PyLegendUnion,
|
|
21
|
+
)
|
|
22
|
+
from pylegend.core.sql.metamodel import LongLiteral, QuerySpecification
|
|
23
|
+
from pylegend.core.tds.pandas_api.frames.pandas_api_applied_function_tds_frame import PandasApiAppliedFunction
|
|
24
|
+
from pylegend.core.tds.pandas_api.frames.pandas_api_base_tds_frame import PandasApiBaseTdsFrame
|
|
25
|
+
from pylegend.core.tds.sql_query_helpers import create_sub_query, copy_query
|
|
26
|
+
from pylegend.core.tds.tds_column import TdsColumn
|
|
27
|
+
from pylegend.core.tds.tds_frame import FrameToPureConfig, FrameToSqlConfig
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
class TruncateFunction(PandasApiAppliedFunction):
|
|
31
|
+
__base_frame: PandasApiBaseTdsFrame
|
|
32
|
+
__before: int
|
|
33
|
+
__after: PyLegendUnion[int, None]
|
|
34
|
+
__axis: PyLegendUnion[str, int]
|
|
35
|
+
__copy: bool
|
|
36
|
+
|
|
37
|
+
@classmethod
|
|
38
|
+
def name(cls) -> str:
|
|
39
|
+
return "truncate" # pragma: no cover
|
|
40
|
+
|
|
41
|
+
def __init__(
|
|
42
|
+
self,
|
|
43
|
+
base_frame: PandasApiBaseTdsFrame,
|
|
44
|
+
before: PyLegendUnion[date, str, int, None],
|
|
45
|
+
after: PyLegendUnion[date, str, int, None],
|
|
46
|
+
axis: PyLegendUnion[str, int],
|
|
47
|
+
copy: bool,
|
|
48
|
+
) -> None:
|
|
49
|
+
self.__base_frame = base_frame
|
|
50
|
+
self.__before_input = before
|
|
51
|
+
self.__after_input = after
|
|
52
|
+
self.__axis = axis
|
|
53
|
+
self.__copy = copy
|
|
54
|
+
|
|
55
|
+
def to_sql(self, config: FrameToSqlConfig) -> QuerySpecification:
|
|
56
|
+
base_query: QuerySpecification = self.__base_frame.to_sql_query_object(config)
|
|
57
|
+
should_create_sub_query = (base_query.offset is not None) or (base_query.limit is not None)
|
|
58
|
+
new_query = create_sub_query(base_query, config, "root") if should_create_sub_query else copy_query(base_query)
|
|
59
|
+
new_query.offset = LongLiteral(self.__before)
|
|
60
|
+
if self.__after is not None:
|
|
61
|
+
new_query.limit = LongLiteral(self.__after - self.__before + 1)
|
|
62
|
+
return new_query
|
|
63
|
+
|
|
64
|
+
def to_pure(self, config: FrameToPureConfig) -> str:
|
|
65
|
+
if self.__after is None:
|
|
66
|
+
return f"{self.__base_frame.to_pure(config)}{config.separator(1)}" f"->drop({self.__before})"
|
|
67
|
+
|
|
68
|
+
start_row = self.__before
|
|
69
|
+
end_row = self.__after + 1
|
|
70
|
+
return f"{self.__base_frame.to_pure(config)}{config.separator(1)}" f"->slice({start_row}, {end_row})"
|
|
71
|
+
|
|
72
|
+
def base_frame(self) -> PandasApiBaseTdsFrame:
|
|
73
|
+
return self.__base_frame
|
|
74
|
+
|
|
75
|
+
def tds_frame_parameters(self) -> PyLegendList["PandasApiBaseTdsFrame"]:
|
|
76
|
+
return []
|
|
77
|
+
|
|
78
|
+
def calculate_columns(self) -> PyLegendSequence["TdsColumn"]:
|
|
79
|
+
return [c.copy() for c in self.__base_frame.columns()]
|
|
80
|
+
|
|
81
|
+
def validate(self) -> bool:
|
|
82
|
+
if self.__axis not in [0, "index"]:
|
|
83
|
+
raise NotImplementedError(
|
|
84
|
+
f"The 'axis' parameter of the truncate function must be 0 or 'index', but got: {self.__axis}"
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
if self.__copy not in [True]:
|
|
88
|
+
raise NotImplementedError(f"The 'copy' parameter of the truncate function must be True, but got: {self.__copy}")
|
|
89
|
+
|
|
90
|
+
if self.__before_input is None:
|
|
91
|
+
self.__before = 0
|
|
92
|
+
else:
|
|
93
|
+
self.__before = self.get_positive_integer_or_raise_exception(self.__before_input, variable_name="before")
|
|
94
|
+
|
|
95
|
+
if self.__after_input is None:
|
|
96
|
+
self.__after = None
|
|
97
|
+
return True
|
|
98
|
+
else:
|
|
99
|
+
self.__after = self.get_positive_integer_or_raise_exception(self.__after_input, variable_name="after")
|
|
100
|
+
|
|
101
|
+
if self.__before > self.__after:
|
|
102
|
+
raise ValueError(
|
|
103
|
+
f"The 'before' parameter of the truncate function must be less than or equal to the 'after' parameter, "
|
|
104
|
+
f"but got: before={self.__before}, after={self.__after}"
|
|
105
|
+
)
|
|
106
|
+
|
|
107
|
+
return True
|
|
108
|
+
|
|
109
|
+
def get_positive_integer_or_raise_exception(
|
|
110
|
+
self, variable: PyLegendUnion[date, str, int, None], variable_name: str
|
|
111
|
+
) -> int:
|
|
112
|
+
if type(variable) is not int:
|
|
113
|
+
raise NotImplementedError(
|
|
114
|
+
f"The '{variable_name}' parameter of the truncate function must be an integer, "
|
|
115
|
+
f"but got: {variable} (type: {type(variable).__name__})"
|
|
116
|
+
)
|
|
117
|
+
|
|
118
|
+
if variable < 0:
|
|
119
|
+
return 0
|
|
120
|
+
return variable
|
|
@@ -40,6 +40,10 @@ class PandasApiAppliedFunction(metaclass=ABCMeta):
|
|
|
40
40
|
def to_sql(self, config: FrameToSqlConfig) -> QuerySpecification:
|
|
41
41
|
pass # pragma: no cover
|
|
42
42
|
|
|
43
|
+
@abstractmethod
|
|
44
|
+
def to_pure(self, config: FrameToPureConfig) -> str:
|
|
45
|
+
pass # pragma: no cover
|
|
46
|
+
|
|
43
47
|
@abstractmethod
|
|
44
48
|
def base_frame(self) -> PandasApiBaseTdsFrame:
|
|
45
49
|
pass # pragma: no cover
|
|
@@ -69,7 +73,7 @@ class PandasApiAppliedFunctionTdsFrame(PandasApiBaseTdsFrame):
|
|
|
69
73
|
return self.__applied_function.to_sql(config)
|
|
70
74
|
|
|
71
75
|
def to_pure(self, config: FrameToPureConfig) -> str:
|
|
72
|
-
|
|
76
|
+
return self.__applied_function.to_pure(config)
|
|
73
77
|
|
|
74
78
|
def get_all_tds_frames(self) -> PyLegendList["PandasApiBaseTdsFrame"]:
|
|
75
79
|
return [
|
|
@@ -14,34 +14,46 @@
|
|
|
14
14
|
|
|
15
15
|
from abc import ABCMeta, abstractmethod
|
|
16
16
|
from datetime import date, datetime
|
|
17
|
+
from typing import TYPE_CHECKING
|
|
18
|
+
|
|
17
19
|
import pandas as pd
|
|
20
|
+
|
|
18
21
|
from pylegend._typing import (
|
|
19
22
|
PyLegendSequence,
|
|
20
23
|
PyLegendTypeVar,
|
|
21
24
|
PyLegendList,
|
|
25
|
+
PyLegendSet,
|
|
22
26
|
PyLegendOptional,
|
|
23
27
|
PyLegendCallable,
|
|
24
28
|
PyLegendUnion,
|
|
25
29
|
)
|
|
26
|
-
from pylegend.core.sql.metamodel import QuerySpecification
|
|
27
30
|
from pylegend.core.database.sql_to_string import (
|
|
28
31
|
SqlToStringConfig,
|
|
29
32
|
SqlToStringFormat
|
|
30
33
|
)
|
|
31
|
-
from pylegend.core.language import PyLegendPrimitive,
|
|
34
|
+
from pylegend.core.language import PyLegendPrimitive, PyLegendInteger, PyLegendBoolean
|
|
35
|
+
from pylegend.core.language.pandas_api.pandas_api_aggregate_specification import PyLegendAggInput
|
|
36
|
+
from pylegend.core.language.pandas_api.pandas_api_tds_row import PandasApiTdsRow
|
|
37
|
+
from pylegend.core.language.shared.primitives.primitive import PyLegendPrimitiveOrPythonPrimitive
|
|
38
|
+
from pylegend.core.language.shared.tds_row import AbstractTdsRow
|
|
39
|
+
from pylegend.core.sql.metamodel import QuerySpecification
|
|
40
|
+
from pylegend.core.tds.abstract.frames.base_tds_frame import BaseTdsFrame
|
|
32
41
|
from pylegend.core.tds.pandas_api.frames.pandas_api_tds_frame import PandasApiTdsFrame
|
|
33
|
-
from pylegend.core.tds.tds_column import TdsColumn
|
|
34
|
-
from pylegend.core.tds.tds_frame import FrameToSqlConfig
|
|
35
|
-
from pylegend.core.tds.tds_frame import FrameToPureConfig
|
|
36
42
|
from pylegend.core.tds.result_handler import (
|
|
37
43
|
ResultHandler,
|
|
38
44
|
ToStringResultHandler,
|
|
39
45
|
)
|
|
46
|
+
from pylegend.core.tds.tds_column import TdsColumn
|
|
47
|
+
from pylegend.core.tds.tds_frame import FrameToPureConfig
|
|
48
|
+
from pylegend.core.tds.tds_frame import FrameToSqlConfig
|
|
40
49
|
from pylegend.extensions.tds.result_handler import (
|
|
41
50
|
ToPandasDfResultHandler,
|
|
42
51
|
PandasDfReadConfig,
|
|
43
52
|
)
|
|
44
53
|
|
|
54
|
+
if TYPE_CHECKING:
|
|
55
|
+
from pylegend.core.language.pandas_api.pandas_api_series import Series
|
|
56
|
+
|
|
45
57
|
__all__: PyLegendSequence[str] = [
|
|
46
58
|
"PandasApiBaseTdsFrame"
|
|
47
59
|
]
|
|
@@ -49,7 +61,7 @@ __all__: PyLegendSequence[str] = [
|
|
|
49
61
|
R = PyLegendTypeVar('R')
|
|
50
62
|
|
|
51
63
|
|
|
52
|
-
class PandasApiBaseTdsFrame(PandasApiTdsFrame, metaclass=ABCMeta):
|
|
64
|
+
class PandasApiBaseTdsFrame(PandasApiTdsFrame, BaseTdsFrame, metaclass=ABCMeta):
|
|
53
65
|
__columns: PyLegendSequence[TdsColumn]
|
|
54
66
|
|
|
55
67
|
def __init__(self, columns: PyLegendSequence[TdsColumn]) -> None:
|
|
@@ -62,10 +74,63 @@ class PandasApiBaseTdsFrame(PandasApiTdsFrame, metaclass=ABCMeta):
|
|
|
62
74
|
def columns(self) -> PyLegendSequence[TdsColumn]:
|
|
63
75
|
return [c.copy() for c in self.__columns]
|
|
64
76
|
|
|
77
|
+
def __getitem__(
|
|
78
|
+
self,
|
|
79
|
+
key: PyLegendUnion[str, PyLegendList[str], PyLegendBoolean]
|
|
80
|
+
) -> PyLegendUnion["PandasApiTdsFrame", "Series"]:
|
|
81
|
+
from pylegend.core.tds.pandas_api.frames.pandas_api_applied_function_tds_frame import \
|
|
82
|
+
PandasApiAppliedFunctionTdsFrame
|
|
83
|
+
from pylegend.core.tds.pandas_api.frames.functions.filtering import \
|
|
84
|
+
PandasApiFilteringFunction
|
|
85
|
+
from pylegend.core.language.shared.primitives.boolean import PyLegendBoolean
|
|
86
|
+
|
|
87
|
+
if isinstance(key, PyLegendBoolean):
|
|
88
|
+
return PandasApiAppliedFunctionTdsFrame(
|
|
89
|
+
PandasApiFilteringFunction(self, filter_expr=key)
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
elif isinstance(key, str):
|
|
93
|
+
for col in self.__columns:
|
|
94
|
+
if col.get_name() == key:
|
|
95
|
+
col_type = col.get_type()
|
|
96
|
+
if col_type == "Boolean":
|
|
97
|
+
from pylegend.core.language.pandas_api.pandas_api_series import BooleanSeries # pragma: no cover
|
|
98
|
+
return BooleanSeries(self, key) # pragma: no cover (Boolean column not supported in PURE)
|
|
99
|
+
elif col_type == "String":
|
|
100
|
+
from pylegend.core.language.pandas_api.pandas_api_series import StringSeries
|
|
101
|
+
return StringSeries(self, key)
|
|
102
|
+
elif col_type == "Integer":
|
|
103
|
+
from pylegend.core.language.pandas_api.pandas_api_series import IntegerSeries
|
|
104
|
+
return IntegerSeries(self, key)
|
|
105
|
+
elif col_type == "Float":
|
|
106
|
+
from pylegend.core.language.pandas_api.pandas_api_series import FloatSeries
|
|
107
|
+
return FloatSeries(self, key)
|
|
108
|
+
elif col_type == "Date":
|
|
109
|
+
from pylegend.core.language.pandas_api.pandas_api_series import DateSeries
|
|
110
|
+
return DateSeries(self, key)
|
|
111
|
+
elif col_type == "DateTime":
|
|
112
|
+
from pylegend.core.language.pandas_api.pandas_api_series import DateTimeSeries
|
|
113
|
+
return DateTimeSeries(self, key)
|
|
114
|
+
elif col_type == "StrictDate":
|
|
115
|
+
from pylegend.core.language.pandas_api.pandas_api_series import StrictDateSeries
|
|
116
|
+
return StrictDateSeries(self, key)
|
|
117
|
+
else:
|
|
118
|
+
raise ValueError(f"Unsupported column type '{col_type}' for column '{key}'") # pragma: no cover
|
|
119
|
+
raise KeyError(f"['{key}'] not in index")
|
|
120
|
+
|
|
121
|
+
elif isinstance(key, list):
|
|
122
|
+
valid_col_names = {col.get_name() for col in self.__columns}
|
|
123
|
+
invalid_cols = [k for k in key if k not in valid_col_names]
|
|
124
|
+
if invalid_cols:
|
|
125
|
+
raise KeyError(f"{invalid_cols} not in index")
|
|
126
|
+
return self.filter(items=key)
|
|
127
|
+
else:
|
|
128
|
+
raise TypeError(f"Invalid key type: {type(key)}. Expected str, list, or boolean expression")
|
|
129
|
+
|
|
65
130
|
def assign(
|
|
66
131
|
self,
|
|
67
132
|
**kwargs: PyLegendCallable[
|
|
68
|
-
[
|
|
133
|
+
[PandasApiTdsRow],
|
|
69
134
|
PyLegendUnion[int, float, bool, str, date, datetime, PyLegendPrimitive]
|
|
70
135
|
],
|
|
71
136
|
) -> "PandasApiTdsFrame":
|
|
@@ -75,6 +140,138 @@ class PandasApiBaseTdsFrame(PandasApiTdsFrame, metaclass=ABCMeta):
|
|
|
75
140
|
from pylegend.core.tds.pandas_api.frames.functions.assign_function import AssignFunction
|
|
76
141
|
return PandasApiAppliedFunctionTdsFrame(AssignFunction(self, col_definitions=kwargs))
|
|
77
142
|
|
|
143
|
+
def filter(
|
|
144
|
+
self,
|
|
145
|
+
items: PyLegendOptional[PyLegendList[str]] = None,
|
|
146
|
+
like: PyLegendOptional[str] = None,
|
|
147
|
+
regex: PyLegendOptional[str] = None,
|
|
148
|
+
axis: PyLegendOptional[PyLegendUnion[str, int, PyLegendInteger]] = None
|
|
149
|
+
) -> "PandasApiTdsFrame":
|
|
150
|
+
from pylegend.core.tds.pandas_api.frames.pandas_api_applied_function_tds_frame import (
|
|
151
|
+
PandasApiAppliedFunctionTdsFrame
|
|
152
|
+
)
|
|
153
|
+
from pylegend.core.tds.pandas_api.frames.functions.filter import PandasApiFilterFunction
|
|
154
|
+
return PandasApiAppliedFunctionTdsFrame(
|
|
155
|
+
PandasApiFilterFunction(
|
|
156
|
+
self,
|
|
157
|
+
items=items,
|
|
158
|
+
like=like,
|
|
159
|
+
regex=regex,
|
|
160
|
+
axis=axis
|
|
161
|
+
)
|
|
162
|
+
)
|
|
163
|
+
|
|
164
|
+
def sort_values(
|
|
165
|
+
self,
|
|
166
|
+
by: PyLegendUnion[str, PyLegendList[str]],
|
|
167
|
+
axis: PyLegendUnion[str, int] = 0,
|
|
168
|
+
ascending: PyLegendUnion[bool, PyLegendList[bool]] = True,
|
|
169
|
+
inplace: bool = False,
|
|
170
|
+
kind: PyLegendOptional[str] = None,
|
|
171
|
+
na_position: str = 'last',
|
|
172
|
+
ignore_index: bool = True,
|
|
173
|
+
key: PyLegendOptional[PyLegendCallable[[AbstractTdsRow], AbstractTdsRow]] = None
|
|
174
|
+
) -> "PandasApiTdsFrame":
|
|
175
|
+
from pylegend.core.tds.pandas_api.frames.pandas_api_applied_function_tds_frame import (
|
|
176
|
+
PandasApiAppliedFunctionTdsFrame
|
|
177
|
+
)
|
|
178
|
+
from pylegend.core.tds.pandas_api.frames.functions.sort_values_function import SortValuesFunction
|
|
179
|
+
return PandasApiAppliedFunctionTdsFrame(SortValuesFunction(
|
|
180
|
+
base_frame=self,
|
|
181
|
+
by=by,
|
|
182
|
+
axis=axis,
|
|
183
|
+
ascending=ascending,
|
|
184
|
+
inplace=inplace,
|
|
185
|
+
kind=kind,
|
|
186
|
+
na_position=na_position,
|
|
187
|
+
ignore_index=ignore_index,
|
|
188
|
+
key=key
|
|
189
|
+
))
|
|
190
|
+
|
|
191
|
+
def truncate(
|
|
192
|
+
self,
|
|
193
|
+
before: PyLegendUnion[date, str, int, None] = None,
|
|
194
|
+
after: PyLegendUnion[date, str, int, None] = None,
|
|
195
|
+
axis: PyLegendUnion[str, int] = 0,
|
|
196
|
+
copy: bool = True
|
|
197
|
+
) -> "PandasApiTdsFrame":
|
|
198
|
+
from pylegend.core.tds.pandas_api.frames.pandas_api_applied_function_tds_frame import (
|
|
199
|
+
PandasApiAppliedFunctionTdsFrame
|
|
200
|
+
)
|
|
201
|
+
from pylegend.core.tds.pandas_api.frames.functions.truncate_function import TruncateFunction
|
|
202
|
+
return PandasApiAppliedFunctionTdsFrame(TruncateFunction(
|
|
203
|
+
base_frame=self,
|
|
204
|
+
before=before,
|
|
205
|
+
after=after,
|
|
206
|
+
axis=axis,
|
|
207
|
+
copy=copy
|
|
208
|
+
))
|
|
209
|
+
|
|
210
|
+
def drop(
|
|
211
|
+
self,
|
|
212
|
+
labels: PyLegendOptional[PyLegendUnion[str, PyLegendSequence[str], PyLegendSet[str]]] = None,
|
|
213
|
+
axis: PyLegendUnion[str, int, PyLegendInteger] = 1,
|
|
214
|
+
index: PyLegendOptional[PyLegendUnion[str, PyLegendSequence[str], PyLegendSet[str]]] = None,
|
|
215
|
+
columns: PyLegendOptional[PyLegendUnion[str, PyLegendSequence[str], PyLegendSet[str]]] = None,
|
|
216
|
+
level: PyLegendOptional[PyLegendUnion[int, PyLegendInteger, str]] = None,
|
|
217
|
+
inplace: PyLegendUnion[bool, PyLegendBoolean] = True,
|
|
218
|
+
errors: str = "raise",
|
|
219
|
+
) -> "PandasApiTdsFrame":
|
|
220
|
+
from pylegend.core.tds.pandas_api.frames.pandas_api_applied_function_tds_frame import \
|
|
221
|
+
PandasApiAppliedFunctionTdsFrame
|
|
222
|
+
from pylegend.core.tds.pandas_api.frames.functions.drop import PandasApiDropFunction
|
|
223
|
+
|
|
224
|
+
return PandasApiAppliedFunctionTdsFrame(
|
|
225
|
+
PandasApiDropFunction(
|
|
226
|
+
base_frame=self,
|
|
227
|
+
labels=labels,
|
|
228
|
+
axis=axis,
|
|
229
|
+
index=index,
|
|
230
|
+
columns=columns,
|
|
231
|
+
level=level,
|
|
232
|
+
inplace=inplace,
|
|
233
|
+
errors=errors
|
|
234
|
+
)
|
|
235
|
+
)
|
|
236
|
+
|
|
237
|
+
def aggregate(
|
|
238
|
+
self,
|
|
239
|
+
func: PyLegendAggInput,
|
|
240
|
+
axis: PyLegendUnion[int, str] = 0,
|
|
241
|
+
*args: PyLegendPrimitiveOrPythonPrimitive,
|
|
242
|
+
**kwargs: PyLegendPrimitiveOrPythonPrimitive
|
|
243
|
+
) -> "PandasApiTdsFrame":
|
|
244
|
+
from pylegend.core.tds.pandas_api.frames.pandas_api_applied_function_tds_frame import (
|
|
245
|
+
PandasApiAppliedFunctionTdsFrame
|
|
246
|
+
)
|
|
247
|
+
from pylegend.core.tds.pandas_api.frames.functions.aggregate_function import AggregateFunction
|
|
248
|
+
return PandasApiAppliedFunctionTdsFrame(AggregateFunction(
|
|
249
|
+
self,
|
|
250
|
+
func,
|
|
251
|
+
axis,
|
|
252
|
+
*args,
|
|
253
|
+
**kwargs
|
|
254
|
+
))
|
|
255
|
+
|
|
256
|
+
def agg(
|
|
257
|
+
self,
|
|
258
|
+
func: PyLegendAggInput,
|
|
259
|
+
axis: PyLegendUnion[int, str] = 0,
|
|
260
|
+
*args: PyLegendPrimitiveOrPythonPrimitive,
|
|
261
|
+
**kwargs: PyLegendPrimitiveOrPythonPrimitive
|
|
262
|
+
) -> "PandasApiTdsFrame":
|
|
263
|
+
from pylegend.core.tds.pandas_api.frames.pandas_api_applied_function_tds_frame import (
|
|
264
|
+
PandasApiAppliedFunctionTdsFrame
|
|
265
|
+
)
|
|
266
|
+
from pylegend.core.tds.pandas_api.frames.functions.aggregate_function import AggregateFunction
|
|
267
|
+
return PandasApiAppliedFunctionTdsFrame(AggregateFunction(
|
|
268
|
+
self,
|
|
269
|
+
func,
|
|
270
|
+
axis,
|
|
271
|
+
*args,
|
|
272
|
+
**kwargs
|
|
273
|
+
))
|
|
274
|
+
|
|
78
275
|
@abstractmethod
|
|
79
276
|
def to_sql_query_object(self, config: FrameToSqlConfig) -> QuerySpecification:
|
|
80
277
|
pass # pragma: no cover
|
|
@@ -17,6 +17,8 @@ from pylegend._typing import (
|
|
|
17
17
|
PyLegendSequence,
|
|
18
18
|
PyLegendList
|
|
19
19
|
)
|
|
20
|
+
from pylegend.core.tds.abstract.frames.input_tds_frame import InputTdsFrame, ExecutableInputTdsFrame, \
|
|
21
|
+
NonExecutableInputTdsFrame
|
|
20
22
|
from pylegend.core.tds.tds_column import TdsColumn
|
|
21
23
|
from pylegend.core.tds.pandas_api.frames.pandas_api_base_tds_frame import PandasApiBaseTdsFrame
|
|
22
24
|
from pylegend.core.request.legend_client import LegendClient
|
|
@@ -29,7 +31,7 @@ __all__: PyLegendSequence[str] = [
|
|
|
29
31
|
]
|
|
30
32
|
|
|
31
33
|
|
|
32
|
-
class PandasApiInputTdsFrame(PandasApiBaseTdsFrame, metaclass=ABCMeta):
|
|
34
|
+
class PandasApiInputTdsFrame(PandasApiBaseTdsFrame, InputTdsFrame, metaclass=ABCMeta):
|
|
33
35
|
|
|
34
36
|
def __init__(self, columns: PyLegendSequence[TdsColumn]) -> None:
|
|
35
37
|
super().__init__(columns=columns)
|
|
@@ -38,7 +40,7 @@ class PandasApiInputTdsFrame(PandasApiBaseTdsFrame, metaclass=ABCMeta):
|
|
|
38
40
|
return [self]
|
|
39
41
|
|
|
40
42
|
|
|
41
|
-
class PandasApiExecutableInputTdsFrame(PandasApiInputTdsFrame, metaclass=ABCMeta):
|
|
43
|
+
class PandasApiExecutableInputTdsFrame(PandasApiInputTdsFrame, ExecutableInputTdsFrame, metaclass=ABCMeta):
|
|
42
44
|
__legend_client: LegendClient
|
|
43
45
|
|
|
44
46
|
def __init__(self, legend_client: LegendClient, columns: PyLegendSequence[TdsColumn]) -> None:
|
|
@@ -49,7 +51,7 @@ class PandasApiExecutableInputTdsFrame(PandasApiInputTdsFrame, metaclass=ABCMeta
|
|
|
49
51
|
return self.__legend_client
|
|
50
52
|
|
|
51
53
|
|
|
52
|
-
class PandasApiNonExecutableInputTdsFrame(PandasApiInputTdsFrame, metaclass=ABCMeta):
|
|
54
|
+
class PandasApiNonExecutableInputTdsFrame(PandasApiInputTdsFrame, NonExecutableInputTdsFrame, metaclass=ABCMeta):
|
|
53
55
|
|
|
54
56
|
def __init__(self, columns: PyLegendSequence[TdsColumn]) -> None:
|
|
55
57
|
super().__init__(columns=columns)
|