pylegend 0.12.0__py3-none-any.whl → 0.14.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.
Files changed (30) hide show
  1. pylegend/core/database/sql_to_string/db_extension.py +177 -1
  2. pylegend/core/language/pandas_api/pandas_api_groupby_series.py +357 -0
  3. pylegend/core/language/pandas_api/pandas_api_series.py +202 -8
  4. pylegend/core/language/shared/expression.py +5 -0
  5. pylegend/core/language/shared/literal_expressions.py +22 -1
  6. pylegend/core/language/shared/operations/boolean_operation_expressions.py +144 -0
  7. pylegend/core/language/shared/operations/date_operation_expressions.py +91 -0
  8. pylegend/core/language/shared/operations/integer_operation_expressions.py +183 -1
  9. pylegend/core/language/shared/operations/string_operation_expressions.py +31 -1
  10. pylegend/core/language/shared/primitives/boolean.py +40 -0
  11. pylegend/core/language/shared/primitives/date.py +39 -0
  12. pylegend/core/language/shared/primitives/datetime.py +18 -0
  13. pylegend/core/language/shared/primitives/integer.py +54 -1
  14. pylegend/core/language/shared/primitives/strictdate.py +25 -1
  15. pylegend/core/language/shared/primitives/string.py +16 -2
  16. pylegend/core/sql/metamodel.py +50 -1
  17. pylegend/core/sql/metamodel_extension.py +77 -1
  18. pylegend/core/tds/pandas_api/frames/functions/aggregate_function.py +21 -11
  19. pylegend/core/tds/pandas_api/frames/functions/iloc.py +99 -0
  20. pylegend/core/tds/pandas_api/frames/functions/loc.py +136 -0
  21. pylegend/core/tds/pandas_api/frames/pandas_api_applied_function_tds_frame.py +3 -0
  22. pylegend/core/tds/pandas_api/frames/pandas_api_base_tds_frame.py +50 -2
  23. pylegend/core/tds/pandas_api/frames/pandas_api_groupby_tds_frame.py +87 -27
  24. pylegend/core/tds/pandas_api/frames/pandas_api_tds_frame.py +12 -0
  25. {pylegend-0.12.0.dist-info → pylegend-0.14.0.dist-info}/METADATA +1 -1
  26. {pylegend-0.12.0.dist-info → pylegend-0.14.0.dist-info}/RECORD +30 -27
  27. {pylegend-0.12.0.dist-info → pylegend-0.14.0.dist-info}/WHEEL +1 -1
  28. {pylegend-0.12.0.dist-info → pylegend-0.14.0.dist-info}/licenses/LICENSE +0 -0
  29. {pylegend-0.12.0.dist-info → pylegend-0.14.0.dist-info}/licenses/LICENSE.spdx +0 -0
  30. {pylegend-0.12.0.dist-info → pylegend-0.14.0.dist-info}/licenses/NOTICE +0 -0
@@ -12,7 +12,7 @@
12
12
  # See the License for the specific language governing permissions and
13
13
  # limitations under the License.
14
14
 
15
- from typing import TYPE_CHECKING
15
+ from typing import TYPE_CHECKING, runtime_checkable, Protocol
16
16
 
17
17
  import pandas as pd
18
18
 
@@ -22,8 +22,10 @@ from pylegend._typing import (
22
22
  from pylegend._typing import (
23
23
  PyLegendSequence,
24
24
  PyLegendOptional,
25
- PyLegendTypeVar
25
+ PyLegendTypeVar,
26
+ PyLegendUnion
26
27
  )
28
+ from pylegend.core.language.pandas_api.pandas_api_aggregate_specification import PyLegendAggInput
27
29
  from pylegend.core.language.pandas_api.pandas_api_tds_row import PandasApiTdsRow
28
30
  from pylegend.core.language.shared.column_expressions import PyLegendColumnExpression
29
31
  from pylegend.core.language.shared.expression import (
@@ -42,7 +44,7 @@ from pylegend.core.language.shared.primitives.datetime import PyLegendDateTime
42
44
  from pylegend.core.language.shared.primitives.float import PyLegendFloat
43
45
  from pylegend.core.language.shared.primitives.integer import PyLegendInteger
44
46
  from pylegend.core.language.shared.primitives.number import PyLegendNumber
45
- from pylegend.core.language.shared.primitives.primitive import PyLegendPrimitive
47
+ from pylegend.core.language.shared.primitives.primitive import PyLegendPrimitive, PyLegendPrimitiveOrPythonPrimitive
46
48
  from pylegend.core.language.shared.primitives.strictdate import PyLegendStrictDate
47
49
  from pylegend.core.language.shared.primitives.string import PyLegendString
48
50
  from pylegend.core.sql.metamodel import (
@@ -50,6 +52,8 @@ from pylegend.core.sql.metamodel import (
50
52
  )
51
53
  from pylegend.core.sql.metamodel import QuerySpecification
52
54
  from pylegend.core.tds.abstract.frames.base_tds_frame import BaseTdsFrame
55
+ from pylegend.core.tds.pandas_api.frames.functions.filter import PandasApiFilterFunction
56
+ from pylegend.core.tds.pandas_api.frames.pandas_api_applied_function_tds_frame import PandasApiAppliedFunctionTdsFrame
53
57
  from pylegend.core.tds.result_handler import ResultHandler
54
58
  from pylegend.core.tds.tds_column import TdsColumn
55
59
  from pylegend.core.tds.tds_frame import FrameToPureConfig
@@ -60,19 +64,39 @@ if TYPE_CHECKING:
60
64
  from pylegend.core.tds.pandas_api.frames.pandas_api_tds_frame import PandasApiTdsFrame
61
65
 
62
66
  __all__: PyLegendSequence[str] = [
63
- "Series"
67
+ "Series",
68
+ "SupportsToSqlExpression",
69
+ "SupportsToPureExpression",
64
70
  ]
65
71
 
66
72
  R = PyLegendTypeVar('R')
67
73
 
68
74
 
75
+ @runtime_checkable
76
+ class SupportsToSqlExpression(Protocol):
77
+ def to_sql_expression(
78
+ self,
79
+ frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
80
+ config: FrameToSqlConfig
81
+ ) -> Expression:
82
+ ...
83
+
84
+
85
+ @runtime_checkable
86
+ class SupportsToPureExpression(Protocol):
87
+ def to_pure_expression(self, config: FrameToPureConfig) -> str:
88
+ ...
89
+
90
+
69
91
  class Series(PyLegendColumnExpression, PyLegendPrimitive, BaseTdsFrame):
70
92
  def __init__(self, base_frame: "PandasApiTdsFrame", column: str):
71
93
  row = PandasApiTdsRow.from_tds_frame("c", base_frame)
72
94
  PyLegendColumnExpression.__init__(self, row=row, column=column)
73
95
 
74
96
  self.__base_frame = base_frame
75
- self._filtered_frame = base_frame.filter(items=[column])
97
+ filtered = base_frame.filter(items=[column])
98
+ assert isinstance(filtered, PandasApiAppliedFunctionTdsFrame)
99
+ self._filtered_frame: PandasApiAppliedFunctionTdsFrame = filtered
76
100
 
77
101
  def value(self) -> PyLegendColumnExpression:
78
102
  return self
@@ -85,9 +109,27 @@ class Series(PyLegendColumnExpression, PyLegendPrimitive, BaseTdsFrame):
85
109
  frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
86
110
  config: FrameToSqlConfig
87
111
  ) -> Expression:
112
+ applied_func = self._filtered_frame.get_applied_function()
113
+ if not isinstance(applied_func, PandasApiFilterFunction): # pragma: no cover
114
+ if isinstance(applied_func, SupportsToSqlExpression):
115
+ return applied_func.to_sql_expression(frame_name_to_base_query_map, config)
116
+ else:
117
+ raise NotImplementedError(
118
+ f"The '{applied_func.name()}' function cannot provide a SQL expression"
119
+ )
120
+
88
121
  return super().to_sql_expression(frame_name_to_base_query_map, config)
89
122
 
90
123
  def to_pure_expression(self, config: FrameToPureConfig) -> str:
124
+ applied_func = self._filtered_frame.get_applied_function()
125
+ if not isinstance(applied_func, PandasApiFilterFunction): # pragma: no cover
126
+ if isinstance(applied_func, SupportsToPureExpression):
127
+ return applied_func.to_pure_expression(config)
128
+ else:
129
+ raise NotImplementedError(
130
+ f"The '{applied_func.name()}' function cannot provide a pure expression"
131
+ )
132
+
91
133
  return super().to_pure_expression(config)
92
134
 
93
135
  def columns(self) -> PyLegendSequence[TdsColumn]:
@@ -120,13 +162,165 @@ class Series(PyLegendColumnExpression, PyLegendPrimitive, BaseTdsFrame):
120
162
  return self._filtered_frame.execute_frame_to_pandas_df(chunk_size, pandas_df_read_config) # pragma: no cover
121
163
 
122
164
  def to_sql_query_object(self, config: FrameToSqlConfig) -> QuerySpecification:
123
- return self._filtered_frame.to_sql_query_object(config) # type: ignore
165
+ return self._filtered_frame.to_sql_query_object(config)
124
166
 
125
167
  def to_pure(self, config: FrameToPureConfig) -> str:
126
- return self._filtered_frame.to_pure(config) # type: ignore
168
+ return self._filtered_frame.to_pure(config)
127
169
 
128
170
  def get_all_tds_frames(self) -> PyLegendSequence["BaseTdsFrame"]:
129
- return self._filtered_frame.get_all_tds_frames() # type: ignore
171
+ return self._filtered_frame.get_all_tds_frames()
172
+
173
+ def aggregate(
174
+ self,
175
+ func: PyLegendAggInput,
176
+ axis: PyLegendUnion[int, str] = 0,
177
+ *args: PyLegendPrimitiveOrPythonPrimitive,
178
+ **kwargs: PyLegendPrimitiveOrPythonPrimitive
179
+ ) -> "PandasApiTdsFrame":
180
+ return self._filtered_frame.aggregate(func, axis, *args, **kwargs)
181
+
182
+ def agg(
183
+ self,
184
+ func: PyLegendAggInput,
185
+ axis: PyLegendUnion[int, str] = 0,
186
+ *args: PyLegendPrimitiveOrPythonPrimitive,
187
+ **kwargs: PyLegendPrimitiveOrPythonPrimitive
188
+ ) -> "PandasApiTdsFrame":
189
+ return self.aggregate(func, axis, *args, **kwargs)
190
+
191
+ def sum(
192
+ self,
193
+ axis: PyLegendUnion[int, str] = 0,
194
+ skipna: bool = True,
195
+ numeric_only: bool = False,
196
+ min_count: int = 0,
197
+ **kwargs: PyLegendPrimitiveOrPythonPrimitive
198
+ ) -> "PandasApiTdsFrame":
199
+ if axis not in [0, "index"]:
200
+ raise NotImplementedError(f"The 'axis' parameter must be 0 or 'index' in sum function, but got: {axis}")
201
+ if skipna is not True:
202
+ raise NotImplementedError("skipna=False is not currently supported in sum function. "
203
+ "SQL aggregation ignores nulls by default.")
204
+ if numeric_only is not False:
205
+ raise NotImplementedError("numeric_only=True is not currently supported in sum function.")
206
+ if min_count != 0:
207
+ raise NotImplementedError(f"min_count must be 0 in sum function, but got: {min_count}")
208
+ if len(kwargs) > 0:
209
+ raise NotImplementedError(
210
+ f"Additional keyword arguments not supported in sum function: {list(kwargs.keys())}")
211
+ return self.aggregate("sum", 0)
212
+
213
+ def mean(
214
+ self,
215
+ axis: PyLegendUnion[int, str] = 0,
216
+ skipna: bool = True,
217
+ numeric_only: bool = False,
218
+ **kwargs: PyLegendPrimitiveOrPythonPrimitive
219
+ ) -> "PandasApiTdsFrame":
220
+ if axis not in [0, "index"]:
221
+ raise NotImplementedError(f"The 'axis' parameter must be 0 or 'index' in mean function, but got: {axis}")
222
+ if skipna is not True:
223
+ raise NotImplementedError("skipna=False is not currently supported in mean function.")
224
+ if numeric_only is not False:
225
+ raise NotImplementedError("numeric_only=True is not currently supported in mean function.")
226
+ if len(kwargs) > 0:
227
+ raise NotImplementedError(
228
+ f"Additional keyword arguments not supported in mean function: {list(kwargs.keys())}")
229
+ return self.aggregate("mean", 0)
230
+
231
+ def min(
232
+ self,
233
+ axis: PyLegendUnion[int, str] = 0,
234
+ skipna: bool = True,
235
+ numeric_only: bool = False,
236
+ **kwargs: PyLegendPrimitiveOrPythonPrimitive
237
+ ) -> "PandasApiTdsFrame":
238
+ if axis not in [0, "index"]:
239
+ raise NotImplementedError(f"The 'axis' parameter must be 0 or 'index' in min function, but got: {axis}")
240
+ if skipna is not True:
241
+ raise NotImplementedError("skipna=False is not currently supported in min function.")
242
+ if numeric_only is not False:
243
+ raise NotImplementedError("numeric_only=True is not currently supported in min function.")
244
+ if len(kwargs) > 0:
245
+ raise NotImplementedError(
246
+ f"Additional keyword arguments not supported in min function: {list(kwargs.keys())}")
247
+ return self.aggregate("min", 0)
248
+
249
+ def max(
250
+ self,
251
+ axis: PyLegendUnion[int, str] = 0,
252
+ skipna: bool = True,
253
+ numeric_only: bool = False,
254
+ **kwargs: PyLegendPrimitiveOrPythonPrimitive
255
+ ) -> "PandasApiTdsFrame":
256
+ if axis not in [0, "index"]:
257
+ raise NotImplementedError(f"The 'axis' parameter must be 0 or 'index' in max function, but got: {axis}")
258
+ if skipna is not True:
259
+ raise NotImplementedError("skipna=False is not currently supported in max function.")
260
+ if numeric_only is not False:
261
+ raise NotImplementedError("numeric_only=True is not currently supported in max function.")
262
+ if len(kwargs) > 0:
263
+ raise NotImplementedError(
264
+ f"Additional keyword arguments not supported in max function: {list(kwargs.keys())}")
265
+ return self.aggregate("max", 0)
266
+
267
+ def std(
268
+ self,
269
+ axis: PyLegendUnion[int, str] = 0,
270
+ skipna: bool = True,
271
+ ddof: int = 1,
272
+ numeric_only: bool = False,
273
+ **kwargs: PyLegendPrimitiveOrPythonPrimitive
274
+ ) -> "PandasApiTdsFrame":
275
+ if axis not in [0, "index"]:
276
+ raise NotImplementedError(f"The 'axis' parameter must be 0 or 'index' in std function, but got: {axis}")
277
+ if skipna is not True:
278
+ raise NotImplementedError("skipna=False is not currently supported in std function.")
279
+ if ddof != 1:
280
+ raise NotImplementedError(
281
+ f"Only ddof=1 (Sample Standard Deviation) is supported in std function, but got: {ddof}")
282
+ if numeric_only is not False:
283
+ raise NotImplementedError("numeric_only=True is not currently supported in std function.")
284
+ if len(kwargs) > 0:
285
+ raise NotImplementedError(
286
+ f"Additional keyword arguments not supported in std function: {list(kwargs.keys())}")
287
+ return self.aggregate("std", 0)
288
+
289
+ def var(
290
+ self,
291
+ axis: PyLegendUnion[int, str] = 0,
292
+ skipna: bool = True,
293
+ ddof: int = 1,
294
+ numeric_only: bool = False,
295
+ **kwargs: PyLegendPrimitiveOrPythonPrimitive
296
+ ) -> "PandasApiTdsFrame":
297
+ if axis not in [0, "index"]:
298
+ raise NotImplementedError(f"The 'axis' parameter must be 0 or 'index' in var function, but got: {axis}")
299
+ if skipna is not True:
300
+ raise NotImplementedError("skipna=False is not currently supported in var function.")
301
+ if ddof != 1:
302
+ raise NotImplementedError(f"Only ddof=1 (Sample Variance) is supported in var function, but got: {ddof}")
303
+ if numeric_only is not False:
304
+ raise NotImplementedError("numeric_only=True is not currently supported in var function.")
305
+ if len(kwargs) > 0:
306
+ raise NotImplementedError(
307
+ f"Additional keyword arguments not supported in var function: {list(kwargs.keys())}")
308
+ return self.aggregate("var", 0)
309
+
310
+ def count(
311
+ self,
312
+ axis: PyLegendUnion[int, str] = 0,
313
+ numeric_only: bool = False,
314
+ **kwargs: PyLegendPrimitiveOrPythonPrimitive
315
+ ) -> "PandasApiTdsFrame":
316
+ if axis not in [0, "index"]:
317
+ raise NotImplementedError(f"The 'axis' parameter must be 0 or 'index' in count function, but got: {axis}")
318
+ if numeric_only is not False:
319
+ raise NotImplementedError("numeric_only=True is not currently supported in count function.")
320
+ if len(kwargs) > 0:
321
+ raise NotImplementedError(
322
+ f"Additional keyword arguments not supported in count function: {list(kwargs.keys())}")
323
+ return self.aggregate("count", 0)
130
324
 
131
325
 
132
326
  class BooleanSeries(Series, PyLegendBoolean, PyLegendExpressionBooleanReturn): # type: ignore
@@ -36,6 +36,7 @@ __all__: PyLegendSequence[str] = [
36
36
  "PyLegendExpressionDateReturn",
37
37
  "PyLegendExpressionDateTimeReturn",
38
38
  "PyLegendExpressionStrictDateReturn",
39
+ "PyLegendExpressionNullReturn"
39
40
  ]
40
41
 
41
42
 
@@ -86,3 +87,7 @@ class PyLegendExpressionDateTimeReturn(PyLegendExpressionDateReturn, metaclass=A
86
87
 
87
88
  class PyLegendExpressionStrictDateReturn(PyLegendExpressionDateReturn, metaclass=ABCMeta):
88
89
  pass
90
+
91
+
92
+ class PyLegendExpressionNullReturn(PyLegendExpression, metaclass=ABCMeta):
93
+ pass
@@ -27,6 +27,7 @@ from pylegend.core.language.shared.expression import (
27
27
  PyLegendExpressionFloatReturn,
28
28
  PyLegendExpressionDateTimeReturn,
29
29
  PyLegendExpressionStrictDateReturn,
30
+ PyLegendExpressionNullReturn,
30
31
  )
31
32
  from pylegend.core.sql.metamodel import (
32
33
  Expression,
@@ -37,6 +38,7 @@ from pylegend.core.sql.metamodel import (
37
38
  QuerySpecification,
38
39
  Cast,
39
40
  ColumnType,
41
+ NullLiteral,
40
42
  )
41
43
  from pylegend.core.tds.tds_frame import FrameToSqlConfig
42
44
  from pylegend.core.tds.tds_frame import FrameToPureConfig
@@ -180,8 +182,25 @@ class PyLegendStrictDateLiteralExpression(PyLegendExpressionStrictDateReturn):
180
182
  return True
181
183
 
182
184
 
185
+ class PyLegendNullLiteralExpression(PyLegendExpressionNullReturn):
186
+ __value: None
187
+
188
+ def __init__(self) -> None:
189
+ return
190
+
191
+ def to_sql_expression(
192
+ self,
193
+ frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
194
+ config: FrameToSqlConfig
195
+ ) -> Expression:
196
+ return NullLiteral()
197
+
198
+ def to_pure_expression(self, config: FrameToPureConfig) -> str:
199
+ return "[]"
200
+
201
+
183
202
  def convert_literal_to_literal_expression(
184
- literal: PyLegendUnion[int, float, bool, str, datetime, date]
203
+ literal: PyLegendUnion[int, float, bool, str, datetime, date, None]
185
204
  ) -> PyLegendExpression:
186
205
  if isinstance(literal, bool):
187
206
  return PyLegendBooleanLiteralExpression(literal)
@@ -195,5 +214,7 @@ def convert_literal_to_literal_expression(
195
214
  return PyLegendDateTimeLiteralExpression(literal)
196
215
  if isinstance(literal, date):
197
216
  return PyLegendStrictDateLiteralExpression(literal)
217
+ if isinstance(literal, type(None)):
218
+ return PyLegendNullLiteralExpression()
198
219
 
199
220
  raise TypeError(f"Cannot convert value - {literal} of type {type(literal)} to literal expression")
@@ -28,6 +28,8 @@ from pylegend.core.sql.metamodel import (
28
28
  LogicalBinaryExpression,
29
29
  LogicalBinaryType,
30
30
  NotExpression,
31
+ ComparisonExpression,
32
+ ComparisonOperator,
31
33
  )
32
34
  from pylegend.core.tds.tds_frame import FrameToSqlConfig
33
35
  from pylegend.core.tds.tds_frame import FrameToPureConfig
@@ -37,6 +39,11 @@ __all__: PyLegendSequence[str] = [
37
39
  "PyLegendBooleanOrExpression",
38
40
  "PyLegendBooleanAndExpression",
39
41
  "PyLegendBooleanNotExpression",
42
+ "PyLegendBooleanLessThanExpression",
43
+ "PyLegendBooleanLessThanEqualExpression",
44
+ "PyLegendBooleanGreaterThanExpression",
45
+ "PyLegendBooleanGreaterThanEqualExpression",
46
+ "PyLegendBooleanXorExpression"
40
47
  ]
41
48
 
42
49
 
@@ -98,6 +105,143 @@ class PyLegendBooleanAndExpression(PyLegendBinaryExpression, PyLegendExpressionB
98
105
  )
99
106
 
100
107
 
108
+ class PyLegendBooleanLessThanExpression(PyLegendBinaryExpression, PyLegendExpressionBooleanReturn):
109
+
110
+ @staticmethod
111
+ def __to_sql_func(
112
+ expression1: Expression,
113
+ expression2: Expression,
114
+ frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
115
+ config: FrameToSqlConfig
116
+ ) -> Expression:
117
+ return ComparisonExpression(expression1, expression2, ComparisonOperator.LESS_THAN)
118
+
119
+ @staticmethod
120
+ def __to_pure_func(op1_expr: str, op2_expr: str, config: FrameToPureConfig) -> str:
121
+ return f"({op1_expr} < {op2_expr})"
122
+
123
+ def __init__(self, operand1: PyLegendExpressionBooleanReturn, operand2: PyLegendExpressionBooleanReturn) -> None:
124
+ PyLegendExpressionBooleanReturn.__init__(self)
125
+ PyLegendBinaryExpression.__init__(
126
+ self,
127
+ operand1,
128
+ operand2,
129
+ PyLegendBooleanLessThanExpression.__to_sql_func,
130
+ PyLegendBooleanLessThanExpression.__to_pure_func,
131
+ non_nullable=True
132
+ )
133
+
134
+
135
+ class PyLegendBooleanLessThanEqualExpression(PyLegendBinaryExpression, PyLegendExpressionBooleanReturn):
136
+
137
+ @staticmethod
138
+ def __to_sql_func(
139
+ expression1: Expression,
140
+ expression2: Expression,
141
+ frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
142
+ config: FrameToSqlConfig
143
+ ) -> Expression:
144
+ return ComparisonExpression(expression1, expression2, ComparisonOperator.LESS_THAN_OR_EQUAL)
145
+
146
+ @staticmethod
147
+ def __to_pure_func(op1_expr: str, op2_expr: str, config: FrameToPureConfig) -> str:
148
+ return f"({op1_expr} <= {op2_expr})"
149
+
150
+ def __init__(self, operand1: PyLegendExpressionBooleanReturn, operand2: PyLegendExpressionBooleanReturn) -> None:
151
+ PyLegendExpressionBooleanReturn.__init__(self)
152
+ PyLegendBinaryExpression.__init__(
153
+ self,
154
+ operand1,
155
+ operand2,
156
+ PyLegendBooleanLessThanEqualExpression.__to_sql_func,
157
+ PyLegendBooleanLessThanEqualExpression.__to_pure_func,
158
+ non_nullable=True
159
+ )
160
+
161
+
162
+ class PyLegendBooleanGreaterThanExpression(PyLegendBinaryExpression, PyLegendExpressionBooleanReturn):
163
+
164
+ @staticmethod
165
+ def __to_sql_func(
166
+ expression1: Expression,
167
+ expression2: Expression,
168
+ frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
169
+ config: FrameToSqlConfig
170
+ ) -> Expression:
171
+ return ComparisonExpression(expression1, expression2, ComparisonOperator.GREATER_THAN)
172
+
173
+ @staticmethod
174
+ def __to_pure_func(op1_expr: str, op2_expr: str, config: FrameToPureConfig) -> str:
175
+ return f"({op1_expr} > {op2_expr})"
176
+
177
+ def __init__(self, operand1: PyLegendExpressionBooleanReturn, operand2: PyLegendExpressionBooleanReturn) -> None:
178
+ PyLegendExpressionBooleanReturn.__init__(self)
179
+ PyLegendBinaryExpression.__init__(
180
+ self,
181
+ operand1,
182
+ operand2,
183
+ PyLegendBooleanGreaterThanExpression.__to_sql_func,
184
+ PyLegendBooleanGreaterThanExpression.__to_pure_func,
185
+ non_nullable=True
186
+ )
187
+
188
+
189
+ class PyLegendBooleanGreaterThanEqualExpression(PyLegendBinaryExpression, PyLegendExpressionBooleanReturn):
190
+
191
+ @staticmethod
192
+ def __to_sql_func(
193
+ expression1: Expression,
194
+ expression2: Expression,
195
+ frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
196
+ config: FrameToSqlConfig
197
+ ) -> Expression:
198
+ return ComparisonExpression(expression1, expression2, ComparisonOperator.GREATER_THAN_OR_EQUAL)
199
+
200
+ @staticmethod
201
+ def __to_pure_func(op1_expr: str, op2_expr: str, config: FrameToPureConfig) -> str:
202
+ return f"({op1_expr} >= {op2_expr})"
203
+
204
+ def __init__(self, operand1: PyLegendExpressionBooleanReturn, operand2: PyLegendExpressionBooleanReturn) -> None:
205
+ PyLegendExpressionBooleanReturn.__init__(self)
206
+ PyLegendBinaryExpression.__init__(
207
+ self,
208
+ operand1,
209
+ operand2,
210
+ PyLegendBooleanGreaterThanEqualExpression.__to_sql_func,
211
+ PyLegendBooleanGreaterThanEqualExpression.__to_pure_func,
212
+ non_nullable=True
213
+ )
214
+
215
+
216
+ class PyLegendBooleanXorExpression(PyLegendBinaryExpression, PyLegendExpressionBooleanReturn):
217
+
218
+ @staticmethod
219
+ def __to_sql_func(
220
+ expression1: Expression,
221
+ expression2: Expression,
222
+ frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
223
+ config: FrameToSqlConfig
224
+ ) -> Expression:
225
+ return ComparisonExpression(expression1, expression2, ComparisonOperator.NOT_EQUAL)
226
+
227
+ @staticmethod
228
+ def __to_pure_func(op1_expr: str, op2_expr: str, config: FrameToPureConfig) -> str:
229
+ return generate_pure_functional_call("xor", [op1_expr, op2_expr])
230
+
231
+ def __init__(self, operand1: PyLegendExpressionBooleanReturn, operand2: PyLegendExpressionBooleanReturn) -> None:
232
+ PyLegendExpressionBooleanReturn.__init__(self)
233
+ PyLegendBinaryExpression.__init__(
234
+ self,
235
+ operand1,
236
+ operand2,
237
+ PyLegendBooleanXorExpression.__to_sql_func,
238
+ PyLegendBooleanXorExpression.__to_pure_func,
239
+ non_nullable=True,
240
+ first_operand_needs_to_be_non_nullable=True,
241
+ second_operand_needs_to_be_non_nullable=True
242
+ )
243
+
244
+
101
245
  class PyLegendBooleanNotExpression(PyLegendUnaryExpression, PyLegendExpressionBooleanReturn):
102
246
 
103
247
  @staticmethod
@@ -22,8 +22,10 @@ from pylegend.core.language.shared.expression import (
22
22
  PyLegendExpressionStrictDateReturn,
23
23
  PyLegendExpressionIntegerReturn,
24
24
  PyLegendExpressionBooleanReturn,
25
+ PyLegendExpression
25
26
  )
26
27
  from pylegend.core.language.shared.operations.binary_expression import PyLegendBinaryExpression
28
+ from pylegend.core.language.shared.operations.nary_expression import PyLegendNaryExpression
27
29
  from pylegend.core.language.shared.operations.nullary_expression import PyLegendNullaryExpression
28
30
  from pylegend.core.language.shared.operations.unary_expression import PyLegendUnaryExpression
29
31
  from pylegend.core.language.shared.helpers import generate_pure_functional_call
@@ -61,6 +63,10 @@ from pylegend.core.sql.metamodel_extension import (
61
63
  MinuteExpression,
62
64
  SecondExpression,
63
65
  EpochExpression,
66
+ DateAdjustExpression,
67
+ DateDiffExpression,
68
+ DateTimeBucketExpression,
69
+ DateType,
64
70
  )
65
71
 
66
72
 
@@ -91,6 +97,9 @@ __all__: PyLegendSequence[str] = [
91
97
  "PyLegendDateLessThanEqualExpression",
92
98
  "PyLegendDateGreaterThanExpression",
93
99
  "PyLegendDateGreaterThanEqualExpression",
100
+ "PyLegendDateAdjustExpression",
101
+ "PyLegendDateDiffExpression",
102
+ "PyLegendDateTimeBucketExpression",
94
103
  ]
95
104
 
96
105
 
@@ -780,3 +789,85 @@ class PyLegendDateGreaterThanEqualExpression(PyLegendBinaryExpression, PyLegendE
780
789
 
781
790
  def is_non_nullable(self) -> bool:
782
791
  return True
792
+
793
+
794
+ class PyLegendDateAdjustExpression(PyLegendNaryExpression, PyLegendExpressionDateReturn):
795
+
796
+ @staticmethod
797
+ def __to_sql_func(
798
+ expressions: list[Expression],
799
+ frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
800
+ config: FrameToSqlConfig
801
+ ) -> Expression:
802
+ return DateAdjustExpression(expressions[0], expressions[1], expressions[2]) # type:ignore
803
+
804
+ @staticmethod
805
+ def __to_pure_func(op_expr: list[str], config: FrameToPureConfig) -> str:
806
+ return generate_pure_functional_call("adjust", [op_expr[0], op_expr[1], f"DurationUnit.{op_expr[2]}"])
807
+
808
+ def __init__(self, operands: list[PyLegendExpression]) -> None:
809
+ PyLegendExpressionDateReturn.__init__(self)
810
+ PyLegendNaryExpression.__init__(
811
+ self,
812
+ operands,
813
+ PyLegendDateAdjustExpression.__to_sql_func,
814
+ PyLegendDateAdjustExpression.__to_pure_func,
815
+ non_nullable=True,
816
+ operands_non_nullable_flags=[True, True, True]
817
+ )
818
+
819
+
820
+ class PyLegendDateDiffExpression(PyLegendNaryExpression, PyLegendExpressionIntegerReturn):
821
+
822
+ @staticmethod
823
+ def __to_sql_func(
824
+ expressions: list[Expression],
825
+ frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
826
+ config: FrameToSqlConfig
827
+ ) -> Expression:
828
+ return DateDiffExpression(expressions[0], expressions[1], expressions[2]) # type: ignore
829
+
830
+ @staticmethod
831
+ def __to_pure_func(op_expr: list[str], config: FrameToPureConfig) -> str:
832
+ return generate_pure_functional_call("dateDiff", [op_expr[0], op_expr[1], f"DurationUnit.{op_expr[2]}"])
833
+
834
+ def __init__(self, operands: list[PyLegendExpression]) -> None:
835
+ PyLegendExpressionIntegerReturn.__init__(self)
836
+ PyLegendNaryExpression.__init__(
837
+ self,
838
+ operands,
839
+ PyLegendDateDiffExpression.__to_sql_func,
840
+ PyLegendDateDiffExpression.__to_pure_func,
841
+ non_nullable=True,
842
+ operands_non_nullable_flags=[True, True, True]
843
+ )
844
+
845
+
846
+ class PyLegendDateTimeBucketExpression(PyLegendNaryExpression, PyLegendExpressionDateReturn):
847
+
848
+ @staticmethod
849
+ def __to_sql_func(
850
+ expressions: list[Expression],
851
+ frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
852
+ config: FrameToSqlConfig
853
+ ) -> Expression:
854
+ return DateTimeBucketExpression(
855
+ expressions[0],
856
+ expressions[1],
857
+ expressions[2], # type: ignore
858
+ DateType.DateTime if expressions[3].value == "DATETIME" else DateType.StrictDate) # type: ignore
859
+
860
+ @staticmethod
861
+ def __to_pure_func(op_expr: list[str], config: FrameToPureConfig) -> str:
862
+ return generate_pure_functional_call("timeBucket", [op_expr[0], op_expr[1], f"DurationUnit.{op_expr[2]}"])
863
+
864
+ def __init__(self, operands: list[PyLegendExpression]) -> None:
865
+ PyLegendExpressionDateReturn.__init__(self)
866
+ PyLegendNaryExpression.__init__(
867
+ self,
868
+ operands,
869
+ PyLegendDateTimeBucketExpression.__to_sql_func,
870
+ PyLegendDateTimeBucketExpression.__to_pure_func,
871
+ non_nullable=True,
872
+ operands_non_nullable_flags=[True, True, True]
873
+ )