pytrilogy 0.0.3.27__tar.gz → 0.0.3.28__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 (139) hide show
  1. {pytrilogy-0.0.3.27/pytrilogy.egg-info → pytrilogy-0.0.3.28}/PKG-INFO +1 -1
  2. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28/pytrilogy.egg-info}/PKG-INFO +1 -1
  3. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/__init__.py +1 -1
  4. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/models/build.py +11 -1
  5. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/node_generators/group_node.py +2 -2
  6. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/node_generators/rowset_node.py +3 -1
  7. pytrilogy-0.0.3.28/trilogy/core/processing/node_generators/window_node.py +173 -0
  8. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/nodes/window_node.py +12 -1
  9. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/parsing/common.py +8 -1
  10. pytrilogy-0.0.3.27/trilogy/core/processing/node_generators/window_node.py +0 -105
  11. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/LICENSE.md +0 -0
  12. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/README.md +0 -0
  13. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/pyproject.toml +0 -0
  14. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/pytrilogy.egg-info/SOURCES.txt +0 -0
  15. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/pytrilogy.egg-info/dependency_links.txt +0 -0
  16. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/pytrilogy.egg-info/entry_points.txt +0 -0
  17. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/pytrilogy.egg-info/requires.txt +0 -0
  18. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/pytrilogy.egg-info/top_level.txt +0 -0
  19. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/setup.cfg +0 -0
  20. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/setup.py +0 -0
  21. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_datatypes.py +0 -0
  22. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_declarations.py +0 -0
  23. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_derived_concepts.py +0 -0
  24. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_discovery_nodes.py +0 -0
  25. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_enums.py +0 -0
  26. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_environment.py +0 -0
  27. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_executor.py +0 -0
  28. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_functions.py +0 -0
  29. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_imports.py +0 -0
  30. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_metadata.py +0 -0
  31. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_models.py +0 -0
  32. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_multi_join_assignments.py +0 -0
  33. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_parse_engine.py +0 -0
  34. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_parsing.py +0 -0
  35. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_partial_handling.py +0 -0
  36. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_query_processing.py +0 -0
  37. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_query_render.py +0 -0
  38. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_select.py +0 -0
  39. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_show.py +0 -0
  40. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_statements.py +0 -0
  41. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_typing.py +0 -0
  42. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_undefined_concept.py +0 -0
  43. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_user_functions.py +0 -0
  44. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/tests/test_where_clause.py +0 -0
  45. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/authoring/__init__.py +0 -0
  46. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/compiler.py +0 -0
  47. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/constants.py +0 -0
  48. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/__init__.py +0 -0
  49. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/constants.py +0 -0
  50. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/enums.py +0 -0
  51. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/env_processor.py +0 -0
  52. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/environment_helpers.py +0 -0
  53. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/ergonomics.py +0 -0
  54. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/exceptions.py +0 -0
  55. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/functions.py +0 -0
  56. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/graph_models.py +0 -0
  57. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/internal.py +0 -0
  58. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/models/__init__.py +0 -0
  59. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/models/author.py +0 -0
  60. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/models/build_environment.py +0 -0
  61. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/models/core.py +0 -0
  62. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/models/datasource.py +0 -0
  63. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/models/environment.py +0 -0
  64. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/models/execute.py +0 -0
  65. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/optimization.py +0 -0
  66. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/optimizations/__init__.py +0 -0
  67. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/optimizations/base_optimization.py +0 -0
  68. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/optimizations/inline_constant.py +0 -0
  69. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/optimizations/inline_datasource.py +0 -0
  70. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
  71. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/__init__.py +0 -0
  72. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/concept_strategies_v3.py +0 -0
  73. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/graph_utils.py +0 -0
  74. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/node_generators/__init__.py +0 -0
  75. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/node_generators/basic_node.py +0 -0
  76. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/node_generators/common.py +0 -0
  77. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/node_generators/filter_node.py +0 -0
  78. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
  79. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
  80. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
  81. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
  82. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
  83. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
  84. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/node_generators/select_node.py +0 -0
  85. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
  86. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/node_generators/union_node.py +0 -0
  87. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
  88. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/nodes/__init__.py +0 -0
  89. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/nodes/base_node.py +0 -0
  90. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/nodes/filter_node.py +0 -0
  91. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/nodes/group_node.py +0 -0
  92. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/nodes/merge_node.py +0 -0
  93. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
  94. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/nodes/union_node.py +0 -0
  95. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/nodes/unnest_node.py +0 -0
  96. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/processing/utility.py +0 -0
  97. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/query_processor.py +0 -0
  98. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/statements/__init__.py +0 -0
  99. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/statements/author.py +0 -0
  100. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/statements/build.py +0 -0
  101. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/statements/common.py +0 -0
  102. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/core/statements/execute.py +0 -0
  103. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/dialect/__init__.py +0 -0
  104. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/dialect/base.py +0 -0
  105. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/dialect/bigquery.py +0 -0
  106. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/dialect/common.py +0 -0
  107. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/dialect/config.py +0 -0
  108. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/dialect/dataframe.py +0 -0
  109. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/dialect/duckdb.py +0 -0
  110. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/dialect/enums.py +0 -0
  111. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/dialect/postgres.py +0 -0
  112. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/dialect/presto.py +0 -0
  113. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/dialect/snowflake.py +0 -0
  114. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/dialect/sql_server.py +0 -0
  115. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/engine.py +0 -0
  116. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/executor.py +0 -0
  117. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/hooks/__init__.py +0 -0
  118. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/hooks/base_hook.py +0 -0
  119. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/hooks/graph_hook.py +0 -0
  120. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/hooks/query_debugger.py +0 -0
  121. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/metadata/__init__.py +0 -0
  122. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/parser.py +0 -0
  123. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/parsing/__init__.py +0 -0
  124. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/parsing/config.py +0 -0
  125. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/parsing/exceptions.py +0 -0
  126. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/parsing/helpers.py +0 -0
  127. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/parsing/parse_engine.py +0 -0
  128. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/parsing/render.py +0 -0
  129. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/parsing/trilogy.lark +0 -0
  130. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/py.typed +0 -0
  131. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/render.py +0 -0
  132. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/scripts/__init__.py +0 -0
  133. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/scripts/trilogy.py +0 -0
  134. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/std/__init__.py +0 -0
  135. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/std/dashboard.preql +0 -0
  136. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/std/date.preql +0 -0
  137. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/std/geography.preql +0 -0
  138. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/std/money.preql +0 -0
  139. {pytrilogy-0.0.3.27 → pytrilogy-0.0.3.28}/trilogy/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytrilogy
3
- Version: 0.0.3.27
3
+ Version: 0.0.3.28
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.27
3
+ Version: 0.0.3.28
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -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.27"
7
+ __version__ = "0.0.3.28"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -1610,8 +1610,18 @@ class Factory:
1610
1610
 
1611
1611
  @build.register
1612
1612
  def _(self, base: OrderItem) -> BuildOrderItem:
1613
+ from trilogy.parsing.common import arbitrary_to_concept
1614
+
1615
+ bexpr: Any
1616
+ if isinstance(base.expr, AggregateWrapper):
1617
+ bexpr = arbitrary_to_concept(
1618
+ base.expr,
1619
+ environment=self.environment,
1620
+ )
1621
+ else:
1622
+ bexpr = base.expr
1613
1623
  return BuildOrderItem.model_construct(
1614
- expr=(self.build(base.expr)),
1624
+ expr=(self.build(bexpr)),
1615
1625
  order=base.order,
1616
1626
  )
1617
1627
 
@@ -86,7 +86,7 @@ def gen_group_node(
86
86
  )
87
87
  else:
88
88
  logger.info(
89
- f"{padding(depth)}{LOGGER_PREFIX} cannot include optional agg; mismatched grain {BuildGrain.from_concepts(agg_parents)} vs {BuildGrain.from_concepts(parent_concepts)}"
89
+ f"{padding(depth)}{LOGGER_PREFIX} cannot include optional agg {possible_agg.address}; mismatched grain {BuildGrain.from_concepts(agg_parents)} vs {BuildGrain.from_concepts(parent_concepts)}"
90
90
  )
91
91
  if parent_concepts:
92
92
  logger.info(
@@ -139,7 +139,7 @@ def gen_group_node(
139
139
  )
140
140
  return group_node
141
141
  logger.info(
142
- f"{padding(depth)}{LOGGER_PREFIX} group node requires enrichment, missing {missing_optional}"
142
+ f"{padding(depth)}{LOGGER_PREFIX} group node for {concept.address} requires enrichment, missing {missing_optional}"
143
143
  )
144
144
  return gen_enrichment_node(
145
145
  group_node,
@@ -91,7 +91,9 @@ def gen_rowset_node(
91
91
  and x.derivation != Derivation.ROWSET
92
92
  and not any(z in lineage.rowset.derived_concepts for z in x.pseudonyms)
93
93
  ]
94
- logger.info(f"{padding(depth)}{LOGGER_PREFIX} hiding {final_hidden}")
94
+ logger.info(
95
+ f"{padding(depth)}{LOGGER_PREFIX} hiding {final_hidden} local optional {local_optional}"
96
+ )
95
97
  node.hide_output_concepts(final_hidden)
96
98
  assert node.resolution_cache
97
99
  # assume grain to be output of select
@@ -0,0 +1,173 @@
1
+ from typing import List
2
+
3
+ from trilogy.constants import logger
4
+ from trilogy.core.models.build import (
5
+ BuildConcept,
6
+ BuildGrain,
7
+ BuildWhereClause,
8
+ BuildWindowItem,
9
+ )
10
+ from trilogy.core.models.build_environment import BuildEnvironment
11
+ from trilogy.core.processing.node_generators.common import (
12
+ gen_enrichment_node,
13
+ )
14
+ from trilogy.core.processing.nodes import History, StrategyNode, WindowNode
15
+ from trilogy.core.processing.utility import create_log_lambda, padding
16
+ from trilogy.utility import unique
17
+
18
+ LOGGER_PREFIX = "[GEN_WINDOW_NODE]"
19
+
20
+
21
+ WINDOW_TYPES = (BuildWindowItem,)
22
+
23
+
24
+ def resolve_window_parent_concepts(
25
+ concept: BuildConcept, environment: BuildEnvironment
26
+ ) -> tuple[BuildConcept, List[BuildConcept]]:
27
+ if not isinstance(concept.lineage, WINDOW_TYPES):
28
+ raise ValueError
29
+ base = []
30
+ if concept.lineage.over:
31
+ base += concept.lineage.over
32
+ if concept.lineage.order_by:
33
+ for item in concept.lineage.order_by:
34
+ base += item.concept_arguments
35
+
36
+ return concept.lineage.content, unique(base, "address")
37
+
38
+
39
+ def gen_window_node(
40
+ concept: BuildConcept,
41
+ local_optional: list[BuildConcept],
42
+ environment: BuildEnvironment,
43
+ g,
44
+ depth: int,
45
+ source_concepts,
46
+ history: History,
47
+ conditions: BuildWhereClause | None = None,
48
+ ) -> StrategyNode | None:
49
+ base, parent_concepts = resolve_window_parent_concepts(concept, environment)
50
+ logger.info(
51
+ f"{padding(depth)}{LOGGER_PREFIX} generating window node for {concept} with parents {parent_concepts} and optional {local_optional}"
52
+ )
53
+ equivalent_optional = [
54
+ x
55
+ for x in local_optional
56
+ if isinstance(x.lineage, WINDOW_TYPES)
57
+ and resolve_window_parent_concepts(x, environment)[1] == parent_concepts
58
+ ]
59
+
60
+ targets = [base]
61
+ # append in keys to get the right grain
62
+ if concept.keys:
63
+ for item in concept.keys:
64
+ targets.append(environment.concepts[item])
65
+ additional_outputs = []
66
+ if equivalent_optional:
67
+ for x in equivalent_optional:
68
+ assert isinstance(x.lineage, WINDOW_TYPES)
69
+ logger.info(
70
+ f"{padding(depth)}{LOGGER_PREFIX} found equivalent optional {x} with parents {resolve_window_parent_concepts(x, environment)[1]}"
71
+ )
72
+ additional_outputs.append(x)
73
+
74
+ grain_equivalents = [
75
+ x for x in local_optional if x.keys and all([key in targets for key in x.keys])
76
+ ]
77
+
78
+ for x in grain_equivalents:
79
+ targets.append(x)
80
+
81
+ # finally, the ones we'll need to enrich
82
+ non_equivalent_optional = [x for x in local_optional if x.address not in targets]
83
+
84
+ logger.info(
85
+ f"{padding(depth)}{LOGGER_PREFIX} resolving final parents {parent_concepts + targets}"
86
+ )
87
+ parent_node: StrategyNode = source_concepts(
88
+ mandatory_list=parent_concepts + targets,
89
+ environment=environment,
90
+ g=g,
91
+ depth=depth + 1,
92
+ history=history,
93
+ conditions=conditions,
94
+ )
95
+ if not parent_node:
96
+ logger.info(f"{padding(depth)}{LOGGER_PREFIX} window node parents unresolvable")
97
+ return None
98
+ parent_node.resolve()
99
+ if not all(
100
+ [
101
+ x.address in [y.address for y in parent_node.output_concepts]
102
+ for x in parent_concepts
103
+ ]
104
+ ):
105
+ missing = [
106
+ x
107
+ for x in parent_concepts
108
+ if x.address not in [y.address for y in parent_node.output_concepts]
109
+ ]
110
+ logger.info(
111
+ f"{padding(depth)}{LOGGER_PREFIX} window node parents unresolvable, missing {missing}"
112
+ )
113
+ raise SyntaxError
114
+ _window_node = WindowNode(
115
+ input_concepts=parent_concepts + targets,
116
+ output_concepts=[concept] + additional_outputs + parent_concepts + targets,
117
+ environment=environment,
118
+ parents=[
119
+ parent_node,
120
+ ],
121
+ depth=depth,
122
+ preexisting_conditions=conditions.conditional if conditions else None,
123
+ )
124
+ _window_node.rebuild_cache()
125
+ _window_node.resolve()
126
+ window_node = StrategyNode(
127
+ input_concepts=[concept] + additional_outputs + parent_concepts + targets,
128
+ output_concepts=[concept] + additional_outputs + parent_concepts + targets,
129
+ environment=environment,
130
+ parents=[_window_node],
131
+ preexisting_conditions=conditions.conditional if conditions else None,
132
+ # hidden_concepts=[
133
+ # x.address for x in parent_concepts if x.address not in local_optional
134
+ # ],
135
+ )
136
+ if not non_equivalent_optional:
137
+ logger.info(
138
+ f"{padding(depth)}{LOGGER_PREFIX} no optional concepts, returning window node"
139
+ )
140
+ return window_node
141
+
142
+ missing_optional = [
143
+ x.address
144
+ for x in local_optional
145
+ if x.address not in window_node.output_concepts
146
+ ]
147
+
148
+ if not missing_optional:
149
+ logger.info(
150
+ f"{padding(depth)}{LOGGER_PREFIX} no extra enrichment needed for window node, has all of {[x.address for x in local_optional]}"
151
+ )
152
+ return window_node
153
+ logger.info(
154
+ f"{padding(depth)}{LOGGER_PREFIX} window node for {concept.address} requires enrichment, missing {missing_optional}, has {[x.address for x in window_node.output_concepts]}"
155
+ )
156
+
157
+ return gen_enrichment_node(
158
+ window_node,
159
+ join_keys=[
160
+ environment.concepts[c]
161
+ for c in BuildGrain.from_concepts(
162
+ concepts=targets, environment=environment
163
+ ).components
164
+ ],
165
+ local_optional=local_optional,
166
+ environment=environment,
167
+ g=g,
168
+ depth=depth,
169
+ source_concepts=source_concepts,
170
+ log_lambda=create_log_lambda(LOGGER_PREFIX, depth, logger),
171
+ history=history,
172
+ conditions=conditions,
173
+ )
@@ -1,7 +1,13 @@
1
1
  from typing import List
2
2
 
3
3
  from trilogy.core.enums import SourceType
4
- from trilogy.core.models.build import BuildConcept, BuildOrderBy
4
+ from trilogy.core.models.build import (
5
+ BuildComparison,
6
+ BuildConcept,
7
+ BuildConditional,
8
+ BuildOrderBy,
9
+ BuildParenthetical,
10
+ )
5
11
  from trilogy.core.models.execute import QueryDatasource
6
12
  from trilogy.core.processing.nodes.base_node import StrategyNode
7
13
 
@@ -18,6 +24,9 @@ class WindowNode(StrategyNode):
18
24
  parents: List["StrategyNode"] | None = None,
19
25
  depth: int = 0,
20
26
  ordering: BuildOrderBy | None = None,
27
+ preexisting_conditions: (
28
+ BuildConditional | BuildComparison | BuildParenthetical | None
29
+ ) = None,
21
30
  ):
22
31
  super().__init__(
23
32
  input_concepts=input_concepts,
@@ -27,6 +36,7 @@ class WindowNode(StrategyNode):
27
36
  parents=parents,
28
37
  depth=depth,
29
38
  ordering=ordering,
39
+ preexisting_conditions=preexisting_conditions,
30
40
  )
31
41
 
32
42
  def _resolve(self) -> QueryDatasource:
@@ -42,4 +52,5 @@ class WindowNode(StrategyNode):
42
52
  parents=self.parents,
43
53
  depth=self.depth,
44
54
  ordering=self.ordering,
55
+ preexisting_conditions=self.preexisting_conditions,
45
56
  )
@@ -417,7 +417,14 @@ def window_item_to_concept(
417
417
  bcontent = environment.concepts[parent.content.address]
418
418
  if isinstance(bcontent, UndefinedConcept):
419
419
  return UndefinedConcept(address=f"{namespace}.{name}", metadata=fmetadata)
420
- local_purpose, keys = get_purpose_and_keys(None, (bcontent,), environment)
420
+
421
+ if bcontent.purpose == Purpose.METRIC:
422
+ local_purpose, keys = get_purpose_and_keys(None, (bcontent,), environment)
423
+ else:
424
+ local_purpose = Purpose.PROPERTY
425
+ keys = {
426
+ bcontent.address,
427
+ }
421
428
 
422
429
  if parent.order_by:
423
430
  grain_components = parent.over + [bcontent.output]
@@ -1,105 +0,0 @@
1
- from typing import List
2
-
3
- from trilogy.constants import logger
4
- from trilogy.core.models.build import BuildConcept, BuildWhereClause, BuildWindowItem
5
- from trilogy.core.models.build_environment import BuildEnvironment
6
- from trilogy.core.processing.nodes import History, StrategyNode, WindowNode
7
- from trilogy.core.processing.utility import padding
8
- from trilogy.utility import unique
9
-
10
- LOGGER_PREFIX = "[GEN_WINDOW_NODE]"
11
-
12
-
13
- WINDOW_TYPES = (BuildWindowItem,)
14
-
15
-
16
- def resolve_window_parent_concepts(
17
- concept: BuildConcept, environment: BuildEnvironment
18
- ) -> tuple[BuildConcept, List[BuildConcept]]:
19
- if not isinstance(concept.lineage, WINDOW_TYPES):
20
- raise ValueError
21
- base = []
22
- if concept.lineage.over:
23
- base += concept.lineage.over
24
- if concept.lineage.order_by:
25
- for item in concept.lineage.order_by:
26
- base += item.concept_arguments
27
- return concept.lineage.content, unique(base, "address")
28
-
29
-
30
- def gen_window_node(
31
- concept: BuildConcept,
32
- local_optional: list[BuildConcept],
33
- environment: BuildEnvironment,
34
- g,
35
- depth: int,
36
- source_concepts,
37
- history: History | None = None,
38
- conditions: BuildWhereClause | None = None,
39
- ) -> StrategyNode | None:
40
- base, parent_concepts = resolve_window_parent_concepts(concept, environment)
41
- logger.info(
42
- f"{padding(depth)}{LOGGER_PREFIX} generating window node for {concept} with parents {parent_concepts}"
43
- )
44
- equivalent_optional = [
45
- x
46
- for x in local_optional
47
- if isinstance(x.lineage, WINDOW_TYPES)
48
- and resolve_window_parent_concepts(x, environment)[1] == parent_concepts
49
- ]
50
-
51
- non_equivalent_optional = [
52
- x for x in local_optional if x.address not in equivalent_optional
53
- ]
54
- targets = [base]
55
- if equivalent_optional:
56
- for x in equivalent_optional:
57
- assert isinstance(x.lineage, WINDOW_TYPES)
58
- targets.append(x.lineage.content)
59
-
60
- parent_node: StrategyNode = source_concepts(
61
- mandatory_list=parent_concepts + targets + non_equivalent_optional,
62
- environment=environment,
63
- g=g,
64
- depth=depth + 1,
65
- history=history,
66
- conditions=conditions,
67
- )
68
- if not parent_node:
69
- logger.info(f"{padding(depth)}{LOGGER_PREFIX} window node parents unresolvable")
70
- return None
71
- parent_node.resolve()
72
- if not all(
73
- [
74
- x.address in [y.address for y in parent_node.output_concepts]
75
- for x in parent_concepts
76
- ]
77
- ):
78
- missing = [
79
- x
80
- for x in parent_concepts
81
- if x.address not in [y.address for y in parent_node.output_concepts]
82
- ]
83
- logger.info(
84
- f"{padding(depth)}{LOGGER_PREFIX} window node parents unresolvable, missing {missing}"
85
- )
86
- raise SyntaxError
87
- _window_node = WindowNode(
88
- input_concepts=parent_concepts + targets + non_equivalent_optional,
89
- output_concepts=[concept] + parent_concepts + local_optional,
90
- environment=environment,
91
- parents=[
92
- parent_node,
93
- ],
94
- depth=depth,
95
- )
96
- _window_node.rebuild_cache()
97
- _window_node.resolve()
98
- window_node = StrategyNode(
99
- input_concepts=[concept] + local_optional,
100
- output_concepts=[concept] + local_optional,
101
- environment=environment,
102
- parents=[_window_node],
103
- preexisting_conditions=conditions.conditional if conditions else None,
104
- )
105
- return window_node
File without changes
File without changes
File without changes
File without changes