pytrilogy 0.0.3.27__py3-none-any.whl → 0.0.3.28__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.

Potentially problematic release.


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

@@ -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,5 +1,5 @@
1
- pytrilogy-0.0.3.27.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
2
- trilogy/__init__.py,sha256=Otvz_T4-Jv07I6Y8l7F5VvvngjcMDwH0r7k9OTkAwL0,303
1
+ pytrilogy-0.0.3.28.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
2
+ trilogy/__init__.py,sha256=BWRnSblH31X0EL041UhPaq_TRiA9hEJGt6VMVN-XBsg,303
3
3
  trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
4
4
  trilogy/constants.py,sha256=5eQxk1A0pv-TQk3CCvgZCFA9_K-6nxrOm7E5Lxd7KIY,1652
5
5
  trilogy/engine.py,sha256=OK2RuqCIUId6yZ5hfF8J1nxGP0AJqHRZiafcowmW0xc,1728
@@ -23,7 +23,7 @@ trilogy/core/optimization.py,sha256=xGO8piVsLrpqrx-Aid_Y56_5slSv4eZmlP64hCHRiEc,
23
23
  trilogy/core/query_processor.py,sha256=Do8YpdPBdsbKtl9n37hobzk8SORMGqH-e_zNNxd-BE4,19456
24
24
  trilogy/core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
25
25
  trilogy/core/models/author.py,sha256=-90COW7QY6w-g8GkjivPZqG2yNitn74e13m5KYgB1IU,73985
26
- trilogy/core/models/build.py,sha256=2K-dANoFFl36yFdEDf7FDL36zM8FKSzpjLT5K2Na8Kc,57549
26
+ trilogy/core/models/build.py,sha256=tky6ryVPcAECt53Sk9TGzmGCumxdbfY9Uxn0oVi50VA,57854
27
27
  trilogy/core/models/build_environment.py,sha256=8UggvlPU708GZWYPJMc_ou2r7M3TY2g69eqGvz03YX0,5528
28
28
  trilogy/core/models/core.py,sha256=wx6hJcFECMG-Ij972ADNkr-3nFXkYESr82ObPiC46_U,10875
29
29
  trilogy/core/models/datasource.py,sha256=6RjJUd2u4nYmEwFBpJlM9LbHVYDv8iHJxqiBMZqUrwI,9422
@@ -42,17 +42,17 @@ trilogy/core/processing/node_generators/__init__.py,sha256=o8rOFHPSo-s_59hREwXMW
42
42
  trilogy/core/processing/node_generators/basic_node.py,sha256=UVsXMn6jTjm_ofVFt218jAS11s4RV4zD781vP4im-GI,3371
43
43
  trilogy/core/processing/node_generators/common.py,sha256=ZsDzThjm_mAtdQpKAg8QIJiPVZ4KuUkKyilj4eOhSDs,9439
44
44
  trilogy/core/processing/node_generators/filter_node.py,sha256=rlY7TbgjJlGhahYgdCIJpJbaSREAGVJEsyUIGaA38O0,8271
45
- trilogy/core/processing/node_generators/group_node.py,sha256=3-TXVnRO9_jqE_e1kWLqbgtBShW8WFtKwQk8oOtOULs,5894
45
+ trilogy/core/processing/node_generators/group_node.py,sha256=iB5kq-EaueoaSQkgegULwo4g-fe8PKtRtSDiphNO5_k,5939
46
46
  trilogy/core/processing/node_generators/group_to_node.py,sha256=E5bEjovSx422d_MlAUCDFdY4P2WJVp61BmWwltkhzA8,3095
47
47
  trilogy/core/processing/node_generators/multiselect_node.py,sha256=GWV5yLmKTe1yyPhN60RG1Rnrn4ktfn9lYYXi_FVU4UI,7061
48
48
  trilogy/core/processing/node_generators/node_merge_node.py,sha256=sv55oynfqgpHEpo1OEtVDri-5fywzPhDlR85qaWikvY,16195
49
- trilogy/core/processing/node_generators/rowset_node.py,sha256=8yeMWiyi9IFnza7qPn9YYC3WpA53weq3AY5WisIui8Y,6705
49
+ trilogy/core/processing/node_generators/rowset_node.py,sha256=tQog2YbG7h2TSVZGdXCFbwjqpq-CKqNewFOOfpZNqh4,6751
50
50
  trilogy/core/processing/node_generators/select_merge_node.py,sha256=pIsHfXWA30RkKSMBDKPtDmCriJtHoNKRMJC0THSDxpI,19951
51
51
  trilogy/core/processing/node_generators/select_node.py,sha256=Y-zO0AFkTrpi2LyebjpyHU7WWANr7nKZSS9rY7DH4Wo,1888
52
52
  trilogy/core/processing/node_generators/synonym_node.py,sha256=9LHK2XHDjbyTLjmDQieskG8fqbiSpRnFOkfrutDnOTE,2258
53
53
  trilogy/core/processing/node_generators/union_node.py,sha256=zuMSmgF170vzlp2BBQEhKbqUMjVl2xQDqUB82Dhv-VU,2536
54
54
  trilogy/core/processing/node_generators/unnest_node.py,sha256=cOEKnMRzXUW3bwmiOlgn3E1-B38osng0dh2pDykwITY,2410
55
- trilogy/core/processing/node_generators/window_node.py,sha256=6KoxhmpVOTN3HGWT0FIS96xqlm2Inouw9VL2D_0kSg0,3481
55
+ trilogy/core/processing/node_generators/window_node.py,sha256=MjLmFKUiS-_p-Ak_9mr3becGde9eu5frxmqI7plIETY,5808
56
56
  trilogy/core/processing/node_generators/select_helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
57
57
  trilogy/core/processing/node_generators/select_helpers/datasource_injection.py,sha256=GMW07bb6hXurhF0hZLYoMAKSIS65tat5hwBjvqqPeSA,6516
58
58
  trilogy/core/processing/nodes/__init__.py,sha256=DqPG3Y8vl5-UTeox6hn1EE6iwPIJpsM-XeZALHSgLZQ,5058
@@ -63,7 +63,7 @@ trilogy/core/processing/nodes/merge_node.py,sha256=bEz1QU2o-fl_O-VotE5dN1GmlZPCl
63
63
  trilogy/core/processing/nodes/select_node_v2.py,sha256=Xyfq8lU7rP7JTAd8VV0ATDNal64n4xIBgWQsOuMe_Ak,8824
64
64
  trilogy/core/processing/nodes/union_node.py,sha256=fDFzLAUh5876X6_NM7nkhoMvHEdGJ_LpvPokpZKOhx4,1425
65
65
  trilogy/core/processing/nodes/unnest_node.py,sha256=oLKMMNMx6PLDPlt2V5neFMFrFWxET8r6XZElAhSNkO0,2181
66
- trilogy/core/processing/nodes/window_node.py,sha256=STvwheVttxSWVHB-yUQUSo-Pyz7Uk8G1txFDAbWMp-s,1380
66
+ trilogy/core/processing/nodes/window_node.py,sha256=JXJ0iVRlSEM2IBr1TANym2RaUf_p5E_l2sNykRzXWDo,1710
67
67
  trilogy/core/statements/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
68
68
  trilogy/core/statements/author.py,sha256=sPrItQEKXzE7IR3SGOTVN4OBvGHldUCqXCzbcmEpb7I,14575
69
69
  trilogy/core/statements/build.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
@@ -87,7 +87,7 @@ trilogy/hooks/graph_hook.py,sha256=c-vC-IXoJ_jDmKQjxQyIxyXPOuUcLIURB573gCsAfzQ,2
87
87
  trilogy/hooks/query_debugger.py,sha256=1npRjww94sPV5RRBBlLqMJRaFkH9vhEY6o828MeoEcw,5583
88
88
  trilogy/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
89
89
  trilogy/parsing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
90
- trilogy/parsing/common.py,sha256=99tDKrpQTk-SpyTXUqKFtm-lfmhjCOQIn25hxbQvRRg,21459
90
+ trilogy/parsing/common.py,sha256=JN8pPjtoEMyURAtp7fttm-8iFkaAvuinUKyQs05FqoM,21615
91
91
  trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
92
92
  trilogy/parsing/exceptions.py,sha256=92E5i2frv5hj9wxObJZsZqj5T6bglvPzvdvco_vW1Zk,38
93
93
  trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
@@ -101,8 +101,8 @@ trilogy/std/dashboard.preql,sha256=eJJTs3xPjYKmp-U5SUxCXAAbp55NlmmC3kECaNg4ya4,1
101
101
  trilogy/std/date.preql,sha256=0MHeGLp2mG4QBKtmozcBZ7qVjOAdWOtrliIKn6hz1Pc,95
102
102
  trilogy/std/geography.preql,sha256=KavDw4wbTJcgooo30vWF-MYg5WvD7dZYIWuBQtSaPxg,441
103
103
  trilogy/std/money.preql,sha256=ZHW-csTX-kYbOLmKSO-TcGGgQ-_DMrUXy0BjfuJSFxM,80
104
- pytrilogy-0.0.3.27.dist-info/METADATA,sha256=evVL1Vwen5IhOSqahza3hHhSxrPnwZcCqL1z2ism22g,9100
105
- pytrilogy-0.0.3.27.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
106
- pytrilogy-0.0.3.27.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
107
- pytrilogy-0.0.3.27.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
108
- pytrilogy-0.0.3.27.dist-info/RECORD,,
104
+ pytrilogy-0.0.3.28.dist-info/METADATA,sha256=yyfDxvVnQCKioM0j6JT07GLQpwwRAtwFwPukuUz6HsI,9100
105
+ pytrilogy-0.0.3.28.dist-info/WHEEL,sha256=CmyFI0kx5cdEMTLiONQRbGQwjIoR1aIYB7eCAQ4KPJ0,91
106
+ pytrilogy-0.0.3.28.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
107
+ pytrilogy-0.0.3.28.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
108
+ pytrilogy-0.0.3.28.dist-info/RECORD,,
trilogy/__init__.py CHANGED
@@ -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
@@ -1,10 +1,18 @@
1
1
  from typing import List
2
2
 
3
3
  from trilogy.constants import logger
4
- from trilogy.core.models.build import BuildConcept, BuildWhereClause, BuildWindowItem
4
+ from trilogy.core.models.build import (
5
+ BuildConcept,
6
+ BuildGrain,
7
+ BuildWhereClause,
8
+ BuildWindowItem,
9
+ )
5
10
  from trilogy.core.models.build_environment import BuildEnvironment
11
+ from trilogy.core.processing.node_generators.common import (
12
+ gen_enrichment_node,
13
+ )
6
14
  from trilogy.core.processing.nodes import History, StrategyNode, WindowNode
7
- from trilogy.core.processing.utility import padding
15
+ from trilogy.core.processing.utility import create_log_lambda, padding
8
16
  from trilogy.utility import unique
9
17
 
10
18
  LOGGER_PREFIX = "[GEN_WINDOW_NODE]"
@@ -24,6 +32,7 @@ def resolve_window_parent_concepts(
24
32
  if concept.lineage.order_by:
25
33
  for item in concept.lineage.order_by:
26
34
  base += item.concept_arguments
35
+
27
36
  return concept.lineage.content, unique(base, "address")
28
37
 
29
38
 
@@ -34,12 +43,12 @@ def gen_window_node(
34
43
  g,
35
44
  depth: int,
36
45
  source_concepts,
37
- history: History | None = None,
46
+ history: History,
38
47
  conditions: BuildWhereClause | None = None,
39
48
  ) -> StrategyNode | None:
40
49
  base, parent_concepts = resolve_window_parent_concepts(concept, environment)
41
50
  logger.info(
42
- f"{padding(depth)}{LOGGER_PREFIX} generating window node for {concept} with parents {parent_concepts}"
51
+ f"{padding(depth)}{LOGGER_PREFIX} generating window node for {concept} with parents {parent_concepts} and optional {local_optional}"
43
52
  )
44
53
  equivalent_optional = [
45
54
  x
@@ -48,17 +57,35 @@ def gen_window_node(
48
57
  and resolve_window_parent_concepts(x, environment)[1] == parent_concepts
49
58
  ]
50
59
 
51
- non_equivalent_optional = [
52
- x for x in local_optional if x.address not in equivalent_optional
53
- ]
54
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 = []
55
66
  if equivalent_optional:
56
67
  for x in equivalent_optional:
57
68
  assert isinstance(x.lineage, WINDOW_TYPES)
58
- targets.append(x.lineage.content)
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
+ ]
59
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
+ )
60
87
  parent_node: StrategyNode = source_concepts(
61
- mandatory_list=parent_concepts + targets + non_equivalent_optional,
88
+ mandatory_list=parent_concepts + targets,
62
89
  environment=environment,
63
90
  g=g,
64
91
  depth=depth + 1,
@@ -85,21 +112,62 @@ def gen_window_node(
85
112
  )
86
113
  raise SyntaxError
87
114
  _window_node = WindowNode(
88
- input_concepts=parent_concepts + targets + non_equivalent_optional,
89
- output_concepts=[concept] + parent_concepts + local_optional,
115
+ input_concepts=parent_concepts + targets,
116
+ output_concepts=[concept] + additional_outputs + parent_concepts + targets,
90
117
  environment=environment,
91
118
  parents=[
92
119
  parent_node,
93
120
  ],
94
121
  depth=depth,
122
+ preexisting_conditions=conditions.conditional if conditions else None,
95
123
  )
96
124
  _window_node.rebuild_cache()
97
125
  _window_node.resolve()
98
126
  window_node = StrategyNode(
99
- input_concepts=[concept] + local_optional,
100
- output_concepts=[concept] + local_optional,
127
+ input_concepts=[concept] + additional_outputs + parent_concepts + targets,
128
+ output_concepts=[concept] + additional_outputs + parent_concepts + targets,
101
129
  environment=environment,
102
130
  parents=[_window_node],
103
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,
104
173
  )
105
- return window_node
@@ -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
  )
trilogy/parsing/common.py CHANGED
@@ -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]