pytrilogy 0.0.2.49__tar.gz → 0.0.2.51__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.
- {pytrilogy-0.0.2.49/pytrilogy.egg-info → pytrilogy-0.0.2.51}/PKG-INFO +1 -1
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51/pytrilogy.egg-info}/PKG-INFO +1 -1
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/pytrilogy.egg-info/SOURCES.txt +2 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_discovery_nodes.py +0 -1
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_environment.py +2 -2
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_functions.py +33 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_models.py +5 -3
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_query_processing.py +28 -9
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/__init__.py +1 -1
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/enums.py +11 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/functions.py +4 -1
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/internal.py +5 -1
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/models.py +135 -263
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/concept_strategies_v3.py +14 -7
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/basic_node.py +7 -3
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/common.py +8 -5
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/filter_node.py +5 -8
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/group_node.py +24 -9
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/group_to_node.py +0 -2
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/multiselect_node.py +4 -5
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/node_merge_node.py +14 -3
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/rowset_node.py +3 -5
- pytrilogy-0.0.2.51/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +203 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/select_merge_node.py +153 -66
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/union_node.py +0 -1
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/unnest_node.py +0 -2
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/window_node.py +0 -2
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/nodes/base_node.py +2 -36
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/nodes/filter_node.py +0 -3
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/nodes/group_node.py +19 -13
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/nodes/merge_node.py +2 -5
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/nodes/select_node_v2.py +0 -4
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/nodes/union_node.py +0 -3
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/nodes/unnest_node.py +0 -3
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/nodes/window_node.py +0 -3
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/utility.py +3 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/query_processor.py +0 -1
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/dialect/base.py +14 -2
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/dialect/duckdb.py +7 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/hooks/graph_hook.py +17 -1
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/parsing/common.py +68 -17
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/parsing/parse_engine.py +70 -20
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/parsing/render.py +8 -1
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/parsing/trilogy.lark +3 -1
- pytrilogy-0.0.2.51/trilogy/scripts/__init__.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/LICENSE.md +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/README.md +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/pyproject.toml +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/pytrilogy.egg-info/dependency_links.txt +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/pytrilogy.egg-info/entry_points.txt +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/pytrilogy.egg-info/requires.txt +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/pytrilogy.egg-info/top_level.txt +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/setup.cfg +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/setup.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_datatypes.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_declarations.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_derived_concepts.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_enums.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_executor.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_imports.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_metadata.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_multi_join_assignments.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_parse_engine.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_parsing.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_partial_handling.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_select.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_show.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_statements.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_undefined_concept.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/tests/test_where_clause.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/compiler.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/constants.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/env_processor.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/ergonomics.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/exceptions.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/graph_models.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/optimization.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/optimizations/__init__.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/optimizations/base_optimization.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/optimizations/inline_constant.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/optimizations/inline_datasource.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/__init__.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/graph_utils.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/__init__.py +0 -0
- {pytrilogy-0.0.2.49/trilogy/dialect → pytrilogy-0.0.2.51/trilogy/core/processing/node_generators/select_helpers}/__init__.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/node_generators/select_node.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/core/processing/nodes/__init__.py +0 -0
- {pytrilogy-0.0.2.49/trilogy/hooks → pytrilogy-0.0.2.51/trilogy/dialect}/__init__.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/dialect/bigquery.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/dialect/common.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/dialect/config.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/dialect/enums.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/dialect/presto.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/dialect/snowflake.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/dialect/sql_server.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/engine.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/executor.py +0 -0
- {pytrilogy-0.0.2.49/trilogy/metadata → pytrilogy-0.0.2.51/trilogy/hooks}/__init__.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.0.2.49/trilogy/parsing → pytrilogy-0.0.2.51/trilogy/metadata}/__init__.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/parser.py +0 -0
- {pytrilogy-0.0.2.49/trilogy/scripts → pytrilogy-0.0.2.51/trilogy/parsing}/__init__.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/py.typed +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.0.2.49 → pytrilogy-0.0.2.51}/trilogy/utility.py +0 -0
|
@@ -73,6 +73,8 @@ trilogy/core/processing/node_generators/select_node.py
|
|
|
73
73
|
trilogy/core/processing/node_generators/union_node.py
|
|
74
74
|
trilogy/core/processing/node_generators/unnest_node.py
|
|
75
75
|
trilogy/core/processing/node_generators/window_node.py
|
|
76
|
+
trilogy/core/processing/node_generators/select_helpers/__init__.py
|
|
77
|
+
trilogy/core/processing/node_generators/select_helpers/datasource_injection.py
|
|
76
78
|
trilogy/core/processing/nodes/__init__.py
|
|
77
79
|
trilogy/core/processing/nodes/base_node.py
|
|
78
80
|
trilogy/core/processing/nodes/filter_node.py
|
|
@@ -15,7 +15,6 @@ def test_group_node(test_environment, test_environment_graph):
|
|
|
15
15
|
output_concepts=[total_revenue, category],
|
|
16
16
|
input_concepts=[category, revenue],
|
|
17
17
|
environment=test_environment,
|
|
18
|
-
g=test_environment_graph,
|
|
19
18
|
parents=[
|
|
20
19
|
search_concepts(
|
|
21
20
|
[category, revenue],
|
|
@@ -7,9 +7,9 @@ from trilogy.core.models import Environment
|
|
|
7
7
|
|
|
8
8
|
|
|
9
9
|
def test_environment_serialization(test_environment: Environment):
|
|
10
|
-
str(test_environment)
|
|
11
|
-
path = test_environment.to_cache()
|
|
12
10
|
|
|
11
|
+
path = test_environment.to_cache()
|
|
12
|
+
print(path)
|
|
13
13
|
test_environment2 = Environment.from_cache(path)
|
|
14
14
|
assert test_environment2
|
|
15
15
|
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
# from trilogy.compiler import compile
|
|
2
|
+
from datetime import date, datetime
|
|
2
3
|
from logging import INFO
|
|
3
4
|
|
|
4
5
|
from pytest import raises
|
|
5
6
|
|
|
7
|
+
from trilogy import Dialects
|
|
6
8
|
from trilogy.constants import logger
|
|
7
9
|
from trilogy.core.enums import Purpose, PurposeLineage
|
|
8
10
|
from trilogy.core.exceptions import InvalidSyntaxException
|
|
@@ -154,6 +156,36 @@ def test_explicit_cast(test_environment):
|
|
|
154
156
|
dialect.compile_statement(process_query(test_environment, select))
|
|
155
157
|
|
|
156
158
|
|
|
159
|
+
def test_literal_cast(test_environment):
|
|
160
|
+
declarations = """
|
|
161
|
+
select
|
|
162
|
+
'1'::int -> one,
|
|
163
|
+
'1'::float -> one_float,
|
|
164
|
+
'1'::string -> one_string,
|
|
165
|
+
'2024-01-01'::date -> one_date,
|
|
166
|
+
'2024-01-01 01:01:01'::datetime -> one_datetime,
|
|
167
|
+
'true'::bool -> one_bool,
|
|
168
|
+
;"""
|
|
169
|
+
env, parsed = parse(declarations, environment=test_environment)
|
|
170
|
+
|
|
171
|
+
select: SelectStatement = parsed[-1]
|
|
172
|
+
z = (
|
|
173
|
+
Dialects.DUCK_DB.default_executor(environment=test_environment)
|
|
174
|
+
.execute_query(parsed[-1])
|
|
175
|
+
.fetchall()
|
|
176
|
+
)
|
|
177
|
+
assert z[0].one == 1
|
|
178
|
+
assert z[0].one_float == 1.0
|
|
179
|
+
assert z[0].one_string == "1"
|
|
180
|
+
assert z[0].one_date == date(year=2024, month=1, day=1)
|
|
181
|
+
assert z[0].one_datetime == datetime(
|
|
182
|
+
year=2024, month=1, day=1, hour=1, minute=1, second=1
|
|
183
|
+
)
|
|
184
|
+
assert z[0].one_bool
|
|
185
|
+
for dialect in TEST_DIALECTS:
|
|
186
|
+
dialect.compile_statement(process_query(test_environment, select))
|
|
187
|
+
|
|
188
|
+
|
|
157
189
|
def test_math_functions(test_environment):
|
|
158
190
|
declarations = """
|
|
159
191
|
|
|
@@ -210,6 +242,7 @@ def test_case_function(test_environment):
|
|
|
210
242
|
test_upper_case
|
|
211
243
|
;"""
|
|
212
244
|
env, parsed = parse(declarations, environment=test_environment)
|
|
245
|
+
|
|
213
246
|
assert (
|
|
214
247
|
test_environment.concepts["category_name"]
|
|
215
248
|
in test_environment.concepts["test_upper_case"].lineage.concept_arguments
|
|
@@ -68,7 +68,7 @@ def test_cte_merge(test_environment, test_environment_graph):
|
|
|
68
68
|
|
|
69
69
|
|
|
70
70
|
def test_concept(test_environment, test_environment_graph):
|
|
71
|
-
test_concept = list(test_environment.concepts.values())[0]
|
|
71
|
+
test_concept: Concept = list(test_environment.concepts.values())[0]
|
|
72
72
|
new = test_concept.with_namespace("test")
|
|
73
73
|
assert (
|
|
74
74
|
new.namespace == ("test" + "." + test_concept.namespace)
|
|
@@ -129,7 +129,7 @@ def test_grain(test_environment):
|
|
|
129
129
|
x = Grain(components=[oid, pid])
|
|
130
130
|
y = Grain(components=[pid, cid])
|
|
131
131
|
z = Grain(components=[cid])
|
|
132
|
-
z2 = Grain(
|
|
132
|
+
z2 = Grain.from_concepts([cid, cname], environment=test_environment)
|
|
133
133
|
|
|
134
134
|
assert x.intersection(y) == Grain(components=[pid])
|
|
135
135
|
assert x.union(y) == Grain(components=[oid, pid, cid])
|
|
@@ -137,7 +137,9 @@ def test_grain(test_environment):
|
|
|
137
137
|
assert z.isdisjoint(x)
|
|
138
138
|
assert z.issubset(y)
|
|
139
139
|
|
|
140
|
-
assert
|
|
140
|
+
assert (
|
|
141
|
+
z2 == z
|
|
142
|
+
), f"Property should be removed from grain ({z.components}) vs {z2.components}"
|
|
141
143
|
|
|
142
144
|
|
|
143
145
|
def test_select(test_environment: Environment):
|
|
@@ -1,4 +1,10 @@
|
|
|
1
|
-
from trilogy.core.models import
|
|
1
|
+
from trilogy.core.models import (
|
|
2
|
+
Environment,
|
|
3
|
+
Grain,
|
|
4
|
+
QueryDatasource,
|
|
5
|
+
SelectStatement,
|
|
6
|
+
SourceType,
|
|
7
|
+
)
|
|
2
8
|
from trilogy.core.processing.concept_strategies_v3 import search_concepts
|
|
3
9
|
from trilogy.core.query_processor import get_query_datasources, process_query
|
|
4
10
|
|
|
@@ -7,7 +13,7 @@ def test_direct_select(test_environment, test_environment_graph):
|
|
|
7
13
|
product = test_environment.concepts["product_id"]
|
|
8
14
|
# concept, grain: Grain, environment: Environment, g: ReferenceGraph, query_graph: ReferenceGraph
|
|
9
15
|
datasource = search_concepts(
|
|
10
|
-
[product] + product.grain.
|
|
16
|
+
[product] + [test_environment.concepts[c] for c in product.grain.components],
|
|
11
17
|
environment=test_environment,
|
|
12
18
|
g=test_environment_graph,
|
|
13
19
|
depth=0,
|
|
@@ -27,7 +33,8 @@ def test_get_datasource_from_window_function(
|
|
|
27
33
|
# concept, grain: Grain, environment: Environment, g: ReferenceGraph, query_graph: ReferenceGraph
|
|
28
34
|
# assert product_rank.grain.components[0] == test_environment.concepts['name']
|
|
29
35
|
node = search_concepts(
|
|
30
|
-
[product_rank]
|
|
36
|
+
[product_rank]
|
|
37
|
+
+ [test_environment.concepts[c] for c in product_rank.grain.components],
|
|
31
38
|
environment=test_environment,
|
|
32
39
|
g=test_environment_graph,
|
|
33
40
|
depth=0,
|
|
@@ -38,14 +45,18 @@ def test_get_datasource_from_window_function(
|
|
|
38
45
|
assert product_rank in datasource.output_concepts
|
|
39
46
|
# assert datasource.grain == product_rank.grain
|
|
40
47
|
assert isinstance(datasource, QueryDatasource)
|
|
41
|
-
assert datasource.grain.
|
|
48
|
+
assert datasource.grain.components == product_rank.grain.components
|
|
42
49
|
|
|
43
50
|
product_rank_by_category = test_environment.concepts[
|
|
44
51
|
"product_revenue_rank_by_category"
|
|
45
52
|
]
|
|
46
53
|
# concept, grain: Grain, environment: Environment, g: ReferenceGraph, query_graph: ReferenceGraph
|
|
47
54
|
datasource = search_concepts(
|
|
48
|
-
[product_rank_by_category]
|
|
55
|
+
[product_rank_by_category]
|
|
56
|
+
+ [
|
|
57
|
+
test_environment.concepts[c]
|
|
58
|
+
for c in product_rank_by_category.grain.components
|
|
59
|
+
],
|
|
49
60
|
environment=test_environment,
|
|
50
61
|
g=test_environment_graph,
|
|
51
62
|
depth=0,
|
|
@@ -68,7 +79,8 @@ def test_get_datasource_for_filter(
|
|
|
68
79
|
"product_id",
|
|
69
80
|
}
|
|
70
81
|
datasource = search_concepts(
|
|
71
|
-
[hi_rev_product]
|
|
82
|
+
[hi_rev_product]
|
|
83
|
+
+ [test_environment.concepts[c] for c in hi_rev_product.grain.components],
|
|
72
84
|
environment=test_environment,
|
|
73
85
|
g=test_environment_graph,
|
|
74
86
|
depth=0,
|
|
@@ -86,7 +98,7 @@ def test_select_output(test_environment, test_environment_graph):
|
|
|
86
98
|
# concept, grain: Grain, environment: Environment, g: ReferenceGraph, query_graph: ReferenceGraph
|
|
87
99
|
|
|
88
100
|
datasource = search_concepts(
|
|
89
|
-
[product] + product.grain.
|
|
101
|
+
[product] + [test_environment.concepts[c] for c in product.grain.components],
|
|
90
102
|
environment=test_environment,
|
|
91
103
|
g=test_environment_graph,
|
|
92
104
|
depth=0,
|
|
@@ -114,18 +126,25 @@ def test_basic_aggregate(test_environment: Environment, test_environment_graph):
|
|
|
114
126
|
|
|
115
127
|
|
|
116
128
|
def test_join_aggregate(test_environment: Environment, test_environment_graph):
|
|
129
|
+
from trilogy.hooks.query_debugger import DebuggingHook
|
|
130
|
+
|
|
131
|
+
DebuggingHook()
|
|
117
132
|
category_id = test_environment.concepts["category_id"]
|
|
118
133
|
total_revenue = test_environment.concepts["total_revenue"]
|
|
119
134
|
# concept, grain: Grain, environment: Environment, g: ReferenceGraph, query_graph: ReferenceGraph
|
|
120
135
|
datasource = search_concepts(
|
|
121
|
-
[
|
|
136
|
+
[
|
|
137
|
+
total_revenue.with_grain(Grain(components={"local.category_id"})),
|
|
138
|
+
category_id,
|
|
139
|
+
],
|
|
122
140
|
environment=test_environment,
|
|
123
141
|
g=test_environment_graph,
|
|
124
142
|
depth=0,
|
|
125
143
|
).resolve()
|
|
126
144
|
assert isinstance(datasource, QueryDatasource)
|
|
145
|
+
assert datasource.source_type == SourceType.GROUP
|
|
127
146
|
assert len(set([datasource.name for datasource in datasource.datasources])) == 1
|
|
128
|
-
assert datasource.grain.components ==
|
|
147
|
+
assert datasource.grain.components == {"local.category_id"}
|
|
129
148
|
|
|
130
149
|
|
|
131
150
|
def test_query_aggregation(test_environment, test_environment_graph):
|
|
@@ -120,6 +120,8 @@ class FunctionType(Enum):
|
|
|
120
120
|
|
|
121
121
|
ALIAS = "alias"
|
|
122
122
|
|
|
123
|
+
PARENTHETICAL = "parenthetical"
|
|
124
|
+
|
|
123
125
|
# Generic
|
|
124
126
|
CASE = "case"
|
|
125
127
|
CAST = "cast"
|
|
@@ -135,6 +137,8 @@ class FunctionType(Enum):
|
|
|
135
137
|
ATTR_ACCESS = "attr_access"
|
|
136
138
|
STRUCT = "struct"
|
|
137
139
|
ARRAY = "array"
|
|
140
|
+
DATE_LITERAL = "date_literal"
|
|
141
|
+
DATETIME_LITERAL = "datetime_literal"
|
|
138
142
|
|
|
139
143
|
# TEXT AND MAYBE MORE
|
|
140
144
|
SPLIT = "split"
|
|
@@ -260,6 +264,13 @@ class ComparisonOperator(Enum):
|
|
|
260
264
|
CONTAINS = "contains"
|
|
261
265
|
ELSE = "else"
|
|
262
266
|
|
|
267
|
+
def __eq__(self, other):
|
|
268
|
+
if isinstance(other, str):
|
|
269
|
+
return self.value == other
|
|
270
|
+
if not isinstance(other, ComparisonOperator):
|
|
271
|
+
return False
|
|
272
|
+
return self.value == other.value
|
|
273
|
+
|
|
263
274
|
@classmethod
|
|
264
275
|
def _missing_(cls, value):
|
|
265
276
|
if not isinstance(value, list) and " " in str(value):
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
from datetime import date, datetime
|
|
1
2
|
from typing import Optional
|
|
2
3
|
|
|
3
4
|
from trilogy.constants import MagicConstants
|
|
@@ -17,6 +18,8 @@ from trilogy.core.models import (
|
|
|
17
18
|
arg_to_datatype,
|
|
18
19
|
)
|
|
19
20
|
|
|
21
|
+
GENERIC_ARGS = Concept | Function | str | int | float | date | datetime
|
|
22
|
+
|
|
20
23
|
|
|
21
24
|
def create_function_derived_concept(
|
|
22
25
|
name: str,
|
|
@@ -262,7 +265,7 @@ def get_attr_datatype(
|
|
|
262
265
|
return arg.datatype
|
|
263
266
|
|
|
264
267
|
|
|
265
|
-
def AttrAccess(args: list[
|
|
268
|
+
def AttrAccess(args: list[GENERIC_ARGS]):
|
|
266
269
|
return Function(
|
|
267
270
|
operator=FunctionType.ATTR_ACCESS,
|
|
268
271
|
arguments=args,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
from trilogy.core.constants import ALL_ROWS_CONCEPT, INTERNAL_NAMESPACE
|
|
2
2
|
from trilogy.core.enums import FunctionType, Purpose
|
|
3
|
-
from trilogy.core.models import Concept, DataType, Function
|
|
3
|
+
from trilogy.core.models import Concept, DataType, Function, Grain
|
|
4
4
|
|
|
5
5
|
DEFAULT_CONCEPTS = {
|
|
6
6
|
ALL_ROWS_CONCEPT: Concept(
|
|
@@ -8,6 +8,7 @@ DEFAULT_CONCEPTS = {
|
|
|
8
8
|
namespace=INTERNAL_NAMESPACE,
|
|
9
9
|
datatype=DataType.INTEGER,
|
|
10
10
|
purpose=Purpose.CONSTANT,
|
|
11
|
+
grain=Grain(),
|
|
11
12
|
lineage=Function(
|
|
12
13
|
operator=FunctionType.CONSTANT,
|
|
13
14
|
arguments=[1],
|
|
@@ -20,17 +21,20 @@ DEFAULT_CONCEPTS = {
|
|
|
20
21
|
namespace=INTERNAL_NAMESPACE,
|
|
21
22
|
datatype=DataType.STRING,
|
|
22
23
|
purpose=Purpose.KEY,
|
|
24
|
+
grain=Grain(),
|
|
23
25
|
),
|
|
24
26
|
"datasource": Concept(
|
|
25
27
|
name="datasource",
|
|
26
28
|
namespace=INTERNAL_NAMESPACE,
|
|
27
29
|
datatype=DataType.STRING,
|
|
28
30
|
purpose=Purpose.KEY,
|
|
31
|
+
grain=Grain(),
|
|
29
32
|
),
|
|
30
33
|
"query_text": Concept(
|
|
31
34
|
name="query_text",
|
|
32
35
|
namespace=INTERNAL_NAMESPACE,
|
|
33
36
|
datatype=DataType.STRING,
|
|
34
37
|
purpose=Purpose.KEY,
|
|
38
|
+
grain=Grain(),
|
|
35
39
|
),
|
|
36
40
|
}
|