pylegend 0.11.0__tar.gz → 0.13.0__tar.gz

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 (181) hide show
  1. {pylegend-0.11.0 → pylegend-0.13.0}/PKG-INFO +1 -1
  2. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/database/sql_to_string/db_extension.py +244 -6
  3. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/legendql_api/legendql_api_custom_expressions.py +190 -5
  4. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/pandas_api/pandas_api_series.py +3 -0
  5. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/expression.py +5 -0
  6. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/literal_expressions.py +22 -1
  7. pylegend-0.13.0/pylegend/core/language/shared/operations/boolean_operation_expressions.py +268 -0
  8. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/operations/date_operation_expressions.py +91 -0
  9. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/operations/integer_operation_expressions.py +183 -1
  10. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/operations/string_operation_expressions.py +31 -1
  11. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/primitives/boolean.py +40 -0
  12. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/primitives/date.py +39 -0
  13. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/primitives/datetime.py +18 -0
  14. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/primitives/integer.py +54 -1
  15. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/primitives/strictdate.py +25 -1
  16. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/primitives/string.py +16 -2
  17. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/sql/metamodel.py +54 -2
  18. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/sql/metamodel_extension.py +77 -1
  19. pylegend-0.13.0/pylegend/core/tds/legendql_api/frames/functions/legendql_api_distinct_function.py +115 -0
  20. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legendql_api/frames/legendql_api_base_tds_frame.py +146 -4
  21. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legendql_api/frames/legendql_api_tds_frame.py +33 -2
  22. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/pandas_api/frames/functions/assign_function.py +65 -23
  23. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/pandas_api/frames/functions/drop.py +3 -3
  24. pylegend-0.13.0/pylegend/core/tds/pandas_api/frames/functions/dropna.py +167 -0
  25. pylegend-0.13.0/pylegend/core/tds/pandas_api/frames/functions/fillna.py +162 -0
  26. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/pandas_api/frames/functions/filter.py +10 -5
  27. pylegend-0.13.0/pylegend/core/tds/pandas_api/frames/functions/iloc.py +99 -0
  28. pylegend-0.13.0/pylegend/core/tds/pandas_api/frames/functions/loc.py +136 -0
  29. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/pandas_api/frames/functions/truncate_function.py +151 -120
  30. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/pandas_api/frames/pandas_api_applied_function_tds_frame.py +7 -3
  31. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/pandas_api/frames/pandas_api_base_tds_frame.py +340 -34
  32. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/pandas_api/frames/pandas_api_tds_frame.py +90 -9
  33. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/pandas_api/frames/pandas_api_legend_function_input_frame.py +9 -4
  34. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/pandas_api/frames/pandas_api_legend_service_input_frame.py +12 -5
  35. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/pandas_api/frames/pandas_api_table_spec_input_frame.py +12 -4
  36. {pylegend-0.11.0 → pylegend-0.13.0}/pyproject.toml +1 -1
  37. pylegend-0.11.0/pylegend/core/language/shared/operations/boolean_operation_expressions.py +0 -124
  38. pylegend-0.11.0/pylegend/core/tds/legendql_api/frames/functions/legendql_api_distinct_function.py +0 -69
  39. {pylegend-0.11.0 → pylegend-0.13.0}/LICENSE +0 -0
  40. {pylegend-0.11.0 → pylegend-0.13.0}/LICENSE.spdx +0 -0
  41. {pylegend-0.11.0 → pylegend-0.13.0}/NOTICE +0 -0
  42. {pylegend-0.11.0 → pylegend-0.13.0}/README.md +0 -0
  43. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/__init__.py +0 -0
  44. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/_typing.py +0 -0
  45. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/__init__.py +0 -0
  46. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/database/__init__.py +0 -0
  47. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/database/sql_to_string/__init__.py +0 -0
  48. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/database/sql_to_string/config.py +0 -0
  49. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/database/sql_to_string/generator.py +0 -0
  50. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/__init__.py +0 -0
  51. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/legacy_api/__init__.py +0 -0
  52. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/legacy_api/aggregate_specification.py +0 -0
  53. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/legacy_api/legacy_api_tds_row.py +0 -0
  54. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/legendql_api/__init__.py +0 -0
  55. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/legendql_api/legendql_api_tds_row.py +0 -0
  56. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/pandas_api/__init__.py +0 -0
  57. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/pandas_api/pandas_api_aggregate_specification.py +0 -0
  58. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/pandas_api/pandas_api_custom_expressions.py +0 -0
  59. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/pandas_api/pandas_api_tds_row.py +0 -0
  60. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/__init__.py +0 -0
  61. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/column_expressions.py +0 -0
  62. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/functions.py +0 -0
  63. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/helpers.py +0 -0
  64. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/operations/__init__.py +0 -0
  65. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/operations/binary_expression.py +0 -0
  66. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/operations/collection_operation_expressions.py +0 -0
  67. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/operations/float_operation_expressions.py +0 -0
  68. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/operations/nary_expression.py +0 -0
  69. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/operations/nullary_expression.py +0 -0
  70. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/operations/number_operation_expressions.py +0 -0
  71. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/operations/primitive_operation_expressions.py +0 -0
  72. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/operations/unary_expression.py +0 -0
  73. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/pct_helpers.py +0 -0
  74. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/primitive_collection.py +0 -0
  75. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/primitives/__init__.py +0 -0
  76. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/primitives/float.py +0 -0
  77. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/primitives/number.py +0 -0
  78. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/primitives/primitive.py +0 -0
  79. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/language/shared/tds_row.py +0 -0
  80. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/project_cooridnates.py +0 -0
  81. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/request/__init__.py +0 -0
  82. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/request/auth.py +0 -0
  83. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/request/legend_client.py +0 -0
  84. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/request/response_reader.py +0 -0
  85. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/request/service_client.py +0 -0
  86. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/sql/__init__.py +0 -0
  87. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/__init__.py +0 -0
  88. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/abstract/__init__.py +0 -0
  89. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/abstract/frames/__init__.py +0 -0
  90. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/abstract/frames/applied_function_tds_frame.py +0 -0
  91. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/abstract/frames/base_tds_frame.py +0 -0
  92. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/abstract/frames/input_tds_frame.py +0 -0
  93. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/abstract/function_helpers.py +0 -0
  94. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legacy_api/__init__.py +0 -0
  95. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legacy_api/frames/__init__.py +0 -0
  96. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legacy_api/frames/functions/__init__.py +0 -0
  97. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_concatenate_function.py +0 -0
  98. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_distinct_function.py +0 -0
  99. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_drop_function.py +0 -0
  100. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_extend_function.py +0 -0
  101. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_filter_function.py +0 -0
  102. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_group_by_function.py +0 -0
  103. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_head_function.py +0 -0
  104. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_join_by_columns_function.py +0 -0
  105. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_join_function.py +0 -0
  106. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_rename_columns_function.py +0 -0
  107. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_restrict_function.py +0 -0
  108. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_slice_function.py +0 -0
  109. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legacy_api/frames/functions/legacy_api_sort_function.py +0 -0
  110. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legacy_api/frames/legacy_api_applied_function_tds_frame.py +0 -0
  111. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legacy_api/frames/legacy_api_base_tds_frame.py +0 -0
  112. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legacy_api/frames/legacy_api_input_tds_frame.py +0 -0
  113. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legacy_api/frames/legacy_api_tds_frame.py +0 -0
  114. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legendql_api/__init__.py +0 -0
  115. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legendql_api/frames/__init__.py +0 -0
  116. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legendql_api/frames/functions/__init__.py +0 -0
  117. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_asofjoin_function.py +0 -0
  118. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_concatenate_function.py +0 -0
  119. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_drop_function.py +0 -0
  120. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_extend_function.py +0 -0
  121. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_filter_function.py +0 -0
  122. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_function_helpers.py +0 -0
  123. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_groupby_function.py +0 -0
  124. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_head_function.py +0 -0
  125. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_join_function.py +0 -0
  126. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_project_function.py +0 -0
  127. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_rename_function.py +0 -0
  128. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_select_function.py +0 -0
  129. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_slice_function.py +0 -0
  130. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_sort_function.py +0 -0
  131. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legendql_api/frames/functions/legendql_api_window_extend_function.py +0 -0
  132. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legendql_api/frames/legendql_api_applied_function_tds_frame.py +0 -0
  133. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/legendql_api/frames/legendql_api_input_tds_frame.py +0 -0
  134. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/pandas_api/__init__.py +0 -0
  135. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/pandas_api/frames/__init__.py +0 -0
  136. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/pandas_api/frames/functions/__init__.py +0 -0
  137. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/pandas_api/frames/functions/aggregate_function.py +0 -0
  138. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/pandas_api/frames/functions/filtering.py +0 -0
  139. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/pandas_api/frames/functions/merge.py +0 -0
  140. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/pandas_api/frames/functions/rename.py +0 -0
  141. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/pandas_api/frames/functions/sort_values_function.py +0 -0
  142. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/pandas_api/frames/pandas_api_groupby_tds_frame.py +0 -0
  143. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/pandas_api/frames/pandas_api_input_tds_frame.py +0 -0
  144. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/result_handler/__init__.py +0 -0
  145. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/result_handler/result_handler.py +0 -0
  146. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/result_handler/to_csv_file_result_handler.py +0 -0
  147. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/result_handler/to_json_file_result_handler.py +0 -0
  148. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/result_handler/to_string_result_handler.py +0 -0
  149. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/sql_query_helpers.py +0 -0
  150. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/tds_column.py +0 -0
  151. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/core/tds/tds_frame.py +0 -0
  152. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/__init__.py +0 -0
  153. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/database/__init__.py +0 -0
  154. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/database/vendors/__init__.py +0 -0
  155. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/database/vendors/postgres/__init__.py +0 -0
  156. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/database/vendors/postgres/postgres_sql_to_string.py +0 -0
  157. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/__init__.py +0 -0
  158. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/abstract/__init__.py +0 -0
  159. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/abstract/csv_tds_frame.py +0 -0
  160. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/abstract/legend_function_input_frame.py +0 -0
  161. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/abstract/legend_service_input_frame.py +0 -0
  162. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/abstract/table_spec_input_frame.py +0 -0
  163. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/legacy_api/__init__.py +0 -0
  164. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/legacy_api/frames/__init__.py +0 -0
  165. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/legacy_api/frames/legacy_api_legend_function_input_frame.py +0 -0
  166. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/legacy_api/frames/legacy_api_legend_service_input_frame.py +0 -0
  167. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/legacy_api/frames/legacy_api_table_spec_input_frame.py +0 -0
  168. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/legendql_api/__init__.py +0 -0
  169. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/legendql_api/frames/__init__.py +0 -0
  170. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/legendql_api/frames/legendql_api_csv_input_frame.py +0 -0
  171. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/legendql_api/frames/legendql_api_legend_function_input_frame.py +0 -0
  172. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/legendql_api/frames/legendql_api_legend_service_input_frame.py +0 -0
  173. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/legendql_api/frames/legendql_api_table_spec_input_frame.py +0 -0
  174. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/pandas_api/__init__.py +0 -0
  175. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/pandas_api/frames/__init__.py +0 -0
  176. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/result_handler/__init__.py +0 -0
  177. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/extensions/tds/result_handler/to_pandas_df_result_handler.py +0 -0
  178. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/legacy_api_tds_client.py +0 -0
  179. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/legendql_api_tds_client.py +0 -0
  180. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/utils/__init__.py +0 -0
  181. {pylegend-0.11.0 → pylegend-0.13.0}/pylegend/utils/class_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pylegend
3
- Version: 0.11.0
3
+ Version: 0.13.0
4
4
  Summary: Python language binding for Legend data management platform
5
5
  License: Apache-2.0
6
6
  License-File: LICENSE
@@ -72,6 +72,14 @@ from pylegend.core.sql.metamodel import (
72
72
  Window,
73
73
  TableFunction,
74
74
  Union,
75
+ WindowFrame,
76
+ WindowFrameMode,
77
+ FrameBound,
78
+ FrameBoundType,
79
+ BitwiseShiftExpression,
80
+ BitwiseShiftDirection,
81
+ BitwiseBinaryExpression,
82
+ BitwiseBinaryOperator
75
83
  )
76
84
  from pylegend.core.sql.metamodel_extension import (
77
85
  StringLengthExpression,
@@ -133,6 +141,11 @@ from pylegend.core.sql.metamodel_extension import (
133
141
  WindowExpression,
134
142
  ConstantExpression,
135
143
  StringSubStringExpression,
144
+ DateAdjustExpression,
145
+ BitwiseNotExpression,
146
+ DateDiffExpression,
147
+ DateTimeBucketExpression,
148
+ DateType,
136
149
  )
137
150
 
138
151
  __all__: PyLegendSequence[str] = [
@@ -456,6 +469,18 @@ def expression_processor(
456
469
  return expression.name
457
470
  elif isinstance(expression, StringSubStringExpression):
458
471
  return extension.process_string_substring_expression(expression, config)
472
+ elif isinstance(expression, DateAdjustExpression):
473
+ return extension.process_date_adjust_expression(expression, config)
474
+ elif isinstance(expression, DateDiffExpression):
475
+ return extension.process_date_diff_expression(expression, config)
476
+ elif isinstance(expression, DateTimeBucketExpression):
477
+ return extension.process_date_time_bucket_expression(expression, config)
478
+ elif isinstance(expression, BitwiseNotExpression):
479
+ return extension.process_bitwise_not_expression(expression, config)
480
+ elif isinstance(expression, BitwiseShiftExpression):
481
+ return extension.process_bitwise_shift_expression(expression, config)
482
+ elif isinstance(expression, BitwiseBinaryExpression):
483
+ return extension.process_bitwise_binary_expression(expression, config)
459
484
 
460
485
  else:
461
486
  raise ValueError("Unsupported expression type: " + str(type(expression))) # pragma: no cover
@@ -516,6 +541,139 @@ def logical_binary_expression_processor(
516
541
  return f"({left} {op} {right})"
517
542
 
518
543
 
544
+ def bitwise_binary_expression_processor(
545
+ bitwise: BitwiseBinaryExpression,
546
+ extension: "SqlToStringDbExtension",
547
+ config: SqlToStringConfig
548
+ ) -> str:
549
+ op_type = bitwise.operator
550
+ if op_type == BitwiseBinaryOperator.AND:
551
+ op = "&"
552
+ elif op_type == BitwiseBinaryOperator.OR:
553
+ op = "|"
554
+ elif op_type == BitwiseBinaryOperator.XOR:
555
+ op = "#"
556
+ else:
557
+ raise ValueError("Unknown bitwise binary operator type: " + str(op_type)) # pragma: no cover
558
+
559
+ left = extension.process_expression(bitwise.left, config)
560
+ right = extension.process_expression(bitwise.right, config)
561
+ return f"({left} {op} {right})"
562
+
563
+
564
+ def date_diff_processor(
565
+ date_diff: DateDiffExpression,
566
+ extension: "SqlToStringDbExtension",
567
+ config: SqlToStringConfig
568
+ ) -> str:
569
+ unit = date_diff.duration_unit.value
570
+
571
+ end = extension.process_expression(date_diff.end_date, config)
572
+ start = extension.process_expression(date_diff.start_date, config)
573
+
574
+ def extract_diff(part: str) -> str:
575
+ return f"(EXTRACT({part} FROM {end}) - EXTRACT({part} FROM {start}))"
576
+
577
+ year_diff = extract_diff("YEAR")
578
+ month_diff = extract_diff("MONTH")
579
+ # d1 - d2 → Pure dateDiff(d1, d2) → evaluated as d2 - d1
580
+ # Reverse to preserve expected semantics.
581
+ # only for days
582
+ day_diff = f"CAST(CAST({start} AS DATE) - CAST({end} AS DATE) AS INTEGER)"
583
+ epoch_diff = f"(EXTRACT(EPOCH FROM {end}) - EXTRACT(EPOCH FROM {start}))"
584
+
585
+ if unit == "YEARS":
586
+ return year_diff
587
+
588
+ if unit == "MONTHS":
589
+ return f"({year_diff} * 12 + {month_diff})"
590
+
591
+ if unit == "DAYS":
592
+ return day_diff
593
+
594
+ if unit == "WEEKS":
595
+ return f"CAST(FLOOR({day_diff} / 7) AS INTEGER)"
596
+
597
+ if unit == "HOURS":
598
+ return f"CAST(FLOOR({epoch_diff} / 3600) AS INTEGER)"
599
+
600
+ if unit == "MINUTES":
601
+ return f"CAST(FLOOR({epoch_diff} / 60) AS INTEGER)"
602
+
603
+ if unit == "SECONDS":
604
+ return f"CAST({epoch_diff} AS BIGINT)"
605
+
606
+ if unit == "MILLISECONDS":
607
+ return f"CAST({epoch_diff} * 1000 AS BIGINT)"
608
+
609
+ raise ValueError(f"Unsupported DATE DIFF unit: {unit}") # pragma: no cover
610
+
611
+
612
+ def date_time_bucket_processor(
613
+ expression: DateTimeBucketExpression,
614
+ extension: "SqlToStringDbExtension",
615
+ config: SqlToStringConfig
616
+ ) -> str:
617
+ unit = expression.duration_unit.value
618
+ ts = extension.process_expression(expression.date, config)
619
+ q = extension.process_expression(expression.quantity, config)
620
+
621
+ def coerce_to_datetime(sql: str) -> str:
622
+ return (
623
+ f"(({sql}) + INTERVAL '0 second')"
624
+ if expression.date_type == DateType.DateTime
625
+ else sql
626
+ )
627
+
628
+ def epoch() -> str:
629
+ return (
630
+ f"EXTRACT(EPOCH FROM {ts})"
631
+ )
632
+
633
+ if unit == "YEARS":
634
+ return coerce_to_datetime(
635
+ f"make_date(1970,1,1) + "
636
+ f"(FLOOR((EXTRACT(YEAR FROM {ts}) - 1970) / {q}) * {q}) * INTERVAL '1 year'"
637
+ )
638
+
639
+ if unit == "MONTHS":
640
+ total_months_sql = f"((EXTRACT(YEAR FROM {ts}) - 1970) * 12 + (EXTRACT(MONTH FROM {ts}) - 1))"
641
+ return coerce_to_datetime(
642
+ f"make_date(1970,1,1) + "
643
+ f"(FLOOR({total_months_sql} / {q}) * {q}) * INTERVAL '1 month'"
644
+ )
645
+
646
+ if unit == "WEEKS":
647
+ return coerce_to_datetime(
648
+ f"make_date(1969,12,29) + ("
649
+ f"FLOOR(("
650
+ f"{epoch()} - EXTRACT(EPOCH FROM make_date(1969,12,29))"
651
+ f") / (86400 * {q} * 7))"
652
+ f") * ({q} * 7) * INTERVAL '1 day'"
653
+ )
654
+
655
+ if unit == "DAYS":
656
+ days_from_1970 = f"({epoch()} / 86400)"
657
+ return coerce_to_datetime(
658
+ f"make_date(1970,1,1) + "
659
+ f"(FLOOR({days_from_1970} / {q}) * {q}) * INTERVAL '1 day'"
660
+ )
661
+
662
+ unit_seconds_map = {
663
+ "HOURS": 3600,
664
+ "MINUTES": 60,
665
+ "SECONDS": 1
666
+ }
667
+
668
+ if unit in unit_seconds_map:
669
+ seconds_per_unit = unit_seconds_map[unit]
670
+ return (f"(make_date(1970,1,1) + "
671
+ f"(FLOOR({epoch()} / ({q} * {seconds_per_unit})) * ({q} * {seconds_per_unit})) "
672
+ f"* INTERVAL '1 second')")
673
+
674
+ raise ValueError(f"Unsupported TIME BUCKET unit: {unit}") # pragma: no cover
675
+
676
+
519
677
  def not_expression_processor(
520
678
  not_expression: NotExpression,
521
679
  extension: "SqlToStringDbExtension",
@@ -864,14 +1022,27 @@ def window_processor(
864
1022
  if window.windowRef:
865
1023
  return window.windowRef
866
1024
 
867
- partitions = "PARTITION BY " + (", ".join([extension.process_expression(e, config) for e in window.partitions])) \
868
- if window.partitions else ""
1025
+ clauses: list[str] = []
869
1026
 
870
- order_by = "ORDER BY " + (", ".join([extension.process_sort_item(o, config) for o in window.orderBy])) \
871
- if window.orderBy else ""
1027
+ if window.partitions:
1028
+ partition_clause = ", ".join(
1029
+ extension.process_expression(expr, config)
1030
+ for expr in window.partitions
1031
+ )
1032
+ clauses.append(f"PARTITION BY {partition_clause}")
1033
+
1034
+ if window.orderBy:
1035
+ order_clause = ", ".join(
1036
+ extension.process_sort_item(item, config)
1037
+ for item in window.orderBy
1038
+ )
1039
+ clauses.append(f"ORDER BY {order_clause}")
872
1040
 
873
- # TODO: Handle window frame
874
- return f"OVER ({partitions}{' ' if (partitions != '') and (order_by != '') else ''}{order_by}){''}"
1041
+ if window.windowFrame:
1042
+ frame_clause = extension.process_window_frame(window.windowFrame, config)
1043
+ clauses.append(frame_clause)
1044
+
1045
+ return f"OVER ({' '.join(clauses)})"
875
1046
 
876
1047
 
877
1048
  def table_function_processor(
@@ -901,6 +1072,45 @@ def union_processor(
901
1072
  return f"{left}{sep0}{union_str}{sep0}{right}"
902
1073
 
903
1074
 
1075
+ def frame_bound_processor(
1076
+ frame_bound: FrameBound,
1077
+ extension: "SqlToStringDbExtension",
1078
+ config: SqlToStringConfig,
1079
+ ) -> str:
1080
+ bound_sql = {
1081
+ FrameBoundType.UNBOUNDED_PRECEDING: "UNBOUNDED PRECEDING",
1082
+ FrameBoundType.PRECEDING: "PRECEDING",
1083
+ FrameBoundType.FOLLOWING: "FOLLOWING",
1084
+ FrameBoundType.CURRENT_ROW: "CURRENT ROW",
1085
+ FrameBoundType.UNBOUNDED_FOLLOWING: "UNBOUNDED FOLLOWING",
1086
+ }[frame_bound.type_]
1087
+
1088
+ if frame_bound.value is None:
1089
+ return bound_sql
1090
+
1091
+ offset_expr = extension.process_expression(frame_bound.value, config)
1092
+
1093
+ offset_sql = (
1094
+ f"INTERVAL '{offset_expr} {frame_bound.duration_unit.value}'"
1095
+ if frame_bound.duration_unit
1096
+ else offset_expr
1097
+ )
1098
+
1099
+ return f"{offset_sql} {bound_sql}"
1100
+
1101
+
1102
+ def window_frame_processor(
1103
+ frame: WindowFrame,
1104
+ extension: "SqlToStringDbExtension",
1105
+ config: SqlToStringConfig,
1106
+ ) -> str:
1107
+ mode = "ROWS" if frame.mode == WindowFrameMode.ROWS else "RANGE"
1108
+ start = extension.process_frame_bound(frame.start, config)
1109
+ end = extension.process_frame_bound(frame.end, config) if frame.end else "UNBOUNDED FOLLOWING"
1110
+
1111
+ return f"{mode} BETWEEN {start} AND {end}"
1112
+
1113
+
904
1114
  class SqlToStringDbExtension:
905
1115
  @classmethod
906
1116
  def reserved_keywords(cls) -> PyLegendList[str]:
@@ -1260,3 +1470,31 @@ class SqlToStringDbExtension:
1260
1470
 
1261
1471
  def process_union(self, union: Union, config: SqlToStringConfig, nested_subquery: bool = False) -> str:
1262
1472
  return union_processor(union, self, config, nested_subquery)
1473
+
1474
+ def process_window_frame(self, frame: WindowFrame, config: SqlToStringConfig) -> str:
1475
+ return window_frame_processor(frame, self, config)
1476
+
1477
+ def process_frame_bound(self, frame_bound: FrameBound, config: SqlToStringConfig) -> str:
1478
+ return frame_bound_processor(frame_bound, self, config)
1479
+
1480
+ def process_date_adjust_expression(self, expr: DateAdjustExpression, config: SqlToStringConfig) -> str:
1481
+ return (f"({self.process_expression(expr.date, config)}::DATE + "
1482
+ f"(INTERVAL '{self.process_expression(expr.number, config)} "
1483
+ f"{expr.duration_unit.value.upper()}'))::DATE")
1484
+
1485
+ def process_bitwise_not_expression(self, expr: BitwiseNotExpression, config: SqlToStringConfig) -> str:
1486
+ return f"~({self.process_expression(expr.value, config)})"
1487
+
1488
+ def process_bitwise_shift_expression(self, expr: BitwiseShiftExpression, config: SqlToStringConfig) -> str:
1489
+ return (f"({self.process_expression(expr.value, config)} "
1490
+ f"{'>>' if expr.direction == BitwiseShiftDirection.RIGHT else '<<'} "
1491
+ f"{self.process_expression(expr.shift, config)})")
1492
+
1493
+ def process_bitwise_binary_expression(self, expr: BitwiseBinaryExpression, config: SqlToStringConfig) -> str:
1494
+ return bitwise_binary_expression_processor(expr, self, config)
1495
+
1496
+ def process_date_diff_expression(self, expr: DateDiffExpression, config: SqlToStringConfig) -> str:
1497
+ return date_diff_processor(expr, self, config)
1498
+
1499
+ def process_date_time_bucket_expression(self, expr: DateTimeBucketExpression, config: SqlToStringConfig) -> str:
1500
+ return date_time_bucket_processor(expr, self, config)
@@ -27,12 +27,14 @@ from pylegend.core.language import (
27
27
  PyLegendColumnExpression,
28
28
  PyLegendExpressionIntegerReturn,
29
29
  PyLegendExpressionFloatReturn,
30
+ convert_literal_to_literal_expression,
30
31
  )
31
32
  from pylegend._typing import (
32
33
  PyLegendSequence,
33
34
  PyLegendOptional,
34
35
  PyLegendList,
35
36
  PyLegendDict,
37
+ PyLegendUnion,
36
38
  )
37
39
  from pylegend.core.language.shared.helpers import escape_column_name
38
40
  from pylegend.core.sql.metamodel import (
@@ -44,7 +46,13 @@ from pylegend.core.sql.metamodel import (
44
46
  SortItemNullOrdering,
45
47
  Window,
46
48
  FunctionCall,
47
- QualifiedName, IntegerLiteral
49
+ QualifiedName,
50
+ IntegerLiteral,
51
+ WindowFrame,
52
+ WindowFrameMode,
53
+ FrameBound,
54
+ FrameBoundType,
55
+ StringLiteral
48
56
  )
49
57
  from pylegend.core.tds.tds_frame import FrameToSqlConfig, FrameToPureConfig
50
58
  from typing import TYPE_CHECKING
@@ -64,6 +72,11 @@ __all__: PyLegendSequence[str] = [
64
72
  "LegendQLApiWindow",
65
73
  "LegendQLApiPartialFrame",
66
74
  "LegendQLApiWindowReference",
75
+ "LegendQLApiWindowFrameBound",
76
+ "LegendQLApiWindowFrameMode",
77
+ "LegendQLApiWindowFrame",
78
+ "LegendQLApiDurationUnit",
79
+ "LegendQLApiWindowFrameBoundType"
67
80
  ]
68
81
 
69
82
 
@@ -168,8 +181,174 @@ class LegendQLApiSortInfo:
168
181
  return f"{func}(~{escape_column_name(self.__column)})"
169
182
 
170
183
 
171
- class LegendQLApiWindowFrame(metaclass=ABCMeta):
172
- pass
184
+ class LegendQLApiWindowFrameMode(Enum):
185
+ ROWS = 1
186
+ RANGE = 2
187
+
188
+
189
+ class LegendQLApiWindowFrameBoundType(Enum):
190
+ UNBOUNDED_PRECEDING = 1
191
+ PRECEDING = 2
192
+ CURRENT_ROW = 3
193
+ FOLLOWING = 4
194
+ UNBOUNDED_FOLLOWING = 5
195
+
196
+ def to_sql_node(
197
+ self,
198
+ query: QuerySpecification,
199
+ config: FrameToSqlConfig,
200
+ ) -> FrameBoundType:
201
+ mapping = {
202
+ LegendQLApiWindowFrameBoundType.UNBOUNDED_PRECEDING: FrameBoundType.UNBOUNDED_PRECEDING,
203
+ LegendQLApiWindowFrameBoundType.PRECEDING: FrameBoundType.PRECEDING,
204
+ LegendQLApiWindowFrameBoundType.CURRENT_ROW: FrameBoundType.CURRENT_ROW,
205
+ LegendQLApiWindowFrameBoundType.FOLLOWING: FrameBoundType.FOLLOWING,
206
+ LegendQLApiWindowFrameBoundType.UNBOUNDED_FOLLOWING: FrameBoundType.UNBOUNDED_FOLLOWING
207
+ }
208
+
209
+ return mapping[self]
210
+
211
+
212
+ class LegendQLApiDurationUnit(Enum):
213
+ YEARS = 1
214
+ MONTHS = 2
215
+ WEEKS = 3
216
+ DAYS = 4
217
+ HOURS = 5
218
+ MINUTES = 6
219
+ SECONDS = 7
220
+ MILLISECONDS = 8
221
+ MICROSECONDS = 9
222
+ NANOSECONDS = 10
223
+
224
+ def to_pure_expression(self, config: FrameToPureConfig) -> str:
225
+ return self.name
226
+
227
+ def to_sql_node(
228
+ self,
229
+ query: QuerySpecification,
230
+ config: FrameToSqlConfig
231
+ ) -> StringLiteral:
232
+ mapping = {
233
+ LegendQLApiDurationUnit.YEARS: "YEAR",
234
+ LegendQLApiDurationUnit.MONTHS: "MONTH",
235
+ LegendQLApiDurationUnit.WEEKS: "WEEK",
236
+ LegendQLApiDurationUnit.DAYS: "DAY",
237
+ LegendQLApiDurationUnit.HOURS: "HOUR",
238
+ LegendQLApiDurationUnit.MINUTES: "MINUTE",
239
+ LegendQLApiDurationUnit.SECONDS: "SECOND",
240
+ LegendQLApiDurationUnit.MILLISECONDS: "MILLISECOND",
241
+ LegendQLApiDurationUnit.MICROSECONDS: "MICROSECOND",
242
+ LegendQLApiDurationUnit.NANOSECONDS: "NANOSECOND",
243
+ }
244
+
245
+ return StringLiteral(mapping[self], quoted=False)
246
+
247
+ @classmethod
248
+ def from_string(cls, value: str) -> "LegendQLApiDurationUnit":
249
+ try:
250
+ return cls[value.upper()]
251
+ except KeyError:
252
+ raise ValueError(
253
+ f"Invalid duration unit '{value}'. "
254
+ f"Supported values: {[u.name.lower() for u in cls]}"
255
+ )
256
+
257
+
258
+ class LegendQLApiWindowFrameBound:
259
+ __bound_type: LegendQLApiWindowFrameBoundType
260
+ __row_offset: PyLegendOptional[PyLegendUnion[int, float]]
261
+ __duration_unit: PyLegendOptional[LegendQLApiDurationUnit]
262
+
263
+ def __init__(
264
+ self,
265
+ bound_type: LegendQLApiWindowFrameBoundType,
266
+ row_offset: PyLegendOptional[PyLegendUnion[int, float]] = None,
267
+ duration_unit: PyLegendOptional[LegendQLApiDurationUnit] = None,
268
+ ) -> None:
269
+ if bound_type in (
270
+ LegendQLApiWindowFrameBoundType.PRECEDING,
271
+ LegendQLApiWindowFrameBoundType.FOLLOWING,
272
+ ) and row_offset is None:
273
+ raise ValueError(f"row_offset must be provided for bound_type {bound_type.name}")
274
+
275
+ if bound_type not in (
276
+ LegendQLApiWindowFrameBoundType.PRECEDING,
277
+ LegendQLApiWindowFrameBoundType.FOLLOWING,
278
+ ) and row_offset is not None:
279
+ raise ValueError(f"row_offset is not allowed for bound_type {bound_type.name}")
280
+
281
+ self.__bound_type = bound_type
282
+ self.__row_offset = row_offset
283
+ self.__duration_unit = duration_unit
284
+
285
+ def to_pure_expression(self, config: FrameToPureConfig) -> str:
286
+ if (self.__bound_type == LegendQLApiWindowFrameBoundType.UNBOUNDED_FOLLOWING
287
+ or self.__bound_type == LegendQLApiWindowFrameBoundType.UNBOUNDED_PRECEDING):
288
+ return "unbounded()"
289
+
290
+ elif self.__bound_type == LegendQLApiWindowFrameBoundType.CURRENT_ROW:
291
+ expr = "0"
292
+
293
+ else:
294
+ expr = convert_literal_to_literal_expression(self.__row_offset).to_pure_expression(
295
+ config) if self.__row_offset is not None else ""
296
+
297
+ if self.__duration_unit is not None:
298
+ expr += f", DurationUnit.{self.__duration_unit.to_pure_expression(config)}"
299
+
300
+ return expr
301
+
302
+ def to_sql_node(
303
+ self,
304
+ query: QuerySpecification,
305
+ config: FrameToSqlConfig,
306
+ ) -> FrameBound:
307
+ value = (convert_literal_to_literal_expression(abs(self.__row_offset))
308
+ .to_sql_expression({"w": query}, config)) \
309
+ if self.__row_offset is not None else None
310
+
311
+ frame_bound_type = self.__bound_type.to_sql_node(query, config)
312
+
313
+ duration_unit = self.__duration_unit.to_sql_node(
314
+ query,
315
+ config) if self.__duration_unit is not None else None
316
+
317
+ return FrameBound(frame_bound_type, value, duration_unit)
318
+
319
+
320
+ class LegendQLApiWindowFrame:
321
+ __mode: LegendQLApiWindowFrameMode
322
+ __start_bound: LegendQLApiWindowFrameBound
323
+ __end_bound: LegendQLApiWindowFrameBound
324
+
325
+ def __init__(
326
+ self,
327
+ mode: LegendQLApiWindowFrameMode,
328
+ start_bound: LegendQLApiWindowFrameBound,
329
+ end_bound: LegendQLApiWindowFrameBound,
330
+ ) -> None:
331
+ self.__mode = mode
332
+ self.__start_bound = start_bound
333
+ self.__end_bound = end_bound
334
+
335
+ def to_pure_expression(self, config: FrameToPureConfig) -> str:
336
+ mode_str = "rows" if self.__mode == LegendQLApiWindowFrameMode.ROWS else "_range"
337
+ start_expr = self.__start_bound.to_pure_expression(config)
338
+ end_expr = self.__end_bound.to_pure_expression(config)
339
+
340
+ return f"{mode_str}({start_expr}, {end_expr})"
341
+
342
+ def to_sql_node(
343
+ self,
344
+ query: QuerySpecification,
345
+ config: FrameToSqlConfig
346
+ ) -> WindowFrame:
347
+ return WindowFrame(
348
+ mode=WindowFrameMode.ROWS if self.__mode == LegendQLApiWindowFrameMode.ROWS else WindowFrameMode.RANGE,
349
+ start=self.__start_bound.to_sql_node(query, config),
350
+ end=self.__end_bound.to_sql_node(query, config),
351
+ )
173
352
 
174
353
 
175
354
  class LegendQLApiWindow:
@@ -211,7 +390,10 @@ class LegendQLApiWindow:
211
390
  [] if self.__order_by is None else
212
391
  [sort_info.to_sql_node(query, config) for sort_info in self.__order_by]
213
392
  ),
214
- windowFrame=None
393
+ windowFrame=(
394
+ None if self.__frame is None else
395
+ self.__frame.to_sql_node(query, config)
396
+ ),
215
397
  )
216
398
 
217
399
  @staticmethod
@@ -235,7 +417,10 @@ class LegendQLApiWindow:
235
417
  "[]" if self.__order_by is None or len(self.__order_by) == 0
236
418
  else "[" + (', '.join([s.to_pure_expression(config) for s in self.__order_by])) + "]"
237
419
  )
238
- return f"over({partitions_str}, {sorts_str})"
420
+
421
+ frame_str = f", {self.__frame.to_pure_expression(config)}" if self.__frame else ""
422
+
423
+ return f"over({partitions_str}, {sorts_str}{frame_str})"
239
424
 
240
425
 
241
426
  class LegendQLApiPartialFrame:
@@ -77,6 +77,9 @@ class Series(PyLegendColumnExpression, PyLegendPrimitive, BaseTdsFrame):
77
77
  def value(self) -> PyLegendColumnExpression:
78
78
  return self
79
79
 
80
+ def get_base_frame(self) -> "PandasApiTdsFrame":
81
+ return self.__base_frame
82
+
80
83
  def to_sql_expression(
81
84
  self,
82
85
  frame_name_to_base_query_map: PyLegendDict[str, QuerySpecification],
@@ -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")