pytrilogy 0.0.3.34__tar.gz → 0.0.3.35__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.

Potentially problematic release.


This version of pytrilogy might be problematic. Click here for more details.

Files changed (140) hide show
  1. {pytrilogy-0.0.3.34/pytrilogy.egg-info → pytrilogy-0.0.3.35}/PKG-INFO +1 -1
  2. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35/pytrilogy.egg-info}/PKG-INFO +1 -1
  3. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/pytrilogy.egg-info/SOURCES.txt +2 -2
  4. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/__init__.py +1 -1
  5. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/functions.py +17 -4
  6. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/models/author.py +20 -4
  7. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/models/build.py +35 -4
  8. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/statements/author.py +0 -1
  9. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/parsing/common.py +1 -1
  10. pytrilogy-0.0.3.35/trilogy/std/report.preql +5 -0
  11. pytrilogy-0.0.3.34/trilogy/std/dashboard.preql +0 -5
  12. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/LICENSE.md +0 -0
  13. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/README.md +0 -0
  14. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/pyproject.toml +0 -0
  15. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/pytrilogy.egg-info/dependency_links.txt +0 -0
  16. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/pytrilogy.egg-info/entry_points.txt +0 -0
  17. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/pytrilogy.egg-info/requires.txt +0 -0
  18. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/pytrilogy.egg-info/top_level.txt +0 -0
  19. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/setup.cfg +0 -0
  20. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/setup.py +0 -0
  21. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_datatypes.py +0 -0
  22. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_declarations.py +0 -0
  23. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_derived_concepts.py +0 -0
  24. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_discovery_nodes.py +0 -0
  25. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_enums.py +0 -0
  26. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_environment.py +0 -0
  27. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_executor.py +0 -0
  28. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_functions.py +0 -0
  29. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_imports.py +0 -0
  30. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_metadata.py +0 -0
  31. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_models.py +0 -0
  32. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_multi_join_assignments.py +0 -0
  33. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_parse_engine.py +0 -0
  34. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_parsing.py +0 -0
  35. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_partial_handling.py +0 -0
  36. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_query_processing.py +0 -0
  37. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_query_render.py +0 -0
  38. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_select.py +0 -0
  39. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_show.py +0 -0
  40. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_statements.py +0 -0
  41. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_typing.py +0 -0
  42. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_undefined_concept.py +0 -0
  43. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_user_functions.py +0 -0
  44. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/tests/test_where_clause.py +0 -0
  45. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/authoring/__init__.py +0 -0
  46. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/compiler.py +0 -0
  47. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/constants.py +0 -0
  48. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/__init__.py +0 -0
  49. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/constants.py +0 -0
  50. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/enums.py +0 -0
  51. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/env_processor.py +0 -0
  52. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/environment_helpers.py +0 -0
  53. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/ergonomics.py +0 -0
  54. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/exceptions.py +0 -0
  55. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/graph_models.py +0 -0
  56. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/internal.py +0 -0
  57. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/models/__init__.py +0 -0
  58. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/models/build_environment.py +0 -0
  59. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/models/core.py +0 -0
  60. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/models/datasource.py +0 -0
  61. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/models/environment.py +0 -0
  62. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/models/execute.py +0 -0
  63. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/optimization.py +0 -0
  64. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/optimizations/__init__.py +0 -0
  65. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/optimizations/base_optimization.py +0 -0
  66. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/optimizations/inline_constant.py +0 -0
  67. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/optimizations/inline_datasource.py +0 -0
  68. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
  69. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/__init__.py +0 -0
  70. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/concept_strategies_v3.py +0 -0
  71. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/graph_utils.py +0 -0
  72. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/node_generators/__init__.py +0 -0
  73. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/node_generators/basic_node.py +0 -0
  74. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/node_generators/common.py +0 -0
  75. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/node_generators/filter_node.py +0 -0
  76. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/node_generators/group_node.py +0 -0
  77. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
  78. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
  79. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
  80. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
  81. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
  82. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
  83. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
  84. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/node_generators/select_node.py +0 -0
  85. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
  86. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/node_generators/union_node.py +0 -0
  87. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
  88. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/node_generators/window_node.py +0 -0
  89. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/nodes/__init__.py +0 -0
  90. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/nodes/base_node.py +0 -0
  91. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/nodes/filter_node.py +0 -0
  92. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/nodes/group_node.py +0 -0
  93. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/nodes/merge_node.py +0 -0
  94. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
  95. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/nodes/union_node.py +0 -0
  96. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/nodes/unnest_node.py +0 -0
  97. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/nodes/window_node.py +0 -0
  98. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/processing/utility.py +0 -0
  99. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/query_processor.py +0 -0
  100. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/statements/__init__.py +0 -0
  101. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/statements/build.py +0 -0
  102. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/statements/common.py +0 -0
  103. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/core/statements/execute.py +0 -0
  104. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/dialect/__init__.py +0 -0
  105. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/dialect/base.py +0 -0
  106. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/dialect/bigquery.py +0 -0
  107. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/dialect/common.py +0 -0
  108. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/dialect/config.py +0 -0
  109. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/dialect/dataframe.py +0 -0
  110. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/dialect/duckdb.py +0 -0
  111. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/dialect/enums.py +0 -0
  112. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/dialect/postgres.py +0 -0
  113. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/dialect/presto.py +0 -0
  114. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/dialect/snowflake.py +0 -0
  115. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/dialect/sql_server.py +0 -0
  116. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/engine.py +0 -0
  117. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/executor.py +0 -0
  118. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/hooks/__init__.py +0 -0
  119. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/hooks/base_hook.py +0 -0
  120. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/hooks/graph_hook.py +0 -0
  121. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/hooks/query_debugger.py +0 -0
  122. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/metadata/__init__.py +0 -0
  123. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/parser.py +0 -0
  124. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/parsing/__init__.py +0 -0
  125. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/parsing/config.py +0 -0
  126. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/parsing/exceptions.py +0 -0
  127. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/parsing/helpers.py +0 -0
  128. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/parsing/parse_engine.py +0 -0
  129. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/parsing/render.py +0 -0
  130. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/parsing/trilogy.lark +0 -0
  131. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/py.typed +0 -0
  132. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/render.py +0 -0
  133. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/scripts/__init__.py +0 -0
  134. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/scripts/trilogy.py +0 -0
  135. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/std/__init__.py +0 -0
  136. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/std/date.preql +0 -0
  137. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/std/display.preql +0 -0
  138. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/std/geography.preql +0 -0
  139. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/std/money.preql +0 -0
  140. {pytrilogy-0.0.3.34 → pytrilogy-0.0.3.35}/trilogy/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytrilogy
3
- Version: 0.0.3.34
3
+ Version: 0.0.3.35
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytrilogy
3
- Version: 0.0.3.34
3
+ Version: 0.0.3.35
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -130,8 +130,8 @@ trilogy/parsing/trilogy.lark
130
130
  trilogy/scripts/__init__.py
131
131
  trilogy/scripts/trilogy.py
132
132
  trilogy/std/__init__.py
133
- trilogy/std/dashboard.preql
134
133
  trilogy/std/date.preql
135
134
  trilogy/std/display.preql
136
135
  trilogy/std/geography.preql
137
- trilogy/std/money.preql
136
+ trilogy/std/money.preql
137
+ trilogy/std/report.preql
@@ -4,6 +4,6 @@ from trilogy.dialect.enums import Dialects
4
4
  from trilogy.executor import Executor
5
5
  from trilogy.parser import parse
6
6
 
7
- __version__ = "0.0.3.34"
7
+ __version__ = "0.0.3.35"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -111,11 +111,14 @@ def validate_case_output(
111
111
  if output_datatype != DataType.NULL:
112
112
  datatypes.add(output_datatype.data_type)
113
113
  mapz[str(arg.expr)] = output_datatype
114
- if not len(datatypes) == 1:
114
+ known = [x for x in datatypes if x != DataType.UNKNOWN]
115
+ if len(known) == 0:
116
+ return DataType.UNKNOWN
117
+ if not len(known) == 1:
115
118
  raise SyntaxError(
116
119
  f"All case expressions must have the same output datatype, got {datatypes} from {mapz}"
117
120
  )
118
- return datatypes.pop()
121
+ return known.pop()
119
122
 
120
123
 
121
124
  def create_struct_output(
@@ -672,12 +675,22 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
672
675
  arg_count=1,
673
676
  ),
674
677
  FunctionType.SUM: FunctionConfig(
675
- valid_inputs={DataType.INTEGER, DataType.FLOAT, DataType.NUMBER},
678
+ valid_inputs={
679
+ DataType.INTEGER,
680
+ DataType.FLOAT,
681
+ DataType.NUMBER,
682
+ DataType.NUMERIC,
683
+ },
676
684
  output_purpose=Purpose.METRIC,
677
685
  arg_count=1,
678
686
  ),
679
687
  FunctionType.AVG: FunctionConfig(
680
- valid_inputs={DataType.INTEGER, DataType.FLOAT, DataType.NUMBER},
688
+ valid_inputs={
689
+ DataType.INTEGER,
690
+ DataType.FLOAT,
691
+ DataType.NUMBER,
692
+ DataType.NUMERIC,
693
+ },
681
694
  output_purpose=Purpose.METRIC,
682
695
  arg_count=1,
683
696
  ),
@@ -107,6 +107,10 @@ class ConceptRef(Addressable, Namespaced, DataTyped, Mergeable, BaseModel):
107
107
  ) = DataType.UNKNOWN
108
108
  metadata: Optional["Metadata"] = None
109
109
 
110
+ @property
111
+ def reference(self):
112
+ return self
113
+
110
114
  @property
111
115
  def line_no(self) -> int | None:
112
116
  if self.metadata:
@@ -1426,7 +1430,7 @@ def get_basic_type(
1426
1430
  return type
1427
1431
 
1428
1432
 
1429
- class CaseWhen(Namespaced, ConceptArgs, Mergeable, BaseModel):
1433
+ class CaseWhen(Namespaced, DataTyped, ConceptArgs, Mergeable, BaseModel):
1430
1434
  comparison: Conditional | SubselectComparison | Comparison
1431
1435
  expr: "Expr"
1432
1436
 
@@ -1436,6 +1440,10 @@ class CaseWhen(Namespaced, ConceptArgs, Mergeable, BaseModel):
1436
1440
  return v.reference
1437
1441
  return v
1438
1442
 
1443
+ @property
1444
+ def output_datatype(self):
1445
+ return arg_to_datatype(self.expr)
1446
+
1439
1447
  def __str__(self):
1440
1448
  return self.__repr__()
1441
1449
 
@@ -1488,7 +1496,7 @@ class CaseWhen(Namespaced, ConceptArgs, Mergeable, BaseModel):
1488
1496
  )
1489
1497
 
1490
1498
 
1491
- class CaseElse(Namespaced, ConceptArgs, Mergeable, BaseModel):
1499
+ class CaseElse(Namespaced, ConceptArgs, DataTyped, Mergeable, BaseModel):
1492
1500
  expr: "Expr"
1493
1501
  # this ensures that it's easily differentiable from CaseWhen
1494
1502
  discriminant: ComparisonOperator = ComparisonOperator.ELSE
@@ -1499,6 +1507,10 @@ class CaseElse(Namespaced, ConceptArgs, Mergeable, BaseModel):
1499
1507
  def __repr__(self):
1500
1508
  return f"ELSE {str(self.expr)}"
1501
1509
 
1510
+ @property
1511
+ def output_datatype(self):
1512
+ return arg_to_datatype(self.expr)
1513
+
1502
1514
  @field_validator("expr", mode="before")
1503
1515
  def enforce_expr(cls, v):
1504
1516
  if isinstance(v, Concept):
@@ -1837,7 +1849,7 @@ class FunctionCallWrapper(
1837
1849
 
1838
1850
  class AggregateWrapper(Mergeable, DataTyped, ConceptArgs, Namespaced, BaseModel):
1839
1851
  function: Function
1840
- by: List[ConceptRef] = Field(default_factory=list)
1852
+ by: List[ConceptRef | Concept] = Field(default_factory=list)
1841
1853
 
1842
1854
  def __init__(self, **kwargs):
1843
1855
  super().__init__(**kwargs)
@@ -1863,7 +1875,7 @@ class AggregateWrapper(Mergeable, DataTyped, ConceptArgs, Namespaced, BaseModel)
1863
1875
 
1864
1876
  @property
1865
1877
  def concept_arguments(self) -> List[ConceptRef]:
1866
- return self.function.concept_arguments + self.by
1878
+ return self.function.concept_arguments + [x.reference for x in self.by]
1867
1879
 
1868
1880
  @property
1869
1881
  def output_datatype(self):
@@ -2392,4 +2404,8 @@ FuncArgs = (
2392
2404
  | NumericType
2393
2405
  | list
2394
2406
  | ListWrapper[Any]
2407
+ | TupleWrapper[Any]
2408
+ | Comparison
2409
+ | Conditional
2410
+ | MagicConstants
2395
2411
  )
@@ -1538,9 +1538,23 @@ class Factory:
1538
1538
 
1539
1539
  @build.register
1540
1540
  def _(self, base: CaseWhen) -> BuildCaseWhen:
1541
+ from trilogy.parsing.common import arbitrary_to_concept
1542
+
1543
+ comparison = base.comparison
1544
+ if isinstance(comparison, (AggregateWrapper, FilterItem, WindowItem)):
1545
+ comparison = arbitrary_to_concept(
1546
+ comparison,
1547
+ environment=self.environment,
1548
+ )
1549
+ expr: Concept | FuncArgs = base.expr
1550
+ if isinstance(expr, (AggregateWrapper, FilterItem, WindowItem)):
1551
+ expr = arbitrary_to_concept(
1552
+ expr,
1553
+ environment=self.environment,
1554
+ )
1541
1555
  return BuildCaseWhen.model_construct(
1542
- comparison=self.build(base.comparison),
1543
- expr=(self.build(base.expr)),
1556
+ comparison=self.build(comparison),
1557
+ expr=self.build(expr),
1544
1558
  )
1545
1559
 
1546
1560
  @build.register
@@ -1647,10 +1661,27 @@ class Factory:
1647
1661
  @build.register
1648
1662
  def _(self, base: WindowItem) -> BuildWindowItem:
1649
1663
  # to do proper discovery, we need to inject virtual intermediate ocncepts
1664
+ from trilogy.parsing.common import arbitrary_to_concept
1665
+
1666
+ content: Concept | FuncArgs = base.content
1667
+ if isinstance(content, (AggregateWrapper, FilterItem, WindowItem)):
1668
+ content = arbitrary_to_concept(
1669
+ content,
1670
+ environment=self.environment,
1671
+ )
1672
+ final_by = []
1673
+ for x in base.order_by:
1674
+ if (
1675
+ isinstance(x.expr, AggregateWrapper)
1676
+ and not x.expr.by
1677
+ and isinstance(content, (ConceptRef, Concept))
1678
+ ):
1679
+ x.expr.by = [content]
1680
+ final_by.append(x)
1650
1681
  return BuildWindowItem.model_construct(
1651
1682
  type=base.type,
1652
- content=self.build(base.content),
1653
- order_by=[self.build(x) for x in base.order_by],
1683
+ content=self.build(content),
1684
+ order_by=[self.build(x) for x in final_by],
1654
1685
  over=[self.build(x) for x in base.over],
1655
1686
  index=base.index,
1656
1687
  )
@@ -135,7 +135,6 @@ class SelectStatement(HasUUID, SelectTypeMixin, BaseModel):
135
135
  )
136
136
 
137
137
  output.grain = output.calculate_grain(environment)
138
-
139
138
  for x in selection:
140
139
  if x.is_undefined and environment.concepts.fail_on_missing:
141
140
  environment.concepts.raise_undefined(
@@ -270,7 +270,7 @@ def _get_relevant_parent_concepts(arg) -> tuple[list[ConceptRef], bool]:
270
270
  elif isinstance(arg, AggregateWrapper) and not arg.by:
271
271
  return [], True
272
272
  elif isinstance(arg, AggregateWrapper) and arg.by:
273
- return arg.by, True
273
+ return [x.reference for x in arg.by], True
274
274
  elif isinstance(arg, FunctionCallWrapper):
275
275
  return get_relevant_parent_concepts(arg.content)
276
276
  return get_concept_arguments(arg), False
@@ -0,0 +1,5 @@
1
+
2
+
3
+
4
+ def top_x_by_metric(val, metric, x=10, default='other') -> case when rank val by metric desc <x+1 then val else default end; # Get the top X of a value by a certain metric, bucket the rest in other
5
+
@@ -1,5 +0,0 @@
1
-
2
-
3
-
4
- # def top_x_by_metric(val, metric, x=10) -> case when rank val by metric order by metric desc <x then val else 'Other' end;
5
-
File without changes
File without changes
File without changes
File without changes