pytrilogy 0.0.3.48__tar.gz → 0.0.3.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.3.48/pytrilogy.egg-info → pytrilogy-0.0.3.51}/PKG-INFO +1 -1
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51/pytrilogy.egg-info}/PKG-INFO +1 -1
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_functions.py +4 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_parse_engine.py +23 -1
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/__init__.py +1 -1
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/enums.py +3 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/functions.py +22 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/models/author.py +6 -2
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/models/build.py +15 -3
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/models/execute.py +4 -2
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/concept_strategies_v3.py +1 -1
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/common.py +3 -4
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/filter_node.py +142 -91
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/group_node.py +3 -4
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/nodes/base_node.py +4 -1
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/statements/author.py +0 -2
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/dialect/base.py +7 -2
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/dialect/bigquery.py +2 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/dialect/duckdb.py +2 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/parsing/common.py +25 -15
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/parsing/parse_engine.py +35 -7
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/parsing/trilogy.lark +10 -4
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/LICENSE.md +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/README.md +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/pyproject.toml +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/pytrilogy.egg-info/SOURCES.txt +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/pytrilogy.egg-info/dependency_links.txt +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/pytrilogy.egg-info/entry_points.txt +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/pytrilogy.egg-info/requires.txt +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/pytrilogy.egg-info/top_level.txt +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/setup.cfg +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/setup.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_datatypes.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_declarations.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_derived_concepts.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_discovery_nodes.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_enums.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_environment.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_executor.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_failure.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_imports.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_metadata.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_models.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_multi_join_assignments.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_parsing.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_parsing_failures.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_partial_handling.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_query_processing.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_query_render.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_select.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_show.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_statements.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_typing.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_undefined_concept.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_user_functions.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/tests/test_where_clause.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/authoring/__init__.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/compiler.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/constants.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/env_processor.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/ergonomics.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/exceptions.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/graph_models.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/internal.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/models/__init__.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/models/build_environment.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/models/core.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/models/datasource.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/models/environment.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/optimization.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/optimizations/__init__.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/optimizations/base_optimization.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/optimizations/inline_datasource.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/__init__.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/graph_utils.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/__init__.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/basic_node.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/node_merge_node.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/select_node.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/union_node.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/window_node.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/nodes/__init__.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/nodes/filter_node.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/nodes/group_node.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/nodes/merge_node.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/nodes/union_node.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/nodes/unnest_node.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/nodes/window_node.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/utility.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/query_processor.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/statements/__init__.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/statements/build.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/statements/common.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/statements/execute.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/dialect/__init__.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/dialect/common.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/dialect/config.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/dialect/dataframe.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/dialect/enums.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/dialect/presto.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/dialect/snowflake.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/dialect/sql_server.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/engine.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/executor.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/hooks/__init__.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/hooks/graph_hook.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/metadata/__init__.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/parser.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/parsing/__init__.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/parsing/render.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/py.typed +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/render.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/scripts/__init__.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/std/__init__.py +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/std/date.preql +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/std/display.preql +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/std/geography.preql +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/std/money.preql +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/std/report.preql +0 -0
- {pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/utility.py +0 -0
|
@@ -214,6 +214,8 @@ def test_math_functions(test_environment):
|
|
|
214
214
|
property order_id.order_nested <- revenue * 2/2;
|
|
215
215
|
property order_id.rounded <- round(revenue + 2.01,2);
|
|
216
216
|
property order_id.rounded_default <- round(revenue + 2.01);
|
|
217
|
+
property order_id.floor <- floor(revenue + 2.01);
|
|
218
|
+
property order_id.ceil <- ceil(revenue + 2.01);
|
|
217
219
|
constant random <- random(1);
|
|
218
220
|
select
|
|
219
221
|
order_id,
|
|
@@ -224,6 +226,8 @@ def test_math_functions(test_environment):
|
|
|
224
226
|
order_add,
|
|
225
227
|
rounded,
|
|
226
228
|
rounded_default,
|
|
229
|
+
floor,
|
|
230
|
+
ceil,
|
|
227
231
|
random,
|
|
228
232
|
;"""
|
|
229
233
|
env, parsed = parse(declarations, environment=test_environment)
|
|
@@ -2,7 +2,12 @@ from pytest import raises
|
|
|
2
2
|
|
|
3
3
|
from trilogy.core.exceptions import UndefinedConceptException
|
|
4
4
|
from trilogy.core.models.environment import Environment
|
|
5
|
-
from trilogy.parsing.parse_engine import
|
|
5
|
+
from trilogy.parsing.parse_engine import (
|
|
6
|
+
PARSER,
|
|
7
|
+
InvalidSyntaxException,
|
|
8
|
+
ParseToObjects,
|
|
9
|
+
unpack_visit_error,
|
|
10
|
+
)
|
|
6
11
|
|
|
7
12
|
TEXT = """
|
|
8
13
|
const a <- 1;
|
|
@@ -29,3 +34,20 @@ def test_parser():
|
|
|
29
34
|
with raises(UndefinedConceptException):
|
|
30
35
|
unpack_visit_error(e)
|
|
31
36
|
assert failed
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
TEXT2 = """
|
|
40
|
+
const a <- 1;
|
|
41
|
+
|
|
42
|
+
select
|
|
43
|
+
a,
|
|
44
|
+
FROM a
|
|
45
|
+
;
|
|
46
|
+
"""
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
def test_from_error():
|
|
50
|
+
env = Environment()
|
|
51
|
+
|
|
52
|
+
with raises(InvalidSyntaxException):
|
|
53
|
+
env.parse(TEXT2)
|
|
@@ -131,6 +131,7 @@ class FunctionType(Enum):
|
|
|
131
131
|
CONSTANT = "constant"
|
|
132
132
|
COALESCE = "coalesce"
|
|
133
133
|
IS_NULL = "isnull"
|
|
134
|
+
NULLIF = "nullif"
|
|
134
135
|
BOOL = "bool"
|
|
135
136
|
|
|
136
137
|
# COMPLEX
|
|
@@ -156,6 +157,8 @@ class FunctionType(Enum):
|
|
|
156
157
|
ABS = "abs"
|
|
157
158
|
SQRT = "sqrt"
|
|
158
159
|
RANDOM = "random"
|
|
160
|
+
FLOOR = "floor"
|
|
161
|
+
CEIL = "ceil"
|
|
159
162
|
|
|
160
163
|
# Aggregates
|
|
161
164
|
## group is not a real aggregate - it just means group by this + some other set of fields
|
|
@@ -279,6 +279,12 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
279
279
|
output_purpose=Purpose.PROPERTY,
|
|
280
280
|
arg_count=1,
|
|
281
281
|
),
|
|
282
|
+
FunctionType.NULLIF: FunctionConfig(
|
|
283
|
+
valid_inputs={*DataType},
|
|
284
|
+
output_purpose=Purpose.PROPERTY,
|
|
285
|
+
output_type_function=lambda args: get_output_type_at_index(args, 0),
|
|
286
|
+
arg_count=2,
|
|
287
|
+
),
|
|
282
288
|
FunctionType.COALESCE: FunctionConfig(
|
|
283
289
|
valid_inputs={*DataType},
|
|
284
290
|
output_purpose=Purpose.PROPERTY,
|
|
@@ -637,6 +643,22 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
637
643
|
output_type_function=lambda args: get_output_type_at_index(args, 0),
|
|
638
644
|
arg_count=2,
|
|
639
645
|
),
|
|
646
|
+
FunctionType.FLOOR: FunctionConfig(
|
|
647
|
+
valid_inputs=[
|
|
648
|
+
{DataType.INTEGER, DataType.FLOAT, DataType.NUMBER, DataType.NUMERIC},
|
|
649
|
+
],
|
|
650
|
+
output_purpose=Purpose.PROPERTY,
|
|
651
|
+
output_type=DataType.INTEGER,
|
|
652
|
+
arg_count=1,
|
|
653
|
+
),
|
|
654
|
+
FunctionType.CEIL: FunctionConfig(
|
|
655
|
+
valid_inputs=[
|
|
656
|
+
{DataType.INTEGER, DataType.FLOAT, DataType.NUMBER, DataType.NUMERIC},
|
|
657
|
+
],
|
|
658
|
+
output_purpose=Purpose.PROPERTY,
|
|
659
|
+
output_type=DataType.INTEGER,
|
|
660
|
+
arg_count=1,
|
|
661
|
+
),
|
|
640
662
|
FunctionType.CUSTOM: FunctionConfig(
|
|
641
663
|
output_purpose=Purpose.PROPERTY,
|
|
642
664
|
arg_count=InfiniteFunctionArgs,
|
|
@@ -1945,11 +1945,15 @@ class FilterItem(DataTyped, Namespaced, ConceptArgs, BaseModel):
|
|
|
1945
1945
|
|
|
1946
1946
|
@property
|
|
1947
1947
|
def output_datatype(self):
|
|
1948
|
-
return self.content
|
|
1948
|
+
return arg_to_datatype(self.content)
|
|
1949
1949
|
|
|
1950
1950
|
@property
|
|
1951
1951
|
def concept_arguments(self):
|
|
1952
|
-
|
|
1952
|
+
if isinstance(self.content, ConceptRef):
|
|
1953
|
+
return [self.content] + self.where.concept_arguments
|
|
1954
|
+
elif isinstance(self.content, ConceptArgs):
|
|
1955
|
+
return self.content.concept_arguments + self.where.concept_arguments
|
|
1956
|
+
return self.where.concept_arguments
|
|
1953
1957
|
|
|
1954
1958
|
|
|
1955
1959
|
class RowsetLineage(Namespaced, Mergeable, BaseModel):
|
|
@@ -1173,7 +1173,7 @@ class BuildAggregateWrapper(BuildConceptArgs, DataTyped, BaseModel):
|
|
|
1173
1173
|
|
|
1174
1174
|
|
|
1175
1175
|
class BuildFilterItem(BuildConceptArgs, BaseModel):
|
|
1176
|
-
content:
|
|
1176
|
+
content: "BuildExpr"
|
|
1177
1177
|
where: BuildWhereClause
|
|
1178
1178
|
|
|
1179
1179
|
def __str__(self):
|
|
@@ -1181,15 +1181,27 @@ class BuildFilterItem(BuildConceptArgs, BaseModel):
|
|
|
1181
1181
|
|
|
1182
1182
|
@property
|
|
1183
1183
|
def output_datatype(self):
|
|
1184
|
-
return self.content
|
|
1184
|
+
return arg_to_datatype(self.content)
|
|
1185
1185
|
|
|
1186
1186
|
@property
|
|
1187
1187
|
def output_purpose(self):
|
|
1188
1188
|
return self.content.purpose
|
|
1189
1189
|
|
|
1190
|
+
@property
|
|
1191
|
+
def content_concept_arguments(self):
|
|
1192
|
+
if isinstance(self.content, BuildConcept):
|
|
1193
|
+
return [self.content]
|
|
1194
|
+
elif isinstance(self.content, BuildConceptArgs):
|
|
1195
|
+
return self.content.concept_arguments
|
|
1196
|
+
return []
|
|
1197
|
+
|
|
1190
1198
|
@property
|
|
1191
1199
|
def concept_arguments(self):
|
|
1192
|
-
|
|
1200
|
+
if isinstance(self.content, BuildConcept):
|
|
1201
|
+
return [self.content] + self.where.concept_arguments
|
|
1202
|
+
elif isinstance(self.content, BuildConceptArgs):
|
|
1203
|
+
return self.content.concept_arguments + self.where.concept_arguments
|
|
1204
|
+
return self.where.concept_arguments
|
|
1193
1205
|
|
|
1194
1206
|
|
|
1195
1207
|
class BuildRowsetLineage(BuildConceptArgs, BaseModel):
|
|
@@ -695,7 +695,7 @@ class QueryDatasource(BaseModel):
|
|
|
695
695
|
"can only merge two datasources if the force_group flag is the same"
|
|
696
696
|
)
|
|
697
697
|
logger.debug(
|
|
698
|
-
f"
|
|
698
|
+
f"[Query Datasource] merging {self.name} with"
|
|
699
699
|
f" {[c.address for c in self.output_concepts]} concepts and"
|
|
700
700
|
f" {other.name} with {[c.address for c in other.output_concepts]} concepts"
|
|
701
701
|
)
|
|
@@ -759,7 +759,9 @@ class QueryDatasource(BaseModel):
|
|
|
759
759
|
hidden_concepts=hidden,
|
|
760
760
|
ordering=self.ordering,
|
|
761
761
|
)
|
|
762
|
-
|
|
762
|
+
logger.debug(
|
|
763
|
+
f"[Query Datasource] merged with {[c.address for c in qds.output_concepts]} concepts"
|
|
764
|
+
)
|
|
763
765
|
return qds
|
|
764
766
|
|
|
765
767
|
@property
|
|
@@ -319,7 +319,7 @@ def generate_node(
|
|
|
319
319
|
]
|
|
320
320
|
|
|
321
321
|
logger.info(
|
|
322
|
-
f"{depth_to_prefix(depth)}{LOGGER_PREFIX} for {concept.address}, generating aggregate node with {[x
|
|
322
|
+
f"{depth_to_prefix(depth)}{LOGGER_PREFIX} for {concept.address}, generating aggregate node with {[x for x in agg_optional]}"
|
|
323
323
|
)
|
|
324
324
|
return gen_group_node(
|
|
325
325
|
concept,
|
|
@@ -67,14 +67,14 @@ def resolve_condition_parent_concepts(
|
|
|
67
67
|
def resolve_filter_parent_concepts(
|
|
68
68
|
concept: BuildConcept,
|
|
69
69
|
environment: BuildEnvironment,
|
|
70
|
-
) -> Tuple[
|
|
70
|
+
) -> Tuple[List[BuildConcept], List[Tuple[BuildConcept, ...]]]:
|
|
71
71
|
if not isinstance(concept.lineage, (BuildFilterItem,)):
|
|
72
72
|
raise ValueError(
|
|
73
73
|
f"Concept {concept} lineage is not filter item, is {type(concept.lineage)}"
|
|
74
74
|
)
|
|
75
75
|
direct_parent = concept.lineage.content
|
|
76
76
|
base_existence = []
|
|
77
|
-
base_rows = [direct_parent]
|
|
77
|
+
base_rows = [direct_parent] if isinstance(direct_parent, BuildConcept) else []
|
|
78
78
|
condition_rows, condition_existence = resolve_condition_parent_concepts(
|
|
79
79
|
concept.lineage.where
|
|
80
80
|
)
|
|
@@ -90,11 +90,10 @@ def resolve_filter_parent_concepts(
|
|
|
90
90
|
|
|
91
91
|
if concept.lineage.where.existence_arguments:
|
|
92
92
|
return (
|
|
93
|
-
concept.lineage.content,
|
|
94
93
|
unique(base_rows, "address"),
|
|
95
94
|
base_existence,
|
|
96
95
|
)
|
|
97
|
-
return
|
|
96
|
+
return unique(base_rows, "address"), []
|
|
98
97
|
|
|
99
98
|
|
|
100
99
|
def gen_property_enrichment_node(
|
{pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/filter_node.py
RENAMED
|
@@ -25,71 +25,140 @@ LOGGER_PREFIX = "[GEN_FILTER_NODE]"
|
|
|
25
25
|
FILTER_TYPES = (BuildFilterItem,)
|
|
26
26
|
|
|
27
27
|
|
|
28
|
-
def
|
|
29
|
-
concept: BuildConcept,
|
|
28
|
+
def pushdown_filter_to_parent(
|
|
30
29
|
local_optional: List[BuildConcept],
|
|
31
|
-
|
|
32
|
-
|
|
30
|
+
conditions: BuildWhereClause | None,
|
|
31
|
+
filter_where: BuildWhereClause,
|
|
32
|
+
same_filter_optional: list[BuildConcept],
|
|
33
33
|
depth: int,
|
|
34
|
-
|
|
35
|
-
|
|
34
|
+
) -> bool:
|
|
35
|
+
optimized_pushdown = False
|
|
36
|
+
if not is_scalar_condition(filter_where.conditional):
|
|
37
|
+
optimized_pushdown = False
|
|
38
|
+
elif not local_optional:
|
|
39
|
+
optimized_pushdown = True
|
|
40
|
+
elif conditions and conditions == filter_where:
|
|
41
|
+
logger.info(
|
|
42
|
+
f"{padding(depth)}{LOGGER_PREFIX} query conditions are the same as filter conditions, can optimize across all concepts"
|
|
43
|
+
)
|
|
44
|
+
optimized_pushdown = True
|
|
45
|
+
elif same_filter_optional == local_optional:
|
|
46
|
+
logger.info(
|
|
47
|
+
f"{padding(depth)}{LOGGER_PREFIX} all optional concepts are included in the filter, can optimize across all concepts"
|
|
48
|
+
)
|
|
49
|
+
optimized_pushdown = True
|
|
50
|
+
|
|
51
|
+
return optimized_pushdown
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def build_parent_concepts(
|
|
55
|
+
concept: BuildConcept,
|
|
56
|
+
environment: BuildEnvironment,
|
|
57
|
+
local_optional: List[BuildConcept],
|
|
36
58
|
conditions: BuildWhereClause | None = None,
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
59
|
+
depth: int = 0,
|
|
60
|
+
):
|
|
61
|
+
parent_row_concepts, parent_existence_concepts = resolve_filter_parent_concepts(
|
|
62
|
+
concept, environment
|
|
40
63
|
)
|
|
41
64
|
if not isinstance(concept.lineage, FILTER_TYPES):
|
|
42
65
|
raise SyntaxError('Filter node must have a filter type lineage"')
|
|
43
|
-
|
|
66
|
+
filter_where = concept.lineage.where
|
|
44
67
|
|
|
45
|
-
|
|
68
|
+
same_filter_optional: list[BuildConcept] = []
|
|
46
69
|
|
|
47
70
|
for x in local_optional:
|
|
48
71
|
if isinstance(x.lineage, FILTER_TYPES):
|
|
49
|
-
if concept.lineage.where ==
|
|
72
|
+
if concept.lineage.where == filter_where:
|
|
50
73
|
logger.info(
|
|
51
|
-
f"{padding(depth)}{LOGGER_PREFIX} fetching
|
|
74
|
+
f"{padding(depth)}{LOGGER_PREFIX} fetching parents for peer {x} with same filter conditions"
|
|
52
75
|
)
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
76
|
+
|
|
77
|
+
for arg in x.lineage.content_concept_arguments:
|
|
78
|
+
if arg.address not in parent_row_concepts:
|
|
79
|
+
parent_row_concepts.append(arg)
|
|
80
|
+
same_filter_optional.append(x)
|
|
56
81
|
continue
|
|
57
|
-
|
|
58
|
-
|
|
82
|
+
elif conditions and conditions == filter_where:
|
|
83
|
+
same_filter_optional.append(x)
|
|
59
84
|
|
|
60
85
|
# sometimes, it's okay to include other local optional above the filter
|
|
61
86
|
# in case it is, prep our list
|
|
62
87
|
extra_row_level_optional: list[BuildConcept] = []
|
|
88
|
+
|
|
63
89
|
for x in local_optional:
|
|
64
|
-
if x.address in
|
|
90
|
+
if x.address in same_filter_optional:
|
|
65
91
|
continue
|
|
66
92
|
extra_row_level_optional.append(x)
|
|
93
|
+
is_optimized_pushdown = pushdown_filter_to_parent(
|
|
94
|
+
local_optional, conditions, filter_where, same_filter_optional, depth
|
|
95
|
+
)
|
|
96
|
+
if not is_optimized_pushdown:
|
|
97
|
+
parent_row_concepts += extra_row_level_optional
|
|
98
|
+
return (
|
|
99
|
+
parent_row_concepts,
|
|
100
|
+
parent_existence_concepts,
|
|
101
|
+
same_filter_optional,
|
|
102
|
+
is_optimized_pushdown,
|
|
103
|
+
)
|
|
67
104
|
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
105
|
+
|
|
106
|
+
def add_existence_sources(
|
|
107
|
+
core_parent_nodes: list[StrategyNode],
|
|
108
|
+
parent_existence_concepts: list[tuple[BuildConcept, ...]],
|
|
109
|
+
source_concepts,
|
|
110
|
+
environment,
|
|
111
|
+
g,
|
|
112
|
+
depth,
|
|
113
|
+
history,
|
|
114
|
+
):
|
|
115
|
+
for existence_tuple in parent_existence_concepts:
|
|
116
|
+
if not existence_tuple:
|
|
117
|
+
continue
|
|
76
118
|
logger.info(
|
|
77
|
-
f"{padding(depth)}{LOGGER_PREFIX}
|
|
119
|
+
f"{padding(depth)}{LOGGER_PREFIX} fetching filter node existence parents {[x.address for x in existence_tuple]}"
|
|
78
120
|
)
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
121
|
+
parent_existence = source_concepts(
|
|
122
|
+
mandatory_list=list(existence_tuple),
|
|
123
|
+
environment=environment,
|
|
124
|
+
g=g,
|
|
125
|
+
depth=depth + 1,
|
|
126
|
+
history=history,
|
|
83
127
|
)
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
128
|
+
if not parent_existence:
|
|
129
|
+
logger.info(
|
|
130
|
+
f"{padding(depth)}{LOGGER_PREFIX} filter existence node parents could not be found"
|
|
131
|
+
)
|
|
132
|
+
return None
|
|
133
|
+
core_parent_nodes.append(parent_existence)
|
|
134
|
+
|
|
135
|
+
|
|
136
|
+
def gen_filter_node(
|
|
137
|
+
concept: BuildConcept,
|
|
138
|
+
local_optional: List[BuildConcept],
|
|
139
|
+
environment: BuildEnvironment,
|
|
140
|
+
g,
|
|
141
|
+
depth: int,
|
|
142
|
+
source_concepts,
|
|
143
|
+
history: History | None = None,
|
|
144
|
+
conditions: BuildWhereClause | None = None,
|
|
145
|
+
) -> StrategyNode | None:
|
|
146
|
+
if not isinstance(concept.lineage, FILTER_TYPES):
|
|
147
|
+
raise SyntaxError('Filter node must have a filter type lineage"')
|
|
148
|
+
where = concept.lineage.where
|
|
149
|
+
|
|
150
|
+
(
|
|
151
|
+
parent_row_concepts,
|
|
152
|
+
parent_existence_concepts,
|
|
153
|
+
same_filter_optional,
|
|
154
|
+
optimized_pushdown,
|
|
155
|
+
) = build_parent_concepts(
|
|
156
|
+
concept,
|
|
157
|
+
environment=environment,
|
|
158
|
+
local_optional=local_optional,
|
|
159
|
+
conditions=conditions,
|
|
160
|
+
depth=depth,
|
|
87
161
|
)
|
|
88
|
-
# we'll populate this with the row parent
|
|
89
|
-
# and the existence parent(s)
|
|
90
|
-
core_parents = []
|
|
91
|
-
if not optimized_pushdown:
|
|
92
|
-
parent_row_concepts += extra_row_level_optional
|
|
93
162
|
|
|
94
163
|
row_parent: StrategyNode = source_concepts(
|
|
95
164
|
mandatory_list=parent_row_concepts,
|
|
@@ -100,27 +169,19 @@ def gen_filter_node(
|
|
|
100
169
|
conditions=conditions,
|
|
101
170
|
)
|
|
102
171
|
|
|
172
|
+
core_parent_nodes: list[StrategyNode] = []
|
|
103
173
|
flattened_existence = [x for y in parent_existence_concepts for x in y]
|
|
104
174
|
if parent_existence_concepts:
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
depth=depth + 1,
|
|
116
|
-
history=history,
|
|
117
|
-
)
|
|
118
|
-
if not parent_existence:
|
|
119
|
-
logger.info(
|
|
120
|
-
f"{padding(depth)}{LOGGER_PREFIX} filter existence node parents could not be found"
|
|
121
|
-
)
|
|
122
|
-
return None
|
|
123
|
-
core_parents.append(parent_existence)
|
|
175
|
+
add_existence_sources(
|
|
176
|
+
core_parent_nodes,
|
|
177
|
+
parent_existence_concepts,
|
|
178
|
+
source_concepts,
|
|
179
|
+
environment,
|
|
180
|
+
g,
|
|
181
|
+
depth,
|
|
182
|
+
history,
|
|
183
|
+
)
|
|
184
|
+
|
|
124
185
|
if not row_parent:
|
|
125
186
|
logger.info(
|
|
126
187
|
f"{padding(depth)}{LOGGER_PREFIX} filter node row parents {[x.address for x in parent_row_concepts]} could not be found"
|
|
@@ -129,7 +190,7 @@ def gen_filter_node(
|
|
|
129
190
|
|
|
130
191
|
if optimized_pushdown:
|
|
131
192
|
logger.info(
|
|
132
|
-
f"{padding(depth)}{LOGGER_PREFIX} returning optimized filter node with pushdown to parent with condition {where.conditional}"
|
|
193
|
+
f"{padding(depth)}{LOGGER_PREFIX} returning optimized filter node with pushdown to parent with condition {where.conditional} across {[concept] + same_filter_optional + row_parent.output_concepts} "
|
|
133
194
|
)
|
|
134
195
|
if isinstance(row_parent, SelectNode):
|
|
135
196
|
logger.info(
|
|
@@ -137,7 +198,9 @@ def gen_filter_node(
|
|
|
137
198
|
)
|
|
138
199
|
parent = StrategyNode(
|
|
139
200
|
input_concepts=row_parent.output_concepts,
|
|
140
|
-
output_concepts=[concept]
|
|
201
|
+
output_concepts=[concept]
|
|
202
|
+
+ same_filter_optional
|
|
203
|
+
+ row_parent.output_concepts,
|
|
141
204
|
environment=row_parent.environment,
|
|
142
205
|
parents=[row_parent],
|
|
143
206
|
depth=row_parent.depth,
|
|
@@ -146,46 +209,34 @@ def gen_filter_node(
|
|
|
146
209
|
)
|
|
147
210
|
else:
|
|
148
211
|
parent = row_parent
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
x
|
|
152
|
-
for x in local_optional
|
|
153
|
-
if x.address in [y for y in parent.output_concepts]
|
|
154
|
-
or x.address in [y for y in optional_included]
|
|
155
|
-
]
|
|
156
|
-
parent.add_parents(core_parents)
|
|
212
|
+
parent.add_output_concepts([concept] + same_filter_optional)
|
|
213
|
+
parent.add_parents(core_parent_nodes)
|
|
157
214
|
parent.add_condition(where.conditional)
|
|
158
|
-
parent.add_existence_concepts(flattened_existence, False)
|
|
159
|
-
expected_output, False
|
|
160
|
-
)
|
|
215
|
+
parent.add_existence_concepts(flattened_existence, False)
|
|
161
216
|
parent.grain = BuildGrain.from_concepts(
|
|
162
|
-
|
|
163
|
-
[environment.concepts[k] for k in immediate_parent.keys]
|
|
164
|
-
if immediate_parent.keys
|
|
165
|
-
else [immediate_parent]
|
|
166
|
-
)
|
|
167
|
-
+ [
|
|
168
|
-
x
|
|
169
|
-
for x in local_optional
|
|
170
|
-
if x.address in [y.address for y in parent.output_concepts]
|
|
171
|
-
],
|
|
217
|
+
parent.output_concepts,
|
|
172
218
|
environment=environment,
|
|
173
219
|
)
|
|
174
220
|
parent.rebuild_cache()
|
|
175
221
|
filter_node = parent
|
|
176
222
|
else:
|
|
177
|
-
|
|
178
|
-
|
|
223
|
+
core_parent_nodes.append(row_parent)
|
|
224
|
+
filters = [concept] + same_filter_optional
|
|
225
|
+
parents_for_grain = [
|
|
226
|
+
x.lineage.content
|
|
227
|
+
for x in filters
|
|
228
|
+
if isinstance(x.lineage.content, BuildConcept)
|
|
229
|
+
]
|
|
179
230
|
filter_node = FilterNode(
|
|
180
231
|
input_concepts=unique(
|
|
181
|
-
|
|
232
|
+
parent_row_concepts + flattened_existence,
|
|
182
233
|
"address",
|
|
183
234
|
),
|
|
184
|
-
output_concepts=[concept
|
|
235
|
+
output_concepts=[concept] + same_filter_optional + parent_row_concepts,
|
|
185
236
|
environment=environment,
|
|
186
|
-
parents=
|
|
237
|
+
parents=core_parent_nodes,
|
|
187
238
|
grain=BuildGrain.from_concepts(
|
|
188
|
-
|
|
239
|
+
parents_for_grain + parent_row_concepts, environment=environment
|
|
189
240
|
),
|
|
190
241
|
preexisting_conditions=conditions.conditional if conditions else None,
|
|
191
242
|
)
|
|
@@ -211,7 +262,8 @@ def gen_filter_node(
|
|
|
211
262
|
)
|
|
212
263
|
enrich_node: StrategyNode = source_concepts( # this fetches the parent + join keys
|
|
213
264
|
# to then connect to the rest of the query
|
|
214
|
-
mandatory_list=
|
|
265
|
+
mandatory_list=parent_row_concepts
|
|
266
|
+
+ [x for x in local_optional if x.address not in filter_node.output_concepts],
|
|
215
267
|
environment=environment,
|
|
216
268
|
g=g,
|
|
217
269
|
depth=depth + 1,
|
|
@@ -227,14 +279,13 @@ def gen_filter_node(
|
|
|
227
279
|
f"{padding(depth)}{LOGGER_PREFIX} returning filter node and enrich node with {enrich_node.output_concepts} and {enrich_node.input_concepts}"
|
|
228
280
|
)
|
|
229
281
|
return MergeNode(
|
|
230
|
-
input_concepts=
|
|
282
|
+
input_concepts=filter_node.output_concepts + enrich_node.output_concepts,
|
|
231
283
|
output_concepts=[
|
|
232
284
|
concept,
|
|
233
285
|
]
|
|
234
286
|
+ local_optional,
|
|
235
287
|
environment=environment,
|
|
236
288
|
parents=[
|
|
237
|
-
# this node fetches only what we need to filter
|
|
238
289
|
filter_node,
|
|
239
290
|
enrich_node,
|
|
240
291
|
],
|
{pytrilogy-0.0.3.48 → pytrilogy-0.0.3.51}/trilogy/core/processing/node_generators/group_node.py
RENAMED
|
@@ -51,6 +51,7 @@ def gen_group_node(
|
|
|
51
51
|
):
|
|
52
52
|
grain_components = [environment.concepts[c] for c in concept.grain.components]
|
|
53
53
|
parent_concepts += grain_components
|
|
54
|
+
build_grain_parents = BuildGrain.from_concepts(parent_concepts)
|
|
54
55
|
output_concepts += grain_components
|
|
55
56
|
for possible_agg in local_optional:
|
|
56
57
|
|
|
@@ -76,9 +77,7 @@ def gen_group_node(
|
|
|
76
77
|
logger.info(
|
|
77
78
|
f"{padding(depth)}{LOGGER_PREFIX} found equivalent group by optional concept {possible_agg.address} for {concept.address}"
|
|
78
79
|
)
|
|
79
|
-
elif BuildGrain.from_concepts(agg_parents) ==
|
|
80
|
-
parent_concepts
|
|
81
|
-
):
|
|
80
|
+
elif BuildGrain.from_concepts(agg_parents) == build_grain_parents:
|
|
82
81
|
extra = [x for x in agg_parents if x.address not in parent_concepts]
|
|
83
82
|
parent_concepts += extra
|
|
84
83
|
output_concepts.append(possible_agg)
|
|
@@ -87,7 +86,7 @@ def gen_group_node(
|
|
|
87
86
|
)
|
|
88
87
|
else:
|
|
89
88
|
logger.info(
|
|
90
|
-
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)}"
|
|
89
|
+
f"{padding(depth)}{LOGGER_PREFIX} cannot include optional agg {possible_agg.address}; mismatched parent grain {BuildGrain.from_concepts(agg_parents)} vs local parent {BuildGrain.from_concepts(parent_concepts)}"
|
|
91
90
|
)
|
|
92
91
|
if parent_concepts:
|
|
93
92
|
logger.info(
|
|
@@ -194,15 +194,18 @@ class StrategyNode:
|
|
|
194
194
|
if not self.parents:
|
|
195
195
|
return
|
|
196
196
|
non_hidden = set()
|
|
197
|
+
hidden = set()
|
|
197
198
|
for x in self.parents:
|
|
198
199
|
for z in x.usable_outputs:
|
|
199
200
|
non_hidden.add(z.address)
|
|
200
201
|
for psd in z.pseudonyms:
|
|
201
202
|
non_hidden.add(psd)
|
|
203
|
+
for z in x.hidden_concepts:
|
|
204
|
+
hidden.add(z)
|
|
202
205
|
if not all([x.address in non_hidden for x in self.input_concepts]):
|
|
203
206
|
missing = [x for x in self.input_concepts if x.address not in non_hidden]
|
|
204
207
|
raise ValueError(
|
|
205
|
-
f"Invalid input concepts; {missing} are missing non-hidden parent nodes"
|
|
208
|
+
f"Invalid input concepts; {missing} are missing non-hidden parent nodes; have {non_hidden} and hidden {hidden}"
|
|
206
209
|
)
|
|
207
210
|
|
|
208
211
|
def add_parents(self, parents: list["StrategyNode"]):
|
|
@@ -162,9 +162,7 @@ class SelectStatement(HasUUID, SelectTypeMixin, BaseModel):
|
|
|
162
162
|
output.local_concepts[x.content.address] = environment.concepts[
|
|
163
163
|
x.content.address
|
|
164
164
|
]
|
|
165
|
-
|
|
166
165
|
output.grain = output.calculate_grain(environment, output.local_concepts)
|
|
167
|
-
|
|
168
166
|
output.validate_syntax(environment)
|
|
169
167
|
return output
|
|
170
168
|
|