pylegend 0.3.0__py3-none-any.whl → 0.4.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/__init__.py +7 -5
- pylegend/core/{databse → database}/sql_to_string/__init__.py +3 -3
- pylegend/core/{databse → database}/sql_to_string/db_extension.py +11 -5
- pylegend/core/{databse → database}/sql_to_string/generator.py +2 -2
- pylegend/core/language/__init__.py +10 -10
- pylegend/core/language/legacy_api/__init__.py +13 -0
- pylegend/core/language/{aggregate_specification.py → legacy_api/aggregate_specification.py} +10 -10
- pylegend/core/language/legacy_api/legacy_api_tds_row.py +32 -0
- pylegend/core/language/legendql_api/__init__.py +13 -0
- pylegend/core/language/legendql_api/legendql_api_custom_expressions.py +541 -0
- pylegend/core/language/legendql_api/legendql_api_tds_row.py +292 -0
- pylegend/core/language/shared/__init__.py +13 -0
- pylegend/core/language/{column_expressions.py → shared/column_expressions.py} +32 -31
- pylegend/core/language/{expression.py → shared/expression.py} +8 -0
- pylegend/core/language/{functions.py → shared/functions.py} +3 -3
- pylegend/core/language/shared/helpers.py +75 -0
- pylegend/core/language/{literal_expressions.py → shared/literal_expressions.py} +39 -1
- pylegend/core/language/{operations → shared/operations}/binary_expression.py +34 -2
- pylegend/core/language/{operations → shared/operations}/boolean_operation_expressions.py +34 -6
- pylegend/core/language/{operations → shared/operations}/collection_operation_expressions.py +146 -26
- pylegend/core/language/{operations → shared/operations}/date_operation_expressions.py +164 -24
- pylegend/core/language/{operations → shared/operations}/float_operation_expressions.py +53 -8
- pylegend/core/language/{operations → shared/operations}/integer_operation_expressions.py +62 -9
- pylegend/core/language/{operations → shared/operations}/nullary_expression.py +9 -2
- pylegend/core/language/{operations → shared/operations}/number_operation_expressions.py +211 -30
- pylegend/core/language/{operations → shared/operations}/primitive_operation_expressions.py +42 -3
- pylegend/core/language/{operations → shared/operations}/string_operation_expressions.py +169 -21
- pylegend/core/language/{operations → shared/operations}/unary_expression.py +10 -2
- pylegend/core/language/{primitive_collection.py → shared/primitive_collection.py} +2 -2
- pylegend/core/language/{primitives → shared/primitives}/__init__.py +9 -9
- pylegend/core/language/{primitives → shared/primitives}/boolean.py +9 -5
- pylegend/core/language/{primitives → shared/primitives}/date.py +23 -15
- pylegend/core/language/{primitives → shared/primitives}/datetime.py +4 -5
- pylegend/core/language/{primitives → shared/primitives}/float.py +6 -6
- pylegend/core/language/{primitives → shared/primitives}/integer.py +6 -6
- pylegend/core/language/{primitives → shared/primitives}/number.py +16 -13
- pylegend/core/language/{primitives → shared/primitives}/primitive.py +25 -5
- pylegend/core/language/{primitives → shared/primitives}/strictdate.py +4 -5
- pylegend/core/language/{primitives → shared/primitives}/string.py +18 -19
- pylegend/core/language/{tds_row.py → shared/tds_row.py} +46 -16
- pylegend/core/request/__init__.py +7 -1
- pylegend/core/request/auth.py +55 -1
- pylegend/core/request/legend_client.py +32 -0
- pylegend/core/sql/metamodel_extension.py +16 -0
- pylegend/core/tds/abstract/__init__.py +13 -0
- pylegend/core/tds/abstract/frames/__init__.py +13 -0
- pylegend/core/tds/{legend_api/frames/legend_api_applied_function_tds_frame.py → abstract/frames/applied_function_tds_frame.py} +19 -13
- pylegend/core/tds/abstract/frames/base_tds_frame.py +125 -0
- pylegend/core/tds/{legend_api/frames/legend_api_input_tds_frame.py → abstract/frames/input_tds_frame.py} +9 -12
- pylegend/core/tds/{legend_api/frames/functions → abstract}/function_helpers.py +1 -1
- pylegend/core/tds/{legend_api/frames/functions/concatenate_function.py → legacy_api/frames/functions/legacy_api_concatenate_function.py} +25 -13
- pylegend/core/tds/{legend_api/frames/functions/distinct_function.py → legacy_api/frames/functions/legacy_api_distinct_function.py} +13 -8
- pylegend/core/tds/{legend_api/frames/functions/drop_function.py → legacy_api/frames/functions/legacy_api_drop_function.py} +13 -8
- pylegend/core/tds/{legend_api/frames/functions/extend_function.py → legacy_api/frames/functions/legacy_api_extend_function.py} +36 -16
- pylegend/core/tds/{legend_api/frames/functions/filter_function.py → legacy_api/frames/functions/legacy_api_filter_function.py} +25 -13
- pylegend/core/tds/{legend_api/frames/functions/group_by_function.py → legacy_api/frames/functions/legacy_api_group_by_function.py} +44 -17
- pylegend/core/tds/{legend_api/frames/functions/head_function.py → legacy_api/frames/functions/legacy_api_head_function.py} +13 -8
- pylegend/core/tds/{legend_api/frames/functions/join_by_columns_function.py → legacy_api/frames/functions/legacy_api_join_by_columns_function.py} +40 -13
- pylegend/core/tds/{legend_api/frames/functions/join_function.py → legacy_api/frames/functions/legacy_api_join_function.py} +44 -20
- pylegend/core/tds/{legend_api/frames/functions/rename_columns_function.py → legacy_api/frames/functions/legacy_api_rename_columns_function.py} +20 -8
- pylegend/core/tds/{legend_api/frames/functions/restrict_function.py → legacy_api/frames/functions/legacy_api_restrict_function.py} +17 -8
- pylegend/core/tds/{legend_api/frames/functions/slice_function.py → legacy_api/frames/functions/legacy_api_slice_function.py} +13 -8
- pylegend/core/tds/{legend_api/frames/functions/sort_function.py → legacy_api/frames/functions/legacy_api_sort_function.py} +19 -8
- pylegend/core/tds/legacy_api/frames/legacy_api_applied_function_tds_frame.py +37 -0
- pylegend/core/tds/legacy_api/frames/legacy_api_base_tds_frame.py +204 -0
- pylegend/core/tds/legacy_api/frames/legacy_api_input_tds_frame.py +51 -0
- pylegend/core/tds/{legend_api/frames/legend_api_tds_frame.py → legacy_api/frames/legacy_api_tds_frame.py} +28 -28
- pylegend/core/tds/legendql_api/__init__.py +13 -0
- pylegend/core/tds/legendql_api/frames/__init__.py +13 -0
- pylegend/core/tds/legendql_api/frames/functions/__init__.py +13 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_asofjoin_function.py +156 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_concatenate_function.py +139 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_distinct_function.py +69 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_drop_function.py +74 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_extend_function.py +256 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_filter_function.py +121 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_function_helpers.py +137 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_groupby_function.py +256 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_head_function.py +74 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_join_function.py +214 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_project_function.py +169 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_rename_function.py +189 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_select_function.py +131 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_slice_function.py +82 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_sort_function.py +93 -0
- pylegend/core/tds/legendql_api/frames/functions/legendql_api_window_extend_function.py +283 -0
- pylegend/core/tds/legendql_api/frames/legendql_api_applied_function_tds_frame.py +37 -0
- pylegend/core/tds/legendql_api/frames/legendql_api_base_tds_frame.py +419 -0
- pylegend/core/tds/legendql_api/frames/legendql_api_input_tds_frame.py +50 -0
- pylegend/core/tds/legendql_api/frames/legendql_api_tds_frame.py +327 -0
- pylegend/core/tds/pandas_api/frames/functions/assign_function.py +6 -6
- pylegend/core/tds/pandas_api/frames/pandas_api_applied_function_tds_frame.py +4 -0
- pylegend/core/tds/pandas_api/frames/pandas_api_base_tds_frame.py +11 -3
- pylegend/core/tds/pandas_api/frames/pandas_api_tds_frame.py +2 -2
- pylegend/core/tds/tds_frame.py +32 -2
- pylegend/extensions/database/vendors/postgres/postgres_sql_to_string.py +1 -1
- pylegend/extensions/tds/abstract/legend_function_input_frame.py +4 -0
- pylegend/extensions/tds/abstract/legend_service_input_frame.py +4 -0
- pylegend/extensions/tds/abstract/table_spec_input_frame.py +4 -0
- pylegend/extensions/tds/{legend_api/frames/legend_api_legend_function_input_frame.py → legacy_api/frames/legacy_api_legend_function_input_frame.py} +5 -5
- pylegend/extensions/tds/{legend_api/frames/legend_api_legend_service_input_frame.py → legacy_api/frames/legacy_api_legend_service_input_frame.py} +6 -6
- pylegend/extensions/tds/{legend_api/frames/legend_api_table_spec_input_frame.py → legacy_api/frames/legacy_api_table_spec_input_frame.py} +5 -5
- pylegend/extensions/tds/legendql_api/__init__.py +13 -0
- pylegend/extensions/tds/legendql_api/frames/__init__.py +13 -0
- pylegend/extensions/tds/legendql_api/frames/legendql_api_legend_service_input_frame.py +46 -0
- pylegend/extensions/tds/legendql_api/frames/legendql_api_table_spec_input_frame.py +36 -0
- pylegend/{legend_api_tds_client.py → legacy_api_tds_client.py} +15 -15
- {pylegend-0.3.0.dist-info → pylegend-0.4.0.dist-info}/METADATA +7 -8
- pylegend-0.4.0.dist-info/NOTICE +5 -0
- pylegend-0.4.0.dist-info/RECORD +155 -0
- {pylegend-0.3.0.dist-info → pylegend-0.4.0.dist-info}/WHEEL +1 -1
- pylegend/core/tds/legend_api/frames/legend_api_base_tds_frame.py +0 -294
- pylegend-0.3.0.dist-info/RECORD +0 -115
- /pylegend/core/{databse → database}/__init__.py +0 -0
- /pylegend/core/{databse → database}/sql_to_string/config.py +0 -0
- /pylegend/core/language/{operations → shared/operations}/__init__.py +0 -0
- /pylegend/core/tds/{legend_api → legacy_api}/__init__.py +0 -0
- /pylegend/core/tds/{legend_api → legacy_api}/frames/__init__.py +0 -0
- /pylegend/core/tds/{legend_api → legacy_api}/frames/functions/__init__.py +0 -0
- /pylegend/extensions/tds/{legend_api → legacy_api}/__init__.py +0 -0
- /pylegend/extensions/tds/{legend_api → legacy_api}/frames/__init__.py +0 -0
- {pylegend-0.3.0.dist-info → pylegend-0.4.0.dist-info}/LICENSE +0 -0
- {pylegend-0.3.0.dist-info → pylegend-0.4.0.dist-info}/LICENSE.spdx +0 -0
|
@@ -0,0 +1,169 @@
|
|
|
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 datetime import date, datetime
|
|
16
|
+
from pylegend._typing import (
|
|
17
|
+
PyLegendList,
|
|
18
|
+
PyLegendSequence,
|
|
19
|
+
PyLegendCallable,
|
|
20
|
+
PyLegendUnion,
|
|
21
|
+
PyLegendTuple,
|
|
22
|
+
)
|
|
23
|
+
from pylegend.core.language.legendql_api.legendql_api_tds_row import LegendQLApiTdsRow
|
|
24
|
+
from pylegend.core.tds.legendql_api.frames.legendql_api_applied_function_tds_frame import LegendQLApiAppliedFunction
|
|
25
|
+
from pylegend.core.tds.sql_query_helpers import create_sub_query
|
|
26
|
+
from pylegend.core.sql.metamodel import (
|
|
27
|
+
QuerySpecification,
|
|
28
|
+
SingleColumn,
|
|
29
|
+
SelectItem,
|
|
30
|
+
)
|
|
31
|
+
from pylegend.core.tds.tds_column import TdsColumn
|
|
32
|
+
from pylegend.core.tds.tds_frame import FrameToSqlConfig
|
|
33
|
+
from pylegend.core.tds.tds_frame import FrameToPureConfig
|
|
34
|
+
from pylegend.core.tds.legendql_api.frames.legendql_api_base_tds_frame import LegendQLApiBaseTdsFrame
|
|
35
|
+
from pylegend.core.language import (
|
|
36
|
+
PyLegendPrimitive,
|
|
37
|
+
PyLegendPrimitiveOrPythonPrimitive,
|
|
38
|
+
convert_literal_to_literal_expression,
|
|
39
|
+
)
|
|
40
|
+
from pylegend.core.language.shared.helpers import generate_pure_lambda, escape_column_name
|
|
41
|
+
from pylegend.core.tds.abstract.function_helpers import tds_column_for_primitive
|
|
42
|
+
|
|
43
|
+
__all__: PyLegendSequence[str] = [
|
|
44
|
+
"LegendQLApiProjectFunction"
|
|
45
|
+
]
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
class LegendQLApiProjectFunction(LegendQLApiAppliedFunction):
|
|
49
|
+
__base_frame: LegendQLApiBaseTdsFrame
|
|
50
|
+
__new_column_expressions: PyLegendList[PyLegendTuple[str, PyLegendPrimitiveOrPythonPrimitive]]
|
|
51
|
+
|
|
52
|
+
@classmethod
|
|
53
|
+
def name(cls) -> str:
|
|
54
|
+
return "project"
|
|
55
|
+
|
|
56
|
+
def __init__(
|
|
57
|
+
self,
|
|
58
|
+
base_frame: LegendQLApiBaseTdsFrame,
|
|
59
|
+
project_columns: PyLegendUnion[
|
|
60
|
+
PyLegendTuple[
|
|
61
|
+
str,
|
|
62
|
+
PyLegendCallable[[LegendQLApiTdsRow], PyLegendPrimitiveOrPythonPrimitive]
|
|
63
|
+
],
|
|
64
|
+
PyLegendList[
|
|
65
|
+
PyLegendTuple[
|
|
66
|
+
str,
|
|
67
|
+
PyLegendCallable[[LegendQLApiTdsRow], PyLegendPrimitiveOrPythonPrimitive]
|
|
68
|
+
]
|
|
69
|
+
]
|
|
70
|
+
]
|
|
71
|
+
) -> None:
|
|
72
|
+
self.__base_frame = base_frame
|
|
73
|
+
col_expressions: PyLegendList[PyLegendTuple[str, PyLegendPrimitiveOrPythonPrimitive]] = []
|
|
74
|
+
tds_row = LegendQLApiTdsRow.from_tds_frame("r", self.__base_frame)
|
|
75
|
+
for (i, project_col) in enumerate(project_columns if isinstance(project_columns, list) else [project_columns]):
|
|
76
|
+
if isinstance(project_col, tuple) and (len(project_col) == 2):
|
|
77
|
+
if not isinstance(project_col[0], str):
|
|
78
|
+
raise TypeError(
|
|
79
|
+
"'project' function project_columns argument incompatible. "
|
|
80
|
+
"First element in an project tuple should be a string (new column name). "
|
|
81
|
+
"E.g - ('new col', lambda r: r.c1 + 1). "
|
|
82
|
+
f"Element at index {i} (0-indexed) is incompatible"
|
|
83
|
+
)
|
|
84
|
+
if not isinstance(project_col[1], type(lambda x: 0)) or (project_col[1].__code__.co_argcount != 1):
|
|
85
|
+
raise TypeError(
|
|
86
|
+
"'project' function project_columns argument incompatible. "
|
|
87
|
+
"Second element in an project tuple should be a lambda function which takes one argument "
|
|
88
|
+
"(LegendQLApiTdsRow) E.g - ('new col', lambda r: r.c1 + 1)."
|
|
89
|
+
f"Element at index {i} (0-indexed) is incompatible"
|
|
90
|
+
)
|
|
91
|
+
try:
|
|
92
|
+
result = project_col[1](tds_row)
|
|
93
|
+
except Exception as e:
|
|
94
|
+
raise RuntimeError(
|
|
95
|
+
"'project' function project_columns argument incompatible. "
|
|
96
|
+
f"Error occurred while evaluating project lambda at index {i} (0-indexed). Message: " + str(e)
|
|
97
|
+
) from e
|
|
98
|
+
|
|
99
|
+
if not isinstance(result, (int, float, bool, str, date, datetime, PyLegendPrimitive)):
|
|
100
|
+
raise TypeError(
|
|
101
|
+
"'project' function project_columns argument incompatible. "
|
|
102
|
+
f"Project lambda at index {i} (0-indexed) returns non-primitive - {str(type(result))}"
|
|
103
|
+
)
|
|
104
|
+
col_expressions.append((project_col[0], result))
|
|
105
|
+
else:
|
|
106
|
+
raise TypeError(
|
|
107
|
+
"'project' function project_columns argument should be a list of tuples with two elements - "
|
|
108
|
+
"first element being a string (new column name), second element being a lambda "
|
|
109
|
+
"function which takes one argument (LegendQLApiTdsRow) "
|
|
110
|
+
"E.g - [('new col1', lambda r: r.c1 + 1), ('new col2', lambda r: r.c2)]. "
|
|
111
|
+
f"Element at index {i} (0-indexed) is incompatible"
|
|
112
|
+
)
|
|
113
|
+
self.__new_column_expressions = col_expressions
|
|
114
|
+
|
|
115
|
+
def to_sql(self, config: FrameToSqlConfig) -> QuerySpecification:
|
|
116
|
+
base_query = self.__base_frame.to_sql_query_object(config)
|
|
117
|
+
db_extension = config.sql_to_string_generator().get_db_extension()
|
|
118
|
+
new_query = create_sub_query(base_query, config, "root")
|
|
119
|
+
new_select_items: PyLegendList[SelectItem] = []
|
|
120
|
+
for c in self.__new_column_expressions:
|
|
121
|
+
if isinstance(c[1], (bool, int, float, str, date, datetime)):
|
|
122
|
+
col_sql_expr = convert_literal_to_literal_expression(c[1]).to_sql_expression(
|
|
123
|
+
{"r": new_query},
|
|
124
|
+
config
|
|
125
|
+
)
|
|
126
|
+
else:
|
|
127
|
+
col_sql_expr = c[1].to_sql_expression({"r": new_query}, config)
|
|
128
|
+
new_select_items.append(
|
|
129
|
+
SingleColumn(alias=db_extension.quote_identifier(c[0]), expression=col_sql_expr)
|
|
130
|
+
)
|
|
131
|
+
new_query.select.selectItems = new_select_items
|
|
132
|
+
return new_query
|
|
133
|
+
|
|
134
|
+
def to_pure(self, config: FrameToPureConfig) -> str:
|
|
135
|
+
def render_single_column_expression(
|
|
136
|
+
c: PyLegendUnion[
|
|
137
|
+
PyLegendTuple[str, PyLegendPrimitiveOrPythonPrimitive],
|
|
138
|
+
PyLegendTuple[str, PyLegendPrimitiveOrPythonPrimitive, PyLegendPrimitive]
|
|
139
|
+
]
|
|
140
|
+
) -> str:
|
|
141
|
+
escaped_col_name = escape_column_name(c[0])
|
|
142
|
+
expr_str = (c[1].to_pure_expression(config) if isinstance(c[1], PyLegendPrimitive) else
|
|
143
|
+
convert_literal_to_literal_expression(c[1]).to_pure_expression(config))
|
|
144
|
+
return f"{escaped_col_name}:{generate_pure_lambda('r', expr_str)}"
|
|
145
|
+
|
|
146
|
+
project_str = (f"->project(~[{config.separator(2)}" +
|
|
147
|
+
("," + config.separator(2, True)).join(
|
|
148
|
+
[render_single_column_expression(x) for x in self.__new_column_expressions]
|
|
149
|
+
) +
|
|
150
|
+
f"{config.separator(1)}])")
|
|
151
|
+
return f"{self.__base_frame.to_pure(config)}{config.separator(1)}" + project_str
|
|
152
|
+
|
|
153
|
+
def base_frame(self) -> LegendQLApiBaseTdsFrame:
|
|
154
|
+
return self.__base_frame
|
|
155
|
+
|
|
156
|
+
def tds_frame_parameters(self) -> PyLegendList["LegendQLApiBaseTdsFrame"]:
|
|
157
|
+
return []
|
|
158
|
+
|
|
159
|
+
def calculate_columns(self) -> PyLegendSequence["TdsColumn"]:
|
|
160
|
+
new_columns = []
|
|
161
|
+
for c in self.__new_column_expressions:
|
|
162
|
+
new_columns.append(tds_column_for_primitive(c[0], c[1] if len(c) == 2 else c[2]))
|
|
163
|
+
return new_columns
|
|
164
|
+
|
|
165
|
+
def validate(self) -> bool:
|
|
166
|
+
col_names = [x[0] for x in self.__new_column_expressions]
|
|
167
|
+
if len(col_names) != len(set(col_names)):
|
|
168
|
+
raise ValueError(f"Project column names list has duplicates: {col_names}")
|
|
169
|
+
return True
|
|
@@ -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
|
+
PyLegendList,
|
|
17
|
+
PyLegendSequence,
|
|
18
|
+
PyLegendCallable,
|
|
19
|
+
PyLegendUnion,
|
|
20
|
+
PyLegendTuple,
|
|
21
|
+
)
|
|
22
|
+
from pylegend.core.language import PyLegendColumnExpression
|
|
23
|
+
from pylegend.core.language.legendql_api.legendql_api_custom_expressions import LegendQLApiPrimitive
|
|
24
|
+
from pylegend.core.language.legendql_api.legendql_api_tds_row import LegendQLApiTdsRow
|
|
25
|
+
from pylegend.core.tds.legendql_api.frames.legendql_api_applied_function_tds_frame import LegendQLApiAppliedFunction
|
|
26
|
+
from pylegend.core.tds.sql_query_helpers import copy_query
|
|
27
|
+
from pylegend.core.sql.metamodel import (
|
|
28
|
+
QuerySpecification,
|
|
29
|
+
SingleColumn,
|
|
30
|
+
SelectItem,
|
|
31
|
+
)
|
|
32
|
+
from pylegend.core.tds.tds_column import TdsColumn
|
|
33
|
+
from pylegend.core.tds.tds_frame import FrameToSqlConfig
|
|
34
|
+
from pylegend.core.tds.tds_frame import FrameToPureConfig
|
|
35
|
+
from pylegend.core.tds.legendql_api.frames.legendql_api_base_tds_frame import LegendQLApiBaseTdsFrame
|
|
36
|
+
from pylegend.core.language.shared.helpers import escape_column_name
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
__all__: PyLegendSequence[str] = [
|
|
40
|
+
"LegendQLApiRenameFunction"
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class LegendQLApiRenameFunction(LegendQLApiAppliedFunction):
|
|
45
|
+
__base_frame: LegendQLApiBaseTdsFrame
|
|
46
|
+
__column_names: PyLegendList[str]
|
|
47
|
+
__renamed_column_names: PyLegendList[str]
|
|
48
|
+
|
|
49
|
+
@classmethod
|
|
50
|
+
def name(cls) -> str:
|
|
51
|
+
return "renameColumns"
|
|
52
|
+
|
|
53
|
+
def __init__(
|
|
54
|
+
self,
|
|
55
|
+
base_frame: LegendQLApiBaseTdsFrame,
|
|
56
|
+
column_renames: PyLegendUnion[
|
|
57
|
+
PyLegendTuple[str, str],
|
|
58
|
+
PyLegendList[PyLegendTuple[str, str]],
|
|
59
|
+
PyLegendCallable[
|
|
60
|
+
[LegendQLApiTdsRow],
|
|
61
|
+
PyLegendUnion[
|
|
62
|
+
PyLegendTuple[LegendQLApiPrimitive, str],
|
|
63
|
+
PyLegendList[PyLegendTuple[LegendQLApiPrimitive, str]]
|
|
64
|
+
]
|
|
65
|
+
]
|
|
66
|
+
]
|
|
67
|
+
) -> None:
|
|
68
|
+
self.__base_frame = base_frame
|
|
69
|
+
|
|
70
|
+
col_names: PyLegendList[str] = []
|
|
71
|
+
renamed_col_names: PyLegendList[str] = []
|
|
72
|
+
|
|
73
|
+
def rename_tuple_check(t): # type: ignore
|
|
74
|
+
return isinstance(t, tuple) and (len(t) == 2) and isinstance(t[0], str) and isinstance(t[1], str)
|
|
75
|
+
|
|
76
|
+
if rename_tuple_check(column_renames): # type: ignore
|
|
77
|
+
col_names.append(column_renames[0]) # type: ignore
|
|
78
|
+
renamed_col_names.append(column_renames[1]) # type: ignore
|
|
79
|
+
|
|
80
|
+
elif isinstance(column_renames, list) and all([rename_tuple_check(r) for r in column_renames]): # type: ignore
|
|
81
|
+
for t in column_renames:
|
|
82
|
+
col_names.append(t[0])
|
|
83
|
+
renamed_col_names.append(t[1])
|
|
84
|
+
|
|
85
|
+
elif isinstance(column_renames, type(lambda x: 0)) and (column_renames.__code__.co_argcount == 1):
|
|
86
|
+
tds_row = LegendQLApiTdsRow.from_tds_frame("frame", self.__base_frame)
|
|
87
|
+
try:
|
|
88
|
+
result = column_renames(tds_row)
|
|
89
|
+
except Exception as e:
|
|
90
|
+
raise RuntimeError(
|
|
91
|
+
"rename' function column_renames argument lambda incompatible. "
|
|
92
|
+
"Error occurred while evaluating. Message: " + str(e)
|
|
93
|
+
) from e
|
|
94
|
+
|
|
95
|
+
list_result = result if isinstance(result, list) else [result]
|
|
96
|
+
for (i, r) in enumerate(list_result):
|
|
97
|
+
if (isinstance(r, tuple) and (len(r) == 2) and
|
|
98
|
+
isinstance(r[0], LegendQLApiPrimitive) and isinstance(r[0].value(), PyLegendColumnExpression)
|
|
99
|
+
and isinstance(r[1], str)):
|
|
100
|
+
col_expr: PyLegendColumnExpression = r[0].value()
|
|
101
|
+
col_names.append(col_expr.get_column())
|
|
102
|
+
renamed_col_names.append(r[1])
|
|
103
|
+
else:
|
|
104
|
+
raise TypeError(
|
|
105
|
+
"'rename' function column_renames argument lambda incompatible. Each element in rename list "
|
|
106
|
+
"should be a tuple with first element being a simple column expression and "
|
|
107
|
+
"second element being a string (renamed column name) "
|
|
108
|
+
f"(E.g - lambda r: [(r.c1, 'c2')]). Element at index {i} (0-indexed) is incompatible"
|
|
109
|
+
)
|
|
110
|
+
|
|
111
|
+
else:
|
|
112
|
+
raise TypeError("'rename' function column_renames argument can either be a list of renaming tuples or "
|
|
113
|
+
"a lambda function which takes one argument (LegendQLApiTdsRow)")
|
|
114
|
+
|
|
115
|
+
self.__column_names = col_names
|
|
116
|
+
self.__renamed_column_names = renamed_col_names
|
|
117
|
+
|
|
118
|
+
def to_sql(self, config: FrameToSqlConfig) -> QuerySpecification:
|
|
119
|
+
base_query = self.__base_frame.to_sql_query_object(config)
|
|
120
|
+
|
|
121
|
+
db_extension = config.sql_to_string_generator().get_db_extension()
|
|
122
|
+
quoted_column_names_to_change = [db_extension.quote_identifier(s) for s in self.__column_names]
|
|
123
|
+
quoted_renamed_column_names = [db_extension.quote_identifier(s) for s in self.__renamed_column_names]
|
|
124
|
+
|
|
125
|
+
new_select_items: PyLegendList[SelectItem] = []
|
|
126
|
+
for col in base_query.select.selectItems:
|
|
127
|
+
if not isinstance(col, SingleColumn):
|
|
128
|
+
raise ValueError("Rename columns operation not supported for queries "
|
|
129
|
+
"with columns other than SingleColumn") # pragma: no cover
|
|
130
|
+
if col.alias is None:
|
|
131
|
+
raise ValueError("Rename columns operation not supported for queries "
|
|
132
|
+
"with SingleColumns with missing alias") # pragma: no cover
|
|
133
|
+
if col.alias in quoted_column_names_to_change:
|
|
134
|
+
new_alias = quoted_renamed_column_names[quoted_column_names_to_change.index(col.alias)]
|
|
135
|
+
new_select_items.append(SingleColumn(alias=new_alias, expression=col.expression))
|
|
136
|
+
else:
|
|
137
|
+
new_select_items.append(col)
|
|
138
|
+
|
|
139
|
+
new_query = copy_query(base_query)
|
|
140
|
+
new_query.select.selectItems = new_select_items
|
|
141
|
+
return new_query
|
|
142
|
+
|
|
143
|
+
def to_pure(self, config: FrameToPureConfig) -> str:
|
|
144
|
+
return (f"{self.__base_frame.to_pure(config)}{config.separator(1)}" +
|
|
145
|
+
f"{config.separator(1)}".join([
|
|
146
|
+
f"->rename(~{x}, ~{y})"
|
|
147
|
+
for x, y in zip(
|
|
148
|
+
map(escape_column_name, self.__column_names),
|
|
149
|
+
map(escape_column_name, self.__renamed_column_names)
|
|
150
|
+
)
|
|
151
|
+
]))
|
|
152
|
+
|
|
153
|
+
def base_frame(self) -> LegendQLApiBaseTdsFrame:
|
|
154
|
+
return self.__base_frame
|
|
155
|
+
|
|
156
|
+
def tds_frame_parameters(self) -> PyLegendList["LegendQLApiBaseTdsFrame"]:
|
|
157
|
+
return []
|
|
158
|
+
|
|
159
|
+
def calculate_columns(self) -> PyLegendSequence["TdsColumn"]:
|
|
160
|
+
new_columns = []
|
|
161
|
+
for base_col in self.__base_frame.columns():
|
|
162
|
+
if base_col.get_name() in self.__column_names:
|
|
163
|
+
renamed_column_name = self.__renamed_column_names[self.__column_names.index(base_col.get_name())]
|
|
164
|
+
new_columns.append(base_col.copy_with_changed_name(renamed_column_name))
|
|
165
|
+
else:
|
|
166
|
+
new_columns.append(base_col.copy())
|
|
167
|
+
return new_columns
|
|
168
|
+
|
|
169
|
+
def validate(self) -> bool:
|
|
170
|
+
if len(self.__column_names) != len(self.__renamed_column_names):
|
|
171
|
+
raise ValueError(
|
|
172
|
+
"column_names list and renamed_column_names list should have same size when renaming columns.\n"
|
|
173
|
+
f"column_names list - (Count: {len(self.__column_names)}) - {self.__column_names}\n"
|
|
174
|
+
f"renamed_column_names_list - (Count: {len(self.__renamed_column_names)}) - {self.__renamed_column_names}\n"
|
|
175
|
+
)
|
|
176
|
+
|
|
177
|
+
if len(self.__column_names) != len(set(self.__column_names)):
|
|
178
|
+
raise ValueError(
|
|
179
|
+
"column_names list shouldn't have duplicates when renaming columns.\n"
|
|
180
|
+
f"column_names list - (Count: {len(self.__column_names)}) - {self.__column_names}\n"
|
|
181
|
+
)
|
|
182
|
+
|
|
183
|
+
if len(self.__renamed_column_names) != len(set(self.__renamed_column_names)):
|
|
184
|
+
raise ValueError(
|
|
185
|
+
"renamed_column_names_list list shouldn't have duplicates when renaming columns.\n"
|
|
186
|
+
f"renamed_column_names_list - (Count: {len(self.__renamed_column_names)}) - {self.__renamed_column_names}\n"
|
|
187
|
+
)
|
|
188
|
+
|
|
189
|
+
return True
|
|
@@ -0,0 +1,131 @@
|
|
|
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
|
+
PyLegendList,
|
|
17
|
+
PyLegendSequence,
|
|
18
|
+
PyLegendTuple,
|
|
19
|
+
PyLegendCallable,
|
|
20
|
+
PyLegendUnion,
|
|
21
|
+
)
|
|
22
|
+
from pylegend.core.language.legendql_api.legendql_api_custom_expressions import LegendQLApiPrimitive
|
|
23
|
+
from pylegend.core.language.legendql_api.legendql_api_tds_row import LegendQLApiTdsRow
|
|
24
|
+
from pylegend.core.tds.legendql_api.frames.functions.legendql_api_function_helpers import infer_columns_from_frame
|
|
25
|
+
from pylegend.core.tds.legendql_api.frames.legendql_api_applied_function_tds_frame import LegendQLApiAppliedFunction
|
|
26
|
+
from pylegend.core.tds.sql_query_helpers import copy_query, create_sub_query
|
|
27
|
+
from pylegend.core.sql.metamodel import (
|
|
28
|
+
QuerySpecification,
|
|
29
|
+
SingleColumn,
|
|
30
|
+
SelectItem
|
|
31
|
+
)
|
|
32
|
+
from pylegend.core.tds.tds_column import TdsColumn
|
|
33
|
+
from pylegend.core.tds.tds_frame import FrameToSqlConfig
|
|
34
|
+
from pylegend.core.tds.tds_frame import FrameToPureConfig
|
|
35
|
+
from pylegend.core.tds.legendql_api.frames.legendql_api_base_tds_frame import LegendQLApiBaseTdsFrame
|
|
36
|
+
from pylegend.core.language.shared.helpers import escape_column_name
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
__all__: PyLegendSequence[str] = [
|
|
40
|
+
"LegendQLApiSelectFunction"
|
|
41
|
+
]
|
|
42
|
+
|
|
43
|
+
|
|
44
|
+
class LegendQLApiSelectFunction(LegendQLApiAppliedFunction):
|
|
45
|
+
__base_frame: LegendQLApiBaseTdsFrame
|
|
46
|
+
__column_name_list: PyLegendList[str]
|
|
47
|
+
|
|
48
|
+
@classmethod
|
|
49
|
+
def name(cls) -> str:
|
|
50
|
+
return "select"
|
|
51
|
+
|
|
52
|
+
def __init__(
|
|
53
|
+
self,
|
|
54
|
+
base_frame: LegendQLApiBaseTdsFrame,
|
|
55
|
+
columns: PyLegendUnion[
|
|
56
|
+
str,
|
|
57
|
+
PyLegendList[str],
|
|
58
|
+
PyLegendCallable[
|
|
59
|
+
[LegendQLApiTdsRow],
|
|
60
|
+
PyLegendUnion[LegendQLApiPrimitive, PyLegendList[LegendQLApiPrimitive]]
|
|
61
|
+
]
|
|
62
|
+
]
|
|
63
|
+
) -> None:
|
|
64
|
+
self.__base_frame = base_frame
|
|
65
|
+
self.__column_name_list = infer_columns_from_frame(base_frame, columns, "'select' function 'columns'")
|
|
66
|
+
|
|
67
|
+
def to_sql(self, config: FrameToSqlConfig) -> QuerySpecification:
|
|
68
|
+
base_query = self.__base_frame.to_sql_query_object(config)
|
|
69
|
+
db_extension = config.sql_to_string_generator().get_db_extension()
|
|
70
|
+
columns_to_retain = [db_extension.quote_identifier(x) for x in self.__column_name_list]
|
|
71
|
+
|
|
72
|
+
sub_query_required = (len(base_query.groupBy) > 0) or (len(base_query.orderBy) > 0) or \
|
|
73
|
+
(base_query.having is not None) or base_query.select.distinct
|
|
74
|
+
|
|
75
|
+
if sub_query_required:
|
|
76
|
+
new_query = create_sub_query(base_query, config, "root", columns_to_retain=columns_to_retain)
|
|
77
|
+
return new_query
|
|
78
|
+
else:
|
|
79
|
+
new_cols_with_index: PyLegendList[PyLegendTuple[int, 'SelectItem']] = []
|
|
80
|
+
for col in base_query.select.selectItems:
|
|
81
|
+
if not isinstance(col, SingleColumn):
|
|
82
|
+
raise ValueError("Select operation not supported for queries "
|
|
83
|
+
"with columns other than SingleColumn") # pragma: no cover
|
|
84
|
+
if col.alias is None:
|
|
85
|
+
raise ValueError("Select operation not supported for queries "
|
|
86
|
+
"with SingleColumns with missing alias") # pragma: no cover
|
|
87
|
+
if col.alias in columns_to_retain:
|
|
88
|
+
new_cols_with_index.append((columns_to_retain.index(col.alias), col))
|
|
89
|
+
|
|
90
|
+
new_select_items = [y[1] for y in sorted(new_cols_with_index, key=lambda x: x[0])]
|
|
91
|
+
new_query = copy_query(base_query)
|
|
92
|
+
new_query.select.selectItems = new_select_items
|
|
93
|
+
return new_query
|
|
94
|
+
|
|
95
|
+
def to_pure(self, config: FrameToPureConfig) -> str:
|
|
96
|
+
escaped_columns = []
|
|
97
|
+
for col_name in self.__column_name_list:
|
|
98
|
+
escaped_columns.append(escape_column_name(col_name))
|
|
99
|
+
return (f"{self.__base_frame.to_pure(config)}{config.separator(1)}" +
|
|
100
|
+
f"->select(~[{', '.join(escaped_columns)}])")
|
|
101
|
+
|
|
102
|
+
def base_frame(self) -> LegendQLApiBaseTdsFrame:
|
|
103
|
+
return self.__base_frame
|
|
104
|
+
|
|
105
|
+
def tds_frame_parameters(self) -> PyLegendList["LegendQLApiBaseTdsFrame"]:
|
|
106
|
+
return []
|
|
107
|
+
|
|
108
|
+
def calculate_columns(self) -> PyLegendSequence["TdsColumn"]:
|
|
109
|
+
base_columns = self.__base_frame.columns()
|
|
110
|
+
new_columns = []
|
|
111
|
+
for c in self.__column_name_list:
|
|
112
|
+
for base_col in base_columns:
|
|
113
|
+
if base_col.get_name() == c:
|
|
114
|
+
new_columns.append(base_col.copy())
|
|
115
|
+
break
|
|
116
|
+
return new_columns
|
|
117
|
+
|
|
118
|
+
def validate(self) -> bool:
|
|
119
|
+
base_columns = self.__base_frame.columns()
|
|
120
|
+
for c in self.__column_name_list:
|
|
121
|
+
found_col = False
|
|
122
|
+
for base_col in base_columns:
|
|
123
|
+
if base_col.get_name() == c:
|
|
124
|
+
found_col = True
|
|
125
|
+
break
|
|
126
|
+
if not found_col:
|
|
127
|
+
raise ValueError(
|
|
128
|
+
f"Column - '{c}' in select columns list doesn't exist in the current frame. "
|
|
129
|
+
f"Current frame columns: {[x.get_name() for x in base_columns]}"
|
|
130
|
+
)
|
|
131
|
+
return True
|
|
@@ -0,0 +1,82 @@
|
|
|
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
|
+
PyLegendList,
|
|
17
|
+
PyLegendSequence
|
|
18
|
+
)
|
|
19
|
+
from pylegend.core.tds.legendql_api.frames.legendql_api_applied_function_tds_frame import LegendQLApiAppliedFunction
|
|
20
|
+
from pylegend.core.tds.sql_query_helpers import copy_query, create_sub_query
|
|
21
|
+
from pylegend.core.sql.metamodel import (
|
|
22
|
+
QuerySpecification,
|
|
23
|
+
LongLiteral,
|
|
24
|
+
)
|
|
25
|
+
from pylegend.core.tds.tds_column import TdsColumn
|
|
26
|
+
from pylegend.core.tds.tds_frame import FrameToSqlConfig
|
|
27
|
+
from pylegend.core.tds.tds_frame import FrameToPureConfig
|
|
28
|
+
from pylegend.core.tds.legendql_api.frames.legendql_api_base_tds_frame import LegendQLApiBaseTdsFrame
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
__all__: PyLegendSequence[str] = [
|
|
32
|
+
"LegendQLApiSliceFunction"
|
|
33
|
+
]
|
|
34
|
+
|
|
35
|
+
|
|
36
|
+
class LegendQLApiSliceFunction(LegendQLApiAppliedFunction):
|
|
37
|
+
__base_frame: LegendQLApiBaseTdsFrame
|
|
38
|
+
__start_row: int
|
|
39
|
+
__end_row: int
|
|
40
|
+
|
|
41
|
+
@classmethod
|
|
42
|
+
def name(cls) -> str:
|
|
43
|
+
return "slice"
|
|
44
|
+
|
|
45
|
+
def __init__(self, base_frame: LegendQLApiBaseTdsFrame, start_row: int, end_row: int) -> None:
|
|
46
|
+
self.__base_frame = base_frame
|
|
47
|
+
self.__start_row = start_row
|
|
48
|
+
self.__end_row = end_row
|
|
49
|
+
|
|
50
|
+
def to_sql(self, config: FrameToSqlConfig) -> QuerySpecification:
|
|
51
|
+
base_query = self.__base_frame.to_sql_query_object(config)
|
|
52
|
+
should_create_sub_query = (base_query.offset is not None) or (base_query.limit is not None)
|
|
53
|
+
new_query = (
|
|
54
|
+
create_sub_query(base_query, config, "root") if should_create_sub_query else
|
|
55
|
+
copy_query(base_query)
|
|
56
|
+
)
|
|
57
|
+
new_query.offset = LongLiteral(self.__start_row)
|
|
58
|
+
new_query.limit = LongLiteral(self.__end_row - self.__start_row)
|
|
59
|
+
return new_query
|
|
60
|
+
|
|
61
|
+
def to_pure(self, config: FrameToPureConfig) -> str:
|
|
62
|
+
return (f"{self.__base_frame.to_pure(config)}{config.separator(1)}"
|
|
63
|
+
f"->slice({self.__start_row}, {self.__end_row})")
|
|
64
|
+
|
|
65
|
+
def base_frame(self) -> LegendQLApiBaseTdsFrame:
|
|
66
|
+
return self.__base_frame
|
|
67
|
+
|
|
68
|
+
def tds_frame_parameters(self) -> PyLegendList["LegendQLApiBaseTdsFrame"]:
|
|
69
|
+
return []
|
|
70
|
+
|
|
71
|
+
def calculate_columns(self) -> PyLegendSequence["TdsColumn"]:
|
|
72
|
+
return [c.copy() for c in self.__base_frame.columns()]
|
|
73
|
+
|
|
74
|
+
def validate(self) -> bool:
|
|
75
|
+
if self.__start_row < 0:
|
|
76
|
+
raise ValueError(
|
|
77
|
+
"Start row argument of slice function cannot be negative. Start row: " + str(self.__start_row)
|
|
78
|
+
)
|
|
79
|
+
if self.__end_row <= self.__start_row:
|
|
80
|
+
raise ValueError("End row argument of slice function cannot be less than or equal to start row argument. "
|
|
81
|
+
f"Start row: {self.__start_row}, End row: {self.__end_row}")
|
|
82
|
+
return True
|
|
@@ -0,0 +1,93 @@
|
|
|
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
|
+
PyLegendList,
|
|
17
|
+
PyLegendSequence,
|
|
18
|
+
PyLegendCallable,
|
|
19
|
+
PyLegendUnion,
|
|
20
|
+
)
|
|
21
|
+
from pylegend.core.language.legendql_api.legendql_api_custom_expressions import (
|
|
22
|
+
LegendQLApiPrimitive,
|
|
23
|
+
LegendQLApiSortInfo,
|
|
24
|
+
)
|
|
25
|
+
from pylegend.core.language.legendql_api.legendql_api_tds_row import LegendQLApiTdsRow
|
|
26
|
+
from pylegend.core.sql.metamodel import (
|
|
27
|
+
QuerySpecification,
|
|
28
|
+
)
|
|
29
|
+
from pylegend.core.tds.legendql_api.frames.functions.legendql_api_function_helpers import infer_sorts_from_frame
|
|
30
|
+
from pylegend.core.tds.legendql_api.frames.legendql_api_applied_function_tds_frame import LegendQLApiAppliedFunction
|
|
31
|
+
from pylegend.core.tds.legendql_api.frames.legendql_api_base_tds_frame import LegendQLApiBaseTdsFrame
|
|
32
|
+
from pylegend.core.tds.sql_query_helpers import copy_query, create_sub_query
|
|
33
|
+
from pylegend.core.tds.tds_column import TdsColumn
|
|
34
|
+
from pylegend.core.tds.tds_frame import FrameToPureConfig
|
|
35
|
+
from pylegend.core.tds.tds_frame import FrameToSqlConfig
|
|
36
|
+
|
|
37
|
+
__all__: PyLegendSequence[str] = [
|
|
38
|
+
"LegendQLApiSortFunction"
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
class LegendQLApiSortFunction(LegendQLApiAppliedFunction):
|
|
43
|
+
__base_frame: LegendQLApiBaseTdsFrame
|
|
44
|
+
__sort_infos: PyLegendList[LegendQLApiSortInfo]
|
|
45
|
+
|
|
46
|
+
@classmethod
|
|
47
|
+
def name(cls) -> str:
|
|
48
|
+
return "sort"
|
|
49
|
+
|
|
50
|
+
def __init__(
|
|
51
|
+
self,
|
|
52
|
+
base_frame: LegendQLApiBaseTdsFrame,
|
|
53
|
+
sort_infos: PyLegendUnion[
|
|
54
|
+
str,
|
|
55
|
+
PyLegendList[str],
|
|
56
|
+
PyLegendCallable[
|
|
57
|
+
[LegendQLApiTdsRow],
|
|
58
|
+
PyLegendUnion[
|
|
59
|
+
LegendQLApiPrimitive,
|
|
60
|
+
LegendQLApiSortInfo,
|
|
61
|
+
PyLegendList[PyLegendUnion[LegendQLApiPrimitive, LegendQLApiSortInfo]],
|
|
62
|
+
]
|
|
63
|
+
]
|
|
64
|
+
]
|
|
65
|
+
) -> None:
|
|
66
|
+
self.__base_frame = base_frame
|
|
67
|
+
self.__sort_infos = infer_sorts_from_frame(base_frame, sort_infos, "'sort' function sort_infos")
|
|
68
|
+
|
|
69
|
+
def to_sql(self, config: FrameToSqlConfig) -> QuerySpecification:
|
|
70
|
+
base_query = self.__base_frame.to_sql_query_object(config)
|
|
71
|
+
should_create_sub_query = (base_query.offset is not None) or (base_query.limit is not None)
|
|
72
|
+
new_query = (
|
|
73
|
+
create_sub_query(base_query, config, "root") if should_create_sub_query else
|
|
74
|
+
copy_query(base_query)
|
|
75
|
+
)
|
|
76
|
+
new_query.orderBy = [i.to_sql_node(query=new_query, config=config) for i in self.__sort_infos]
|
|
77
|
+
return new_query
|
|
78
|
+
|
|
79
|
+
def to_pure(self, config: FrameToPureConfig) -> str:
|
|
80
|
+
return (f"{self.__base_frame.to_pure(config)}{config.separator(1)}" +
|
|
81
|
+
f"->sort([{', '.join([i.to_pure_expression(config) for i in self.__sort_infos])}])")
|
|
82
|
+
|
|
83
|
+
def base_frame(self) -> LegendQLApiBaseTdsFrame:
|
|
84
|
+
return self.__base_frame
|
|
85
|
+
|
|
86
|
+
def tds_frame_parameters(self) -> PyLegendList["LegendQLApiBaseTdsFrame"]:
|
|
87
|
+
return []
|
|
88
|
+
|
|
89
|
+
def calculate_columns(self) -> PyLegendSequence["TdsColumn"]:
|
|
90
|
+
return [c.copy() for c in self.__base_frame.columns()]
|
|
91
|
+
|
|
92
|
+
def validate(self) -> bool:
|
|
93
|
+
return True
|