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,292 @@
|
|
|
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
|
+
PyLegendSequence,
|
|
17
|
+
PyLegendDict,
|
|
18
|
+
)
|
|
19
|
+
from pylegend.core.language import (
|
|
20
|
+
PyLegendBoolean,
|
|
21
|
+
PyLegendString,
|
|
22
|
+
PyLegendInteger,
|
|
23
|
+
PyLegendFloat,
|
|
24
|
+
PyLegendNumber,
|
|
25
|
+
PyLegendStrictDate,
|
|
26
|
+
PyLegendDateTime,
|
|
27
|
+
PyLegendDate,
|
|
28
|
+
)
|
|
29
|
+
from pylegend.core.language.legendql_api.legendql_api_custom_expressions import (
|
|
30
|
+
LegendQLApiBoolean,
|
|
31
|
+
LegendQLApiString,
|
|
32
|
+
LegendQLApiInteger,
|
|
33
|
+
LegendQLApiFloat,
|
|
34
|
+
LegendQLApiNumber,
|
|
35
|
+
LegendQLApiStrictDate,
|
|
36
|
+
LegendQLApiDateTime,
|
|
37
|
+
LegendQLApiDate,
|
|
38
|
+
LegendQLApiPrimitive,
|
|
39
|
+
LegendQLApiPartialFrame,
|
|
40
|
+
LegendQLApiWindowReference,
|
|
41
|
+
)
|
|
42
|
+
from pylegend.core.language.shared.tds_row import AbstractTdsRow
|
|
43
|
+
from pylegend.core.sql.metamodel import (
|
|
44
|
+
QuerySpecification,
|
|
45
|
+
Expression,
|
|
46
|
+
FunctionCall,
|
|
47
|
+
QualifiedName,
|
|
48
|
+
IntegerLiteral
|
|
49
|
+
)
|
|
50
|
+
from pylegend.core.tds.tds_frame import PyLegendTdsFrame, FrameToPureConfig, FrameToSqlConfig
|
|
51
|
+
|
|
52
|
+
__all__: PyLegendSequence[str] = [
|
|
53
|
+
"LegendQLApiTdsRow",
|
|
54
|
+
"LegendQLApiLeadRow",
|
|
55
|
+
"LegendQLApiLagRow",
|
|
56
|
+
"LegendQLApiFirstRow",
|
|
57
|
+
"LegendQLApiLastRow",
|
|
58
|
+
"LegendQLApiNthRow",
|
|
59
|
+
]
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
class LegendQLApiTdsRow(AbstractTdsRow):
|
|
63
|
+
|
|
64
|
+
def __init__(self, frame_name: str, frame: PyLegendTdsFrame) -> None:
|
|
65
|
+
super().__init__(frame_name, frame)
|
|
66
|
+
|
|
67
|
+
@staticmethod
|
|
68
|
+
def from_tds_frame(frame_name: str, frame: PyLegendTdsFrame) -> "LegendQLApiTdsRow":
|
|
69
|
+
return LegendQLApiTdsRow(frame_name=frame_name, frame=frame)
|
|
70
|
+
|
|
71
|
+
def __getattr__(self, key: str) -> LegendQLApiPrimitive:
|
|
72
|
+
return self[key]
|
|
73
|
+
|
|
74
|
+
def get_boolean(self, column: str) -> LegendQLApiBoolean:
|
|
75
|
+
return LegendQLApiBoolean(super().get_boolean(column))
|
|
76
|
+
|
|
77
|
+
def get_string(self, column: str) -> LegendQLApiString:
|
|
78
|
+
return LegendQLApiString(super().get_string(column))
|
|
79
|
+
|
|
80
|
+
def get_number(self, column: str) -> LegendQLApiNumber:
|
|
81
|
+
return LegendQLApiNumber(super().get_number(column))
|
|
82
|
+
|
|
83
|
+
def get_integer(self, column: str) -> LegendQLApiInteger:
|
|
84
|
+
return LegendQLApiInteger(super().get_integer(column))
|
|
85
|
+
|
|
86
|
+
def get_float(self, column: str) -> LegendQLApiFloat:
|
|
87
|
+
return LegendQLApiFloat(super().get_float(column))
|
|
88
|
+
|
|
89
|
+
def get_date(self, column: str) -> LegendQLApiDate:
|
|
90
|
+
return LegendQLApiDate(super().get_date(column))
|
|
91
|
+
|
|
92
|
+
def get_datetime(self, column: str) -> LegendQLApiDateTime:
|
|
93
|
+
return LegendQLApiDateTime(super().get_datetime(column))
|
|
94
|
+
|
|
95
|
+
def get_strictdate(self, column: str) -> LegendQLApiStrictDate:
|
|
96
|
+
return LegendQLApiStrictDate(super().get_strictdate(column))
|
|
97
|
+
|
|
98
|
+
def __getitem__(self, item: str) -> LegendQLApiPrimitive:
|
|
99
|
+
res = super().__getitem__(item)
|
|
100
|
+
if isinstance(res, PyLegendBoolean):
|
|
101
|
+
return LegendQLApiBoolean(res)
|
|
102
|
+
if isinstance(res, PyLegendString):
|
|
103
|
+
return LegendQLApiString(res)
|
|
104
|
+
if isinstance(res, PyLegendInteger):
|
|
105
|
+
return LegendQLApiInteger(res)
|
|
106
|
+
if isinstance(res, PyLegendFloat):
|
|
107
|
+
return LegendQLApiFloat(res)
|
|
108
|
+
if isinstance(res, PyLegendNumber):
|
|
109
|
+
return LegendQLApiNumber(res)
|
|
110
|
+
if isinstance(res, PyLegendStrictDate):
|
|
111
|
+
return LegendQLApiStrictDate(res)
|
|
112
|
+
if isinstance(res, PyLegendDateTime):
|
|
113
|
+
return LegendQLApiDateTime(res)
|
|
114
|
+
if isinstance(res, PyLegendDate):
|
|
115
|
+
return LegendQLApiDate(res)
|
|
116
|
+
|
|
117
|
+
raise RuntimeError(f"Unhandled primitive type {type(res)} in LegendQL Api")
|
|
118
|
+
|
|
119
|
+
|
|
120
|
+
class LegendQLApiLeadRow(LegendQLApiTdsRow):
|
|
121
|
+
__partial_frame: LegendQLApiPartialFrame
|
|
122
|
+
__row: "LegendQLApiTdsRow"
|
|
123
|
+
|
|
124
|
+
def __init__(
|
|
125
|
+
self,
|
|
126
|
+
partial_frame: LegendQLApiPartialFrame,
|
|
127
|
+
row: "LegendQLApiTdsRow"
|
|
128
|
+
) -> None:
|
|
129
|
+
super().__init__(frame_name=row.get_frame_name(), frame=partial_frame.get_base_frame())
|
|
130
|
+
self.__partial_frame = partial_frame
|
|
131
|
+
self.__row = row
|
|
132
|
+
|
|
133
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
134
|
+
return f"{self.__partial_frame.to_pure_expression(config)}->lead({self.__row.to_pure_expression(config)})"
|
|
135
|
+
|
|
136
|
+
def column_sql_expression(
|
|
137
|
+
self,
|
|
138
|
+
column: str,
|
|
139
|
+
frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
|
|
140
|
+
config: FrameToSqlConfig
|
|
141
|
+
) -> Expression:
|
|
142
|
+
return FunctionCall(
|
|
143
|
+
name=QualifiedName(parts=["lead"]),
|
|
144
|
+
distinct=False,
|
|
145
|
+
arguments=[super().column_sql_expression(column, frame_name_to_base_query_map, config)],
|
|
146
|
+
filter_=None,
|
|
147
|
+
window=None
|
|
148
|
+
)
|
|
149
|
+
|
|
150
|
+
|
|
151
|
+
class LegendQLApiLagRow(LegendQLApiTdsRow):
|
|
152
|
+
__partial_frame: LegendQLApiPartialFrame
|
|
153
|
+
__row: "LegendQLApiTdsRow"
|
|
154
|
+
|
|
155
|
+
def __init__(
|
|
156
|
+
self,
|
|
157
|
+
partial_frame: LegendQLApiPartialFrame,
|
|
158
|
+
row: "LegendQLApiTdsRow"
|
|
159
|
+
) -> None:
|
|
160
|
+
super().__init__(frame_name=row.get_frame_name(), frame=partial_frame.get_base_frame())
|
|
161
|
+
self.__partial_frame = partial_frame
|
|
162
|
+
self.__row = row
|
|
163
|
+
|
|
164
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
165
|
+
return f"{self.__partial_frame.to_pure_expression(config)}->lag({self.__row.to_pure_expression(config)})"
|
|
166
|
+
|
|
167
|
+
def column_sql_expression(
|
|
168
|
+
self,
|
|
169
|
+
column: str,
|
|
170
|
+
frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
|
|
171
|
+
config: FrameToSqlConfig
|
|
172
|
+
) -> Expression:
|
|
173
|
+
return FunctionCall(
|
|
174
|
+
name=QualifiedName(parts=["lag"]),
|
|
175
|
+
distinct=False,
|
|
176
|
+
arguments=[super().column_sql_expression(column, frame_name_to_base_query_map, config)],
|
|
177
|
+
filter_=None,
|
|
178
|
+
window=None
|
|
179
|
+
)
|
|
180
|
+
|
|
181
|
+
|
|
182
|
+
class LegendQLApiFirstRow(LegendQLApiTdsRow):
|
|
183
|
+
__partial_frame: LegendQLApiPartialFrame
|
|
184
|
+
__window_ref: LegendQLApiWindowReference
|
|
185
|
+
__row: "LegendQLApiTdsRow"
|
|
186
|
+
|
|
187
|
+
def __init__(
|
|
188
|
+
self,
|
|
189
|
+
partial_frame: LegendQLApiPartialFrame,
|
|
190
|
+
window_ref: LegendQLApiWindowReference,
|
|
191
|
+
row: "LegendQLApiTdsRow"
|
|
192
|
+
) -> None:
|
|
193
|
+
super().__init__(frame_name=row.get_frame_name(), frame=partial_frame.get_base_frame())
|
|
194
|
+
self.__partial_frame = partial_frame
|
|
195
|
+
self.__window_ref = window_ref
|
|
196
|
+
self.__row = row
|
|
197
|
+
|
|
198
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
199
|
+
return (f"{self.__partial_frame.to_pure_expression(config)}->first("
|
|
200
|
+
f"{self.__window_ref.to_pure_expression(config)}, {self.__row.to_pure_expression(config)})")
|
|
201
|
+
|
|
202
|
+
def column_sql_expression(
|
|
203
|
+
self,
|
|
204
|
+
column: str,
|
|
205
|
+
frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
|
|
206
|
+
config: FrameToSqlConfig
|
|
207
|
+
) -> Expression:
|
|
208
|
+
return FunctionCall(
|
|
209
|
+
name=QualifiedName(parts=["first_value"]),
|
|
210
|
+
distinct=False,
|
|
211
|
+
arguments=[super().column_sql_expression(column, frame_name_to_base_query_map, config)],
|
|
212
|
+
filter_=None,
|
|
213
|
+
window=None
|
|
214
|
+
)
|
|
215
|
+
|
|
216
|
+
|
|
217
|
+
class LegendQLApiLastRow(LegendQLApiTdsRow):
|
|
218
|
+
__partial_frame: LegendQLApiPartialFrame
|
|
219
|
+
__window_ref: LegendQLApiWindowReference
|
|
220
|
+
__row: "LegendQLApiTdsRow"
|
|
221
|
+
|
|
222
|
+
def __init__(
|
|
223
|
+
self,
|
|
224
|
+
partial_frame: LegendQLApiPartialFrame,
|
|
225
|
+
window_ref: LegendQLApiWindowReference,
|
|
226
|
+
row: "LegendQLApiTdsRow"
|
|
227
|
+
) -> None:
|
|
228
|
+
super().__init__(frame_name=row.get_frame_name(), frame=partial_frame.get_base_frame())
|
|
229
|
+
self.__partial_frame = partial_frame
|
|
230
|
+
self.__window_ref = window_ref
|
|
231
|
+
self.__row = row
|
|
232
|
+
|
|
233
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
234
|
+
return (f"{self.__partial_frame.to_pure_expression(config)}->last("
|
|
235
|
+
f"{self.__window_ref.to_pure_expression(config)}, {self.__row.to_pure_expression(config)})")
|
|
236
|
+
|
|
237
|
+
def column_sql_expression(
|
|
238
|
+
self,
|
|
239
|
+
column: str,
|
|
240
|
+
frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
|
|
241
|
+
config: FrameToSqlConfig
|
|
242
|
+
) -> Expression:
|
|
243
|
+
return FunctionCall(
|
|
244
|
+
name=QualifiedName(parts=["last_value"]),
|
|
245
|
+
distinct=False,
|
|
246
|
+
arguments=[super().column_sql_expression(column, frame_name_to_base_query_map, config)],
|
|
247
|
+
filter_=None,
|
|
248
|
+
window=None
|
|
249
|
+
)
|
|
250
|
+
|
|
251
|
+
|
|
252
|
+
class LegendQLApiNthRow(LegendQLApiTdsRow):
|
|
253
|
+
__partial_frame: LegendQLApiPartialFrame
|
|
254
|
+
__window_ref: LegendQLApiWindowReference
|
|
255
|
+
__row: "LegendQLApiTdsRow"
|
|
256
|
+
__offset: int
|
|
257
|
+
|
|
258
|
+
def __init__(
|
|
259
|
+
self,
|
|
260
|
+
partial_frame: LegendQLApiPartialFrame,
|
|
261
|
+
window_ref: LegendQLApiWindowReference,
|
|
262
|
+
row: "LegendQLApiTdsRow",
|
|
263
|
+
offset: int
|
|
264
|
+
) -> None:
|
|
265
|
+
super().__init__(frame_name=row.get_frame_name(), frame=partial_frame.get_base_frame())
|
|
266
|
+
self.__partial_frame = partial_frame
|
|
267
|
+
self.__window_ref = window_ref
|
|
268
|
+
self.__row = row
|
|
269
|
+
self.__offset = offset
|
|
270
|
+
|
|
271
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
272
|
+
return (
|
|
273
|
+
f"{self.__partial_frame.to_pure_expression(config)}->nth("
|
|
274
|
+
f"{self.__window_ref.to_pure_expression(config)}, {self.__row.to_pure_expression(config)}, {self.__offset})"
|
|
275
|
+
)
|
|
276
|
+
|
|
277
|
+
def column_sql_expression(
|
|
278
|
+
self,
|
|
279
|
+
column: str,
|
|
280
|
+
frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
|
|
281
|
+
config: FrameToSqlConfig
|
|
282
|
+
) -> Expression:
|
|
283
|
+
return FunctionCall(
|
|
284
|
+
name=QualifiedName(parts=["nth_value"]),
|
|
285
|
+
distinct=False,
|
|
286
|
+
arguments=[
|
|
287
|
+
super().column_sql_expression(column, frame_name_to_base_query_map, config),
|
|
288
|
+
IntegerLiteral(self.__offset)
|
|
289
|
+
],
|
|
290
|
+
filter_=None,
|
|
291
|
+
window=None
|
|
292
|
+
)
|
|
@@ -0,0 +1,13 @@
|
|
|
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.
|
|
@@ -18,7 +18,7 @@ from pylegend._typing import (
|
|
|
18
18
|
PyLegendSequence,
|
|
19
19
|
PyLegendDict,
|
|
20
20
|
)
|
|
21
|
-
from pylegend.core.language.expression import (
|
|
21
|
+
from pylegend.core.language.shared.expression import (
|
|
22
22
|
PyLegendExpression,
|
|
23
23
|
PyLegendExpressionBooleanReturn,
|
|
24
24
|
PyLegendExpressionStringReturn,
|
|
@@ -32,9 +32,13 @@ from pylegend.core.language.expression import (
|
|
|
32
32
|
from pylegend.core.sql.metamodel import (
|
|
33
33
|
Expression,
|
|
34
34
|
QuerySpecification,
|
|
35
|
-
SingleColumn,
|
|
36
35
|
)
|
|
37
36
|
from pylegend.core.tds.tds_frame import FrameToSqlConfig
|
|
37
|
+
from pylegend.core.tds.tds_frame import FrameToPureConfig
|
|
38
|
+
from pylegend.core.language.shared.helpers import escape_column_name
|
|
39
|
+
from typing import TYPE_CHECKING
|
|
40
|
+
if TYPE_CHECKING:
|
|
41
|
+
from pylegend.core.language.shared.tds_row import AbstractTdsRow
|
|
38
42
|
|
|
39
43
|
|
|
40
44
|
__all__: PyLegendSequence[str] = [
|
|
@@ -51,11 +55,11 @@ __all__: PyLegendSequence[str] = [
|
|
|
51
55
|
|
|
52
56
|
|
|
53
57
|
class PyLegendColumnExpression(PyLegendExpression, metaclass=ABCMeta):
|
|
54
|
-
|
|
58
|
+
__row: "AbstractTdsRow"
|
|
55
59
|
__column: str
|
|
56
60
|
|
|
57
|
-
def __init__(self,
|
|
58
|
-
self.
|
|
61
|
+
def __init__(self, row: "AbstractTdsRow", column: str) -> None:
|
|
62
|
+
self.__row = row
|
|
59
63
|
self.__column = column
|
|
60
64
|
|
|
61
65
|
def to_sql_expression(
|
|
@@ -63,61 +67,58 @@ class PyLegendColumnExpression(PyLegendExpression, metaclass=ABCMeta):
|
|
|
63
67
|
frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
|
|
64
68
|
config: FrameToSqlConfig
|
|
65
69
|
) -> Expression:
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
if len(filtered) == 0:
|
|
74
|
-
raise RuntimeError("Cannot find column: " + self.__column) # pragma: no cover
|
|
75
|
-
return filtered[0].expression
|
|
70
|
+
return self.__row.column_sql_expression(self.__column, frame_name_to_base_query_map, config)
|
|
71
|
+
|
|
72
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
73
|
+
return f"{self.__row.to_pure_expression(config)}.{escape_column_name(self.__column)}"
|
|
74
|
+
|
|
75
|
+
def get_column(self) -> str:
|
|
76
|
+
return self.__column
|
|
76
77
|
|
|
77
78
|
|
|
78
79
|
class PyLegendBooleanColumnExpression(PyLegendColumnExpression, PyLegendExpressionBooleanReturn):
|
|
79
80
|
|
|
80
|
-
def __init__(self,
|
|
81
|
-
super().__init__(
|
|
81
|
+
def __init__(self, row: "AbstractTdsRow", column: str) -> None:
|
|
82
|
+
super().__init__(row=row, column=column)
|
|
82
83
|
|
|
83
84
|
|
|
84
85
|
class PyLegendStringColumnExpression(PyLegendColumnExpression, PyLegendExpressionStringReturn):
|
|
85
86
|
|
|
86
|
-
def __init__(self,
|
|
87
|
-
super().__init__(
|
|
87
|
+
def __init__(self, row: "AbstractTdsRow", column: str) -> None:
|
|
88
|
+
super().__init__(row=row, column=column)
|
|
88
89
|
|
|
89
90
|
|
|
90
91
|
class PyLegendNumberColumnExpression(PyLegendColumnExpression, PyLegendExpressionNumberReturn):
|
|
91
92
|
|
|
92
|
-
def __init__(self,
|
|
93
|
-
super().__init__(
|
|
93
|
+
def __init__(self, row: "AbstractTdsRow", column: str) -> None:
|
|
94
|
+
super().__init__(row=row, column=column)
|
|
94
95
|
|
|
95
96
|
|
|
96
97
|
class PyLegendIntegerColumnExpression(PyLegendNumberColumnExpression, PyLegendExpressionIntegerReturn):
|
|
97
98
|
|
|
98
|
-
def __init__(self,
|
|
99
|
-
super().__init__(
|
|
99
|
+
def __init__(self, row: "AbstractTdsRow", column: str) -> None:
|
|
100
|
+
super().__init__(row=row, column=column)
|
|
100
101
|
|
|
101
102
|
|
|
102
103
|
class PyLegendFloatColumnExpression(PyLegendNumberColumnExpression, PyLegendExpressionFloatReturn):
|
|
103
104
|
|
|
104
|
-
def __init__(self,
|
|
105
|
-
super().__init__(
|
|
105
|
+
def __init__(self, row: "AbstractTdsRow", column: str) -> None:
|
|
106
|
+
super().__init__(row=row, column=column)
|
|
106
107
|
|
|
107
108
|
|
|
108
109
|
class PyLegendDateColumnExpression(PyLegendColumnExpression, PyLegendExpressionDateReturn):
|
|
109
110
|
|
|
110
|
-
def __init__(self,
|
|
111
|
-
super().__init__(
|
|
111
|
+
def __init__(self, row: "AbstractTdsRow", column: str) -> None:
|
|
112
|
+
super().__init__(row=row, column=column)
|
|
112
113
|
|
|
113
114
|
|
|
114
115
|
class PyLegendDateTimeColumnExpression(PyLegendDateColumnExpression, PyLegendExpressionDateTimeReturn):
|
|
115
116
|
|
|
116
|
-
def __init__(self,
|
|
117
|
-
super().__init__(
|
|
117
|
+
def __init__(self, row: "AbstractTdsRow", column: str) -> None:
|
|
118
|
+
super().__init__(row=row, column=column)
|
|
118
119
|
|
|
119
120
|
|
|
120
121
|
class PyLegendStrictDateColumnExpression(PyLegendDateColumnExpression, PyLegendExpressionStrictDateReturn):
|
|
121
122
|
|
|
122
|
-
def __init__(self,
|
|
123
|
-
super().__init__(
|
|
123
|
+
def __init__(self, row: "AbstractTdsRow", column: str) -> None:
|
|
124
|
+
super().__init__(row=row, column=column)
|
|
@@ -23,6 +23,7 @@ from pylegend.core.sql.metamodel import (
|
|
|
23
23
|
QuerySpecification,
|
|
24
24
|
)
|
|
25
25
|
from pylegend.core.tds.tds_frame import FrameToSqlConfig
|
|
26
|
+
from pylegend.core.tds.tds_frame import FrameToPureConfig
|
|
26
27
|
|
|
27
28
|
|
|
28
29
|
__all__: PyLegendSequence[str] = [
|
|
@@ -47,6 +48,13 @@ class PyLegendExpression(metaclass=ABCMeta):
|
|
|
47
48
|
) -> Expression:
|
|
48
49
|
pass
|
|
49
50
|
|
|
51
|
+
@abstractmethod
|
|
52
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
53
|
+
pass
|
|
54
|
+
|
|
55
|
+
def is_non_nullable(self) -> bool:
|
|
56
|
+
return False
|
|
57
|
+
|
|
50
58
|
|
|
51
59
|
class PyLegendExpressionBooleanReturn(PyLegendExpression, metaclass=ABCMeta):
|
|
52
60
|
pass
|
|
@@ -16,9 +16,9 @@
|
|
|
16
16
|
from pylegend._typing import (
|
|
17
17
|
PyLegendSequence,
|
|
18
18
|
)
|
|
19
|
-
from pylegend.core.language.primitives.strictdate import PyLegendStrictDate
|
|
20
|
-
from pylegend.core.language.primitives.datetime import PyLegendDateTime
|
|
21
|
-
from pylegend.core.language.operations.date_operation_expressions import (
|
|
19
|
+
from pylegend.core.language.shared.primitives.strictdate import PyLegendStrictDate
|
|
20
|
+
from pylegend.core.language.shared.primitives.datetime import PyLegendDateTime
|
|
21
|
+
from pylegend.core.language.shared.operations.date_operation_expressions import (
|
|
22
22
|
PyLegendTodayExpression,
|
|
23
23
|
PyLegendNowExpression,
|
|
24
24
|
)
|
|
@@ -0,0 +1,75 @@
|
|
|
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 PyLegendList
|
|
16
|
+
__all__ = [
|
|
17
|
+
"generate_pure_functional_call",
|
|
18
|
+
"generate_pure_lambda",
|
|
19
|
+
"escape_column_name",
|
|
20
|
+
"expr_has_matching_start_and_end_parentheses",
|
|
21
|
+
]
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
def generate_pure_functional_call(
|
|
25
|
+
func: str,
|
|
26
|
+
params: PyLegendList[str],
|
|
27
|
+
force_prefix: bool = False,
|
|
28
|
+
auto_map: bool = False
|
|
29
|
+
) -> str:
|
|
30
|
+
should_prefix = force_prefix or (len(params) == 0)
|
|
31
|
+
|
|
32
|
+
updated_params: PyLegendList[str] = []
|
|
33
|
+
for param in params:
|
|
34
|
+
if param.startswith("(") and param.endswith(")"):
|
|
35
|
+
if expr_has_matching_start_and_end_parentheses(param):
|
|
36
|
+
updated_params.append(param[1:-1])
|
|
37
|
+
else:
|
|
38
|
+
updated_params.append(param)
|
|
39
|
+
else:
|
|
40
|
+
updated_params.append(param)
|
|
41
|
+
|
|
42
|
+
if auto_map and len(updated_params) == 1:
|
|
43
|
+
return f"{params[0]}->map(op | {generate_pure_functional_call(func, ['$op'], force_prefix, False)})"
|
|
44
|
+
|
|
45
|
+
if auto_map and len(updated_params) == 2:
|
|
46
|
+
new_params = ['$op'] + [updated_params[1]]
|
|
47
|
+
return f"{params[0]}->map(op | {generate_pure_functional_call(func, new_params, force_prefix, False)})"
|
|
48
|
+
|
|
49
|
+
if should_prefix:
|
|
50
|
+
return f"{func}({', '.join(updated_params)})"
|
|
51
|
+
else:
|
|
52
|
+
return f"{params[0]}->{func}({', '.join(updated_params[1:])})"
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
def generate_pure_lambda(param_name: str, expr: str, wrap_in_braces: bool = True) -> str:
|
|
56
|
+
lambda_code = param_name + " | " + (expr[1:-1] if expr_has_matching_start_and_end_parentheses(expr) else expr)
|
|
57
|
+
return "{" + lambda_code + "}" if wrap_in_braces else lambda_code
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def escape_column_name(name: str) -> str:
|
|
61
|
+
return (name if name.isidentifier() else "'" + name.replace("'", "\\'") + "'")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
def expr_has_matching_start_and_end_parentheses(expr: str) -> bool:
|
|
65
|
+
if expr.startswith("(") and expr.endswith(")"):
|
|
66
|
+
bracket_indices = [0]
|
|
67
|
+
for (i, char) in enumerate(expr[1:-1]):
|
|
68
|
+
if char == "(":
|
|
69
|
+
bracket_indices.append(i + 1)
|
|
70
|
+
elif char == ")":
|
|
71
|
+
if bracket_indices:
|
|
72
|
+
bracket_indices.pop()
|
|
73
|
+
return (len(bracket_indices) == 1) and (bracket_indices[0] == 0)
|
|
74
|
+
else:
|
|
75
|
+
return False
|
|
@@ -19,7 +19,7 @@ from pylegend._typing import (
|
|
|
19
19
|
PyLegendDict,
|
|
20
20
|
PyLegendUnion,
|
|
21
21
|
)
|
|
22
|
-
from pylegend.core.language.expression import (
|
|
22
|
+
from pylegend.core.language.shared.expression import (
|
|
23
23
|
PyLegendExpression,
|
|
24
24
|
PyLegendExpressionBooleanReturn,
|
|
25
25
|
PyLegendExpressionStringReturn,
|
|
@@ -39,6 +39,7 @@ from pylegend.core.sql.metamodel import (
|
|
|
39
39
|
ColumnType,
|
|
40
40
|
)
|
|
41
41
|
from pylegend.core.tds.tds_frame import FrameToSqlConfig
|
|
42
|
+
from pylegend.core.tds.tds_frame import FrameToPureConfig
|
|
42
43
|
|
|
43
44
|
|
|
44
45
|
__all__: PyLegendSequence[str] = [
|
|
@@ -65,6 +66,12 @@ class PyLegendBooleanLiteralExpression(PyLegendExpressionBooleanReturn):
|
|
|
65
66
|
) -> Expression:
|
|
66
67
|
return BooleanLiteral(value=self.__value)
|
|
67
68
|
|
|
69
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
70
|
+
return "true" if self.__value else "false"
|
|
71
|
+
|
|
72
|
+
def is_non_nullable(self) -> bool:
|
|
73
|
+
return True
|
|
74
|
+
|
|
68
75
|
|
|
69
76
|
class PyLegendStringLiteralExpression(PyLegendExpressionStringReturn):
|
|
70
77
|
__value: str
|
|
@@ -79,6 +86,13 @@ class PyLegendStringLiteralExpression(PyLegendExpressionStringReturn):
|
|
|
79
86
|
) -> Expression:
|
|
80
87
|
return StringLiteral(value=self.__value, quoted=False)
|
|
81
88
|
|
|
89
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
90
|
+
escaped = self.__value.replace("'", "\\'")
|
|
91
|
+
return f"'{escaped}'"
|
|
92
|
+
|
|
93
|
+
def is_non_nullable(self) -> bool:
|
|
94
|
+
return True
|
|
95
|
+
|
|
82
96
|
|
|
83
97
|
class PyLegendIntegerLiteralExpression(PyLegendExpressionIntegerReturn):
|
|
84
98
|
__value: int
|
|
@@ -93,6 +107,12 @@ class PyLegendIntegerLiteralExpression(PyLegendExpressionIntegerReturn):
|
|
|
93
107
|
) -> Expression:
|
|
94
108
|
return IntegerLiteral(value=self.__value)
|
|
95
109
|
|
|
110
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
111
|
+
return str(self.__value)
|
|
112
|
+
|
|
113
|
+
def is_non_nullable(self) -> bool:
|
|
114
|
+
return True
|
|
115
|
+
|
|
96
116
|
|
|
97
117
|
class PyLegendFloatLiteralExpression(PyLegendExpressionFloatReturn):
|
|
98
118
|
__value: float
|
|
@@ -107,6 +127,12 @@ class PyLegendFloatLiteralExpression(PyLegendExpressionFloatReturn):
|
|
|
107
127
|
) -> Expression:
|
|
108
128
|
return DoubleLiteral(value=self.__value)
|
|
109
129
|
|
|
130
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
131
|
+
return str(self.__value)
|
|
132
|
+
|
|
133
|
+
def is_non_nullable(self) -> bool:
|
|
134
|
+
return True
|
|
135
|
+
|
|
110
136
|
|
|
111
137
|
class PyLegendDateTimeLiteralExpression(PyLegendExpressionDateTimeReturn):
|
|
112
138
|
__value: datetime
|
|
@@ -124,6 +150,12 @@ class PyLegendDateTimeLiteralExpression(PyLegendExpressionDateTimeReturn):
|
|
|
124
150
|
type_=ColumnType(name="TIMESTAMP", parameters=[])
|
|
125
151
|
)
|
|
126
152
|
|
|
153
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
154
|
+
return f"%{self.__value.isoformat()}"
|
|
155
|
+
|
|
156
|
+
def is_non_nullable(self) -> bool:
|
|
157
|
+
return True
|
|
158
|
+
|
|
127
159
|
|
|
128
160
|
class PyLegendStrictDateLiteralExpression(PyLegendExpressionStrictDateReturn):
|
|
129
161
|
__value: date
|
|
@@ -141,6 +173,12 @@ class PyLegendStrictDateLiteralExpression(PyLegendExpressionStrictDateReturn):
|
|
|
141
173
|
type_=ColumnType(name="DATE", parameters=[])
|
|
142
174
|
)
|
|
143
175
|
|
|
176
|
+
def to_pure_expression(self, config: FrameToPureConfig) -> str:
|
|
177
|
+
return f"%{self.__value.isoformat()}"
|
|
178
|
+
|
|
179
|
+
def is_non_nullable(self) -> bool:
|
|
180
|
+
return True
|
|
181
|
+
|
|
144
182
|
|
|
145
183
|
def convert_literal_to_literal_expression(
|
|
146
184
|
literal: PyLegendUnion[int, float, bool, str, datetime, date]
|