pytrilogy 0.0.3.97__tar.gz → 0.0.3.99__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.97/pytrilogy.egg-info → pytrilogy-0.0.3.99}/PKG-INFO +1 -1
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99/pytrilogy.egg-info}/PKG-INFO +1 -1
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/pytrilogy.egg-info/SOURCES.txt +1 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/__init__.py +1 -1
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/functions.py +1 -1
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/models/execute.py +0 -1
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/node_merge_node.py +144 -13
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/validation/concept.py +9 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/validation/environment.py +1 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/dialect/common.py +2 -10
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/dialect/metadata.py +1 -1
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/executor.py +16 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/parsing/parse_engine.py +4 -2
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/parsing/render.py +2 -2
- pytrilogy-0.0.3.99/trilogy/std/metric.preql +15 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/LICENSE.md +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/README.md +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/pyproject.toml +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/pytrilogy.egg-info/dependency_links.txt +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/pytrilogy.egg-info/entry_points.txt +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/pytrilogy.egg-info/requires.txt +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/pytrilogy.egg-info/top_level.txt +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/setup.cfg +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/setup.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_datatypes.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_declarations.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_derived_concepts.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_discovery_nodes.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_enums.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_environment.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_execute_models.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_executor.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_failure.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_functions.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_imports.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_metadata.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_models.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_multi_join_assignments.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_parse_engine.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_parsing.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_parsing_failures.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_partial_handling.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_query_processing.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_query_render.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_select.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_show.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_statements.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_typing.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_undefined_concept.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_user_functions.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/tests/test_where_clause.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/authoring/__init__.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/constants.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/__init__.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/constants.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/enums.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/env_processor.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/environment_helpers.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/ergonomics.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/exceptions.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/graph_models.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/internal.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/models/__init__.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/models/author.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/models/build.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/models/build_environment.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/models/core.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/models/datasource.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/models/environment.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/optimization.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/optimizations/__init__.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/optimizations/base_optimization.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/optimizations/inline_datasource.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/__init__.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/concept_strategies_v3.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/discovery_loop.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/discovery_node_factory.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/discovery_utility.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/discovery_validation.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/graph_utils.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/__init__.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/basic_node.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/common.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/constant_node.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/filter_node.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/group_node.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/recursive_node.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/select_merge_node.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/select_node.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/union_node.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/window_node.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/nodes/__init__.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/nodes/base_node.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/nodes/filter_node.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/nodes/group_node.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/nodes/merge_node.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/nodes/recursive_node.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/nodes/union_node.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/nodes/unnest_node.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/nodes/window_node.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/utility.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/query_processor.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/statements/__init__.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/statements/author.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/statements/build.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/statements/common.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/statements/execute.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/utility.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/validation/__init__.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/validation/common.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/validation/datasource.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/validation/fix.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/dialect/__init__.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/dialect/base.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/dialect/bigquery.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/dialect/config.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/dialect/dataframe.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/dialect/duckdb.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/dialect/enums.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/dialect/postgres.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/dialect/presto.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/dialect/snowflake.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/dialect/sql_server.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/engine.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/hooks/__init__.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/hooks/base_hook.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/hooks/graph_hook.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/hooks/query_debugger.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/metadata/__init__.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/parser.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/parsing/__init__.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/parsing/common.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/parsing/config.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/parsing/exceptions.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/parsing/helpers.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/parsing/trilogy.lark +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/py.typed +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/render.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/scripts/__init__.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/scripts/trilogy.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/std/__init__.py +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/std/date.preql +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/std/display.preql +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/std/geography.preql +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/std/money.preql +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/std/net.preql +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/std/ranking.preql +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/std/report.preql +0 -0
- {pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/utility.py +0 -0
|
@@ -427,7 +427,7 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
427
427
|
{DataType.STRING},
|
|
428
428
|
],
|
|
429
429
|
output_purpose=Purpose.PROPERTY,
|
|
430
|
-
output_type=DataType.
|
|
430
|
+
output_type=DataType.STRING,
|
|
431
431
|
arg_count=2,
|
|
432
432
|
),
|
|
433
433
|
FunctionType.SUBSTRING: FunctionConfig(
|
{pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/node_merge_node.py
RENAMED
|
@@ -1,17 +1,24 @@
|
|
|
1
|
-
from
|
|
1
|
+
from itertools import combinations
|
|
2
|
+
from typing import Callable, List, Optional
|
|
2
3
|
|
|
3
4
|
import networkx as nx
|
|
4
5
|
from networkx.algorithms import approximation as ax
|
|
5
6
|
|
|
6
7
|
from trilogy.constants import logger
|
|
7
|
-
from trilogy.core.enums import Derivation
|
|
8
|
+
from trilogy.core.enums import Derivation, FunctionType
|
|
8
9
|
from trilogy.core.exceptions import AmbiguousRelationshipResolutionException
|
|
9
10
|
from trilogy.core.graph_models import (
|
|
10
11
|
ReferenceGraph,
|
|
11
12
|
concept_to_node,
|
|
12
13
|
prune_sources_for_conditions,
|
|
13
14
|
)
|
|
14
|
-
from trilogy.core.models.build import
|
|
15
|
+
from trilogy.core.models.build import (
|
|
16
|
+
BuildConcept,
|
|
17
|
+
BuildConditional,
|
|
18
|
+
BuildFunction,
|
|
19
|
+
BuildGrain,
|
|
20
|
+
BuildWhereClause,
|
|
21
|
+
)
|
|
15
22
|
from trilogy.core.models.build_environment import BuildEnvironment
|
|
16
23
|
from trilogy.core.processing.nodes import History, MergeNode, StrategyNode
|
|
17
24
|
from trilogy.core.processing.utility import padding
|
|
@@ -29,7 +36,10 @@ def filter_pseudonyms_for_source(
|
|
|
29
36
|
if edge in pseudonyms:
|
|
30
37
|
lengths = {}
|
|
31
38
|
for n in edge:
|
|
32
|
-
|
|
39
|
+
try:
|
|
40
|
+
lengths[n] = nx.shortest_path_length(ds_graph, node, n)
|
|
41
|
+
except nx.NetworkXNoPath:
|
|
42
|
+
lengths[n] = 999
|
|
33
43
|
to_remove.add(max(lengths, key=lambda x: lengths.get(x, 0)))
|
|
34
44
|
for node in to_remove:
|
|
35
45
|
ds_graph.remove_node(node)
|
|
@@ -84,14 +94,129 @@ def extract_ds_components(
|
|
|
84
94
|
return graphs
|
|
85
95
|
|
|
86
96
|
|
|
97
|
+
def prune_and_merge(
|
|
98
|
+
G: nx.DiGraph,
|
|
99
|
+
keep_node_lambda: Callable[[str], bool],
|
|
100
|
+
) -> nx.DiGraph:
|
|
101
|
+
"""
|
|
102
|
+
Prune nodes of one type and create direct connections between remaining nodes.
|
|
103
|
+
|
|
104
|
+
Args:
|
|
105
|
+
G: NetworkX graph
|
|
106
|
+
keep_node_type: The node type to keep
|
|
107
|
+
node_type_attr: Attribute name that stores node type
|
|
108
|
+
|
|
109
|
+
Returns:
|
|
110
|
+
New graph with only keep_node_type nodes and merged connections
|
|
111
|
+
"""
|
|
112
|
+
# Get nodes to keep
|
|
113
|
+
nodes_to_keep = [n for n in G.nodes if keep_node_lambda(n)]
|
|
114
|
+
# Create new graph
|
|
115
|
+
new_graph = G.subgraph(nodes_to_keep).copy()
|
|
116
|
+
|
|
117
|
+
# Find paths between nodes to keep through removed nodes
|
|
118
|
+
nodes_to_remove = [n for n in G.nodes() if n not in nodes_to_keep]
|
|
119
|
+
|
|
120
|
+
for node_pair in combinations(nodes_to_keep, 2):
|
|
121
|
+
n1, n2 = node_pair
|
|
122
|
+
|
|
123
|
+
# Check if there's a path through removed nodes
|
|
124
|
+
try:
|
|
125
|
+
path = nx.shortest_path(G, n1, n2)
|
|
126
|
+
# If path exists and goes through nodes we're removing
|
|
127
|
+
if len(path) > 2 or any(node in nodes_to_remove for node in path[1:-1]):
|
|
128
|
+
new_graph.add_edge(n1, n2)
|
|
129
|
+
except nx.NetworkXNoPath:
|
|
130
|
+
continue
|
|
131
|
+
|
|
132
|
+
return new_graph
|
|
133
|
+
|
|
134
|
+
|
|
135
|
+
def reinject_common_join_keys_v2(
|
|
136
|
+
G: ReferenceGraph,
|
|
137
|
+
final: nx.DiGraph,
|
|
138
|
+
nodelist: list[str],
|
|
139
|
+
synonyms: set[str] = set(),
|
|
140
|
+
) -> bool:
|
|
141
|
+
# when we've discovered a concept join, for each pair of ds nodes
|
|
142
|
+
# check if they have more keys in common
|
|
143
|
+
# and inject those to discovery as join conditions
|
|
144
|
+
def is_ds_node(n: str) -> bool:
|
|
145
|
+
return n.startswith("ds~")
|
|
146
|
+
|
|
147
|
+
ds_graph = prune_and_merge(final, is_ds_node)
|
|
148
|
+
injected = False
|
|
149
|
+
|
|
150
|
+
for datasource in ds_graph.nodes:
|
|
151
|
+
node1 = G.datasources[datasource]
|
|
152
|
+
neighbors = nx.all_neighbors(ds_graph, datasource)
|
|
153
|
+
for neighbor in neighbors:
|
|
154
|
+
node2 = G.datasources[neighbor]
|
|
155
|
+
common_concepts = set(
|
|
156
|
+
x.concept.address for x in node1.columns
|
|
157
|
+
).intersection(set(x.concept.address for x in node2.columns))
|
|
158
|
+
concrete_concepts = [
|
|
159
|
+
x.concept for x in node1.columns if x.concept.address in common_concepts
|
|
160
|
+
]
|
|
161
|
+
reduced = BuildGrain.from_concepts(concrete_concepts).components
|
|
162
|
+
existing_addresses = set()
|
|
163
|
+
for concrete in concrete_concepts:
|
|
164
|
+
logger.debug(
|
|
165
|
+
f"looking at column {concrete.address} with pseudonyms {concrete.pseudonyms}"
|
|
166
|
+
)
|
|
167
|
+
cnode = concept_to_node(concrete.with_default_grain())
|
|
168
|
+
if cnode in final.nodes:
|
|
169
|
+
existing_addresses.add(concrete.address)
|
|
170
|
+
continue
|
|
171
|
+
for concrete in concrete_concepts:
|
|
172
|
+
if concrete.address in synonyms:
|
|
173
|
+
continue
|
|
174
|
+
if concrete.address not in reduced:
|
|
175
|
+
continue
|
|
176
|
+
if concrete.address in existing_addresses:
|
|
177
|
+
continue
|
|
178
|
+
# skip anything that is already in the graph pseudonyms
|
|
179
|
+
if any(x in concrete.pseudonyms for x in existing_addresses):
|
|
180
|
+
continue
|
|
181
|
+
cnode = concept_to_node(concrete.with_default_grain())
|
|
182
|
+
final.add_edge(datasource, cnode)
|
|
183
|
+
final.add_edge(neighbor, cnode)
|
|
184
|
+
logger.debug(
|
|
185
|
+
f"{LOGGER_PREFIX} reinjecting common join key {cnode} to list {nodelist} between {datasource} and {neighbor}, existing {existing_addresses}"
|
|
186
|
+
)
|
|
187
|
+
injected = True
|
|
188
|
+
return injected
|
|
189
|
+
|
|
190
|
+
|
|
87
191
|
def determine_induced_minimal_nodes(
|
|
88
192
|
G: ReferenceGraph,
|
|
89
193
|
nodelist: list[str],
|
|
90
194
|
environment: BuildEnvironment,
|
|
91
195
|
filter_downstream: bool,
|
|
92
196
|
accept_partial: bool = False,
|
|
197
|
+
synonyms: set[str] = set(),
|
|
93
198
|
) -> nx.DiGraph | None:
|
|
94
199
|
H: nx.Graph = nx.to_undirected(G).copy()
|
|
200
|
+
|
|
201
|
+
# Add weights to edges based on target node's derivation type
|
|
202
|
+
for edge in G.edges():
|
|
203
|
+
_, target = edge
|
|
204
|
+
target_lookup = G.concepts.get(target)
|
|
205
|
+
|
|
206
|
+
weight = 1 # default weight
|
|
207
|
+
# If either node is BASIC, set higher weight
|
|
208
|
+
if target_lookup and target_lookup.derivation == Derivation.BASIC:
|
|
209
|
+
if (
|
|
210
|
+
isinstance(target_lookup.lineage, BuildFunction)
|
|
211
|
+
and target_lookup.lineage.operator == FunctionType.ATTR_ACCESS
|
|
212
|
+
):
|
|
213
|
+
weight = 1
|
|
214
|
+
else:
|
|
215
|
+
# raise SyntaxError(target_lookup.lineage.operator)
|
|
216
|
+
weight = 50
|
|
217
|
+
|
|
218
|
+
H.edges[edge]["weight"] = weight
|
|
219
|
+
|
|
95
220
|
nodes_to_remove = []
|
|
96
221
|
for node, lookup in G.concepts.items():
|
|
97
222
|
# inclusion of aggregates can create ambiguous node relation chains
|
|
@@ -116,27 +241,29 @@ def determine_induced_minimal_nodes(
|
|
|
116
241
|
|
|
117
242
|
zero_out = list(x for x in H.nodes if G.out_degree(x) == 0 and x not in nodelist)
|
|
118
243
|
while zero_out:
|
|
119
|
-
|
|
244
|
+
logger.debug(f"Removing zero out nodes {zero_out} from graph")
|
|
120
245
|
H.remove_nodes_from(zero_out)
|
|
121
246
|
zero_out = list(
|
|
122
247
|
x for x in H.nodes if G.out_degree(x) == 0 and x not in nodelist
|
|
123
248
|
)
|
|
124
249
|
try:
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
250
|
+
# Use weight attribute for Dijkstra pathfinding
|
|
251
|
+
paths = nx.multi_source_dijkstra_path(H, nodelist, weight="weight")
|
|
252
|
+
# logger.debug(f"Paths found for {nodelist} {paths}")
|
|
253
|
+
except nx.exception.NodeNotFound as e:
|
|
254
|
+
logger.debug(f"Unable to find paths for {nodelist}- {str(e)}")
|
|
129
255
|
return None
|
|
130
256
|
path_removals = list(x for x in H.nodes if x not in paths)
|
|
131
257
|
if path_removals:
|
|
132
|
-
logger.debug(f"Removing paths {path_removals} from graph")
|
|
258
|
+
# logger.debug(f"Removing paths {path_removals} from graph")
|
|
133
259
|
H.remove_nodes_from(path_removals)
|
|
134
260
|
# logger.debug(f"Graph after path removal {H.nodes}")
|
|
135
|
-
sG: nx.Graph = ax.steinertree.steiner_tree(H, nodelist).copy()
|
|
261
|
+
sG: nx.Graph = ax.steinertree.steiner_tree(H, nodelist, weight="weight").copy()
|
|
136
262
|
if not sG.nodes:
|
|
137
263
|
logger.debug(f"No Steiner tree found for nodes {nodelist}")
|
|
138
264
|
return None
|
|
139
|
-
|
|
265
|
+
|
|
266
|
+
logger.debug(f"Steiner tree found for nodes {nodelist} {sG.nodes}")
|
|
140
267
|
final: nx.DiGraph = nx.subgraph(G, sG.nodes).copy()
|
|
141
268
|
|
|
142
269
|
for edge in G.edges:
|
|
@@ -148,8 +275,10 @@ def determine_induced_minimal_nodes(
|
|
|
148
275
|
if not accept_partial:
|
|
149
276
|
continue
|
|
150
277
|
final.add_edge(*edge)
|
|
151
|
-
# all concept nodes must have a parent
|
|
152
278
|
|
|
279
|
+
reinject_common_join_keys_v2(G, final, nodelist, synonyms)
|
|
280
|
+
|
|
281
|
+
# all concept nodes must have a parent
|
|
153
282
|
if not all(
|
|
154
283
|
[
|
|
155
284
|
final.in_degree(node) > 0
|
|
@@ -170,6 +299,7 @@ def determine_induced_minimal_nodes(
|
|
|
170
299
|
logger.debug(
|
|
171
300
|
f"Skipping graph for initial list {nodelist} as missing nodes {missing} from final graph {final.nodes}"
|
|
172
301
|
)
|
|
302
|
+
|
|
173
303
|
return None
|
|
174
304
|
logger.debug(f"Found final graph {final.nodes}")
|
|
175
305
|
return final
|
|
@@ -302,6 +432,7 @@ def resolve_weak_components(
|
|
|
302
432
|
filter_downstream=filter_downstream,
|
|
303
433
|
accept_partial=accept_partial,
|
|
304
434
|
environment=environment,
|
|
435
|
+
synonyms=synonyms,
|
|
305
436
|
)
|
|
306
437
|
|
|
307
438
|
if not g or not g.nodes:
|
|
@@ -26,6 +26,15 @@ def validate_key_concept(
|
|
|
26
26
|
):
|
|
27
27
|
results: list[ValidationTest] = []
|
|
28
28
|
seen: dict[str, int] = {}
|
|
29
|
+
|
|
30
|
+
count = 0
|
|
31
|
+
for datasource in build_env.datasources.values():
|
|
32
|
+
if concept.address in [c.address for c in datasource.concepts]:
|
|
33
|
+
count += 1
|
|
34
|
+
# if it only has one source, it's a key
|
|
35
|
+
if count <= 1:
|
|
36
|
+
return results
|
|
37
|
+
|
|
29
38
|
for datasource in build_env.datasources.values():
|
|
30
39
|
if concept.address in [c.address for c in datasource.concepts]:
|
|
31
40
|
assignment = [
|
|
@@ -10,7 +10,6 @@ from trilogy.core.models.build import (
|
|
|
10
10
|
BuildParamaterizedConceptReference,
|
|
11
11
|
BuildParenthetical,
|
|
12
12
|
)
|
|
13
|
-
from trilogy.core.models.datasource import RawColumnExpr
|
|
14
13
|
from trilogy.core.models.execute import (
|
|
15
14
|
CTE,
|
|
16
15
|
InstantiatedUnnestJoin,
|
|
@@ -65,15 +64,8 @@ def render_join_concept(
|
|
|
65
64
|
inlined_ctes: set[str],
|
|
66
65
|
):
|
|
67
66
|
if cte.name in inlined_ctes:
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
if isinstance(raw_content, RawColumnExpr):
|
|
71
|
-
rval = raw_content.text
|
|
72
|
-
return rval
|
|
73
|
-
elif isinstance(raw_content, BuildFunction):
|
|
74
|
-
rval = render_expr(raw_content, cte=cte)
|
|
75
|
-
return rval
|
|
76
|
-
return f"{quote_character}{name}{quote_character}.{quote_character}{raw_content}{quote_character}"
|
|
67
|
+
base = render_expr(concept, cte)
|
|
68
|
+
return base
|
|
77
69
|
return f"{quote_character}{name}{quote_character}.{quote_character}{concept.safe_address}{quote_character}"
|
|
78
70
|
|
|
79
71
|
|
|
@@ -174,7 +174,7 @@ def raw_validation_to_result(
|
|
|
174
174
|
) -> Optional[MockResult]:
|
|
175
175
|
"""Convert raw validation tests to mock result."""
|
|
176
176
|
if not raw:
|
|
177
|
-
return
|
|
177
|
+
return MockResult([], ["check_type", "expected", "result", "ran", "query"])
|
|
178
178
|
output = []
|
|
179
179
|
for row in raw:
|
|
180
180
|
if row.raw_query and generator and not row.generated_query:
|
|
@@ -204,6 +204,15 @@ class Executor(object):
|
|
|
204
204
|
output.append(compiled_sql)
|
|
205
205
|
return output
|
|
206
206
|
|
|
207
|
+
@generate_sql.register
|
|
208
|
+
def _(self, command: ProcessedShowStatement) -> List[str]:
|
|
209
|
+
output = []
|
|
210
|
+
for statement in command.output_values:
|
|
211
|
+
if isinstance(statement, (ProcessedQuery, ProcessedQueryPersist)):
|
|
212
|
+
compiled_sql = self.generator.compile_statement(statement)
|
|
213
|
+
output.append(compiled_sql)
|
|
214
|
+
return output
|
|
215
|
+
|
|
207
216
|
@generate_sql.register # type: ignore
|
|
208
217
|
def _(self, command: MultiSelectStatement) -> List[str]:
|
|
209
218
|
output = []
|
|
@@ -410,6 +419,13 @@ class Executor(object):
|
|
|
410
419
|
)
|
|
411
420
|
output.extend(results)
|
|
412
421
|
continue
|
|
422
|
+
elif isinstance(statement, ProcessedValidateStatement):
|
|
423
|
+
validate_result = handle_processed_validate_statement(
|
|
424
|
+
statement, self.generator, self.validate_environment
|
|
425
|
+
)
|
|
426
|
+
if validate_result:
|
|
427
|
+
output.append(validate_result)
|
|
428
|
+
continue
|
|
413
429
|
if non_interactive:
|
|
414
430
|
if not isinstance(
|
|
415
431
|
statement, (ProcessedCopyStatement, ProcessedQueryPersist)
|
|
@@ -1002,7 +1002,7 @@ class ParseToObjects(Transformer):
|
|
|
1002
1002
|
def validate_statement(self, meta: Meta, args) -> ValidateStatement:
|
|
1003
1003
|
if len(args) == 2:
|
|
1004
1004
|
scope = args[0]
|
|
1005
|
-
targets = args[1]
|
|
1005
|
+
targets = args[1].split(",")
|
|
1006
1006
|
elif len(args) == 0:
|
|
1007
1007
|
scope = ValidationScope.ALL
|
|
1008
1008
|
targets = None
|
|
@@ -2295,7 +2295,9 @@ def parse_text(
|
|
|
2295
2295
|
raise _create_syntax_error(210, pos, text)
|
|
2296
2296
|
|
|
2297
2297
|
# Handle FROM token error
|
|
2298
|
-
parsed_tokens =
|
|
2298
|
+
parsed_tokens = (
|
|
2299
|
+
[x.value for x in e.token_history if x] if e.token_history else []
|
|
2300
|
+
)
|
|
2299
2301
|
if parsed_tokens == ["FROM"]:
|
|
2300
2302
|
raise _create_syntax_error(101, pos, text)
|
|
2301
2303
|
|
|
@@ -546,8 +546,8 @@ class Renderer:
|
|
|
546
546
|
return f"{self.to_string(arg.arguments[0])}[{self.to_string(arg.arguments[1])}]"
|
|
547
547
|
|
|
548
548
|
if arg.operator == FunctionType.CASE:
|
|
549
|
-
inputs = "\n".join(args)
|
|
550
|
-
return f"CASE
|
|
549
|
+
inputs = "\n\t".join(args)
|
|
550
|
+
return f"CASE\n\t{inputs}\nEND"
|
|
551
551
|
return f"{arg.operator.value}({inputs})"
|
|
552
552
|
|
|
553
553
|
@to_string.register
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Length and distance units
|
|
2
|
+
type m numeric; # meters
|
|
3
|
+
type km numeric; # kilometers
|
|
4
|
+
type cm numeric; # centimeters
|
|
5
|
+
type mm numeric; # millimeters
|
|
6
|
+
|
|
7
|
+
# Mass units
|
|
8
|
+
type kg numeric; # kilograms
|
|
9
|
+
type g numeric; # grams
|
|
10
|
+
type tonne numeric; # metric tons (1000 kg)
|
|
11
|
+
|
|
12
|
+
# Force units
|
|
13
|
+
type n numeric; # newtons
|
|
14
|
+
type kn numeric; # kilonewtons (1000 N)
|
|
15
|
+
type mn numeric; # meganewtons (1,000,000 N)
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/__init__.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/basic_node.py
RENAMED
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/constant_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/filter_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/group_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/group_to_node.py
RENAMED
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/recursive_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/rowset_node.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
{pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/select_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/synonym_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/union_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/unnest_node.py
RENAMED
|
File without changes
|
{pytrilogy-0.0.3.97 → pytrilogy-0.0.3.99}/trilogy/core/processing/node_generators/window_node.py
RENAMED
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|