pytrilogy 0.0.3.93__py3-none-any.whl → 0.0.3.94__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of pytrilogy might be problematic. Click here for more details.
- {pytrilogy-0.0.3.93.dist-info → pytrilogy-0.0.3.94.dist-info}/METADATA +1 -1
- {pytrilogy-0.0.3.93.dist-info → pytrilogy-0.0.3.94.dist-info}/RECORD +15 -16
- trilogy/__init__.py +1 -1
- trilogy/core/env_processor.py +4 -2
- trilogy/core/graph_models.py +63 -44
- trilogy/core/models/author.py +16 -25
- trilogy/core/models/build.py +5 -4
- trilogy/core/processing/node_generators/node_merge_node.py +30 -28
- trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +25 -11
- trilogy/core/processing/node_generators/select_merge_node.py +66 -80
- trilogy/parsing/parse_engine.py +1 -1
- trilogy/core/processing/node_generators/select_merge_node_v2.py +0 -792
- {pytrilogy-0.0.3.93.dist-info → pytrilogy-0.0.3.94.dist-info}/WHEEL +0 -0
- {pytrilogy-0.0.3.93.dist-info → pytrilogy-0.0.3.94.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.3.93.dist-info → pytrilogy-0.0.3.94.dist-info}/licenses/LICENSE.md +0 -0
- {pytrilogy-0.0.3.93.dist-info → pytrilogy-0.0.3.94.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
pytrilogy-0.0.3.
|
|
2
|
-
trilogy/__init__.py,sha256=
|
|
1
|
+
pytrilogy-0.0.3.94.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
|
|
2
|
+
trilogy/__init__.py,sha256=GEadNJkWn6lKPBKuEwL1P2slgqo7djyKbAwsmVQZeLI,303
|
|
3
3
|
trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
4
4
|
trilogy/constants.py,sha256=eKb_EJvSqjN9tGbdVEViwdtwwh8fZ3-jpOEDqL71y70,1691
|
|
5
5
|
trilogy/engine.py,sha256=OK2RuqCIUId6yZ5hfF8J1nxGP0AJqHRZiafcowmW0xc,1728
|
|
@@ -12,19 +12,19 @@ trilogy/authoring/__init__.py,sha256=e74k-Jep4DLYPQU_2m0aVsYlw5HKTOucAKtdTbd6f2g
|
|
|
12
12
|
trilogy/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
13
|
trilogy/core/constants.py,sha256=nizWYDCJQ1bigQMtkNIEMNTcN0NoEAXiIHLzpelxQ24,201
|
|
14
14
|
trilogy/core/enums.py,sha256=RQRkpGHLtcBKAO6jZnmGVtSUnb00Q2rP56ltYGdfTok,8294
|
|
15
|
-
trilogy/core/env_processor.py,sha256=
|
|
15
|
+
trilogy/core/env_processor.py,sha256=_DCnD_Qt-kxp5Ta7Apje2TLlgfc7sOpkRYXXQtQNsrw,4154
|
|
16
16
|
trilogy/core/environment_helpers.py,sha256=VvPIiFemqaLLpIpLIqprfu63K7muZ1YzNg7UZIUph8w,8267
|
|
17
17
|
trilogy/core/ergonomics.py,sha256=e-7gE29vPLFdg0_A1smQ7eOrUwKl5VYdxRSTddHweRA,1631
|
|
18
18
|
trilogy/core/exceptions.py,sha256=jYEduuMehcMkmCpf-OC_taELPZm7qNfeSNzIWkDYScs,707
|
|
19
19
|
trilogy/core/functions.py,sha256=hnfcNjAD-XQ572vEwuUEAdBf8zKFWYwPeHIpESjUpZs,32928
|
|
20
|
-
trilogy/core/graph_models.py,sha256=
|
|
20
|
+
trilogy/core/graph_models.py,sha256=S-0M4_OrU6W4AE2uf1LeS4RU-6CEUv59qvQ32lhav1Y,4479
|
|
21
21
|
trilogy/core/internal.py,sha256=wFx4e1I0mtx159YFShSXeUBSQ82NINtAbOI-92RX4i8,2151
|
|
22
22
|
trilogy/core/optimization.py,sha256=ojpn-p79lr03SSVQbbw74iPCyoYpDYBmj1dbZ3oXCjI,8860
|
|
23
23
|
trilogy/core/query_processor.py,sha256=5aFgv-2LVM1Uku9cR_tFuTRDwyLnxc95bCMAHeFy2AY,20332
|
|
24
24
|
trilogy/core/utility.py,sha256=3VC13uSQWcZNghgt7Ot0ZTeEmNqs__cx122abVq9qhM,410
|
|
25
25
|
trilogy/core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
26
|
-
trilogy/core/models/author.py,sha256=
|
|
27
|
-
trilogy/core/models/build.py,sha256=
|
|
26
|
+
trilogy/core/models/author.py,sha256=ZSKEJ6Vg4otpI_m7_JuGyrFM8dZV1HaxBwprvDSwUzo,81149
|
|
27
|
+
trilogy/core/models/build.py,sha256=ZwJJGyp4rVsISvL8Er_AxQdVJrafYc4fesSj4MNgoxU,70615
|
|
28
28
|
trilogy/core/models/build_environment.py,sha256=mpx7MKGc60fnZLVdeLi2YSREy7eQbQYycCrP4zF-rHU,5258
|
|
29
29
|
trilogy/core/models/core.py,sha256=nnz3ZROlVT18uygEWqqbfbHmcJkm2UC3VVCrsri_-K0,12836
|
|
30
30
|
trilogy/core/models/datasource.py,sha256=wogTevZ-9CyUW2a8gjzqMCieircxi-J5lkI7EOAZnck,9596
|
|
@@ -50,18 +50,17 @@ trilogy/core/processing/node_generators/filter_node.py,sha256=ArBsQJl-4fWBJWCE28
|
|
|
50
50
|
trilogy/core/processing/node_generators/group_node.py,sha256=8HJ1lkOvIXfX3xoS2IMbM_wCu_mT0J_hQ7xnTaxsVlo,6611
|
|
51
51
|
trilogy/core/processing/node_generators/group_to_node.py,sha256=jKcNCDOY6fNblrdZwaRU0sbUSr9H0moQbAxrGgX6iGA,3832
|
|
52
52
|
trilogy/core/processing/node_generators/multiselect_node.py,sha256=GWV5yLmKTe1yyPhN60RG1Rnrn4ktfn9lYYXi_FVU4UI,7061
|
|
53
|
-
trilogy/core/processing/node_generators/node_merge_node.py,sha256=
|
|
53
|
+
trilogy/core/processing/node_generators/node_merge_node.py,sha256=83FkcYuOFyDY0_0NWhL45MAT5J_6Y6L1h357WrJPzaI,18230
|
|
54
54
|
trilogy/core/processing/node_generators/recursive_node.py,sha256=l5zdh0dURKwmAy8kK4OpMtZfyUEQRk6N-PwSWIyBpSM,2468
|
|
55
55
|
trilogy/core/processing/node_generators/rowset_node.py,sha256=5L5u6xz1In8EaHQdcYgR2si-tz9WB9YLXURo4AkUT9A,6630
|
|
56
|
-
trilogy/core/processing/node_generators/select_merge_node.py,sha256=
|
|
57
|
-
trilogy/core/processing/node_generators/select_merge_node_v2.py,sha256=1HQedwstFbk3xc7B09ElJ3mMoIKixtkcCqjDIBfsxck,25707
|
|
56
|
+
trilogy/core/processing/node_generators/select_merge_node.py,sha256=CiuBzWX4KyO3G5_FKQmYzCH0R1qifvfj9LHJs9o5_Ro,22259
|
|
58
57
|
trilogy/core/processing/node_generators/select_node.py,sha256=Ta1G39V94gjX_AgyZDz9OqnwLz4BjY3D6Drx9YpziMQ,3555
|
|
59
58
|
trilogy/core/processing/node_generators/synonym_node.py,sha256=AnAsa_Wj50NJ_IK0HSgab_7klYmKVrv0WI1uUe-GvEY,3766
|
|
60
59
|
trilogy/core/processing/node_generators/union_node.py,sha256=VNo6Oey4p8etU9xrOh2oTT2lIOTvY6PULUPRvVa2uxU,2877
|
|
61
60
|
trilogy/core/processing/node_generators/unnest_node.py,sha256=ueOQtoTf2iJHO09RzWHDFQ5iKZq2fVhGf2KAF2U2kU8,2677
|
|
62
61
|
trilogy/core/processing/node_generators/window_node.py,sha256=A90linr4pkZtTNfn9k2YNLqrJ_SFII3lbHxB-BC6mI8,6688
|
|
63
62
|
trilogy/core/processing/node_generators/select_helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
64
|
-
trilogy/core/processing/node_generators/select_helpers/datasource_injection.py,sha256=
|
|
63
|
+
trilogy/core/processing/node_generators/select_helpers/datasource_injection.py,sha256=m2YQ4OmG0N2O61a7NEq1ZzbTa7JsCC00lxB2ymjcYRI,8224
|
|
65
64
|
trilogy/core/processing/nodes/__init__.py,sha256=zTge1EzwzEydlcMliIFO_TT7h7lS8l37lyZuQDir1h0,5487
|
|
66
65
|
trilogy/core/processing/nodes/base_node.py,sha256=C_CjlOzlGMXckyV0b_PJZerpopNesRCKfambMq7Asvc,18221
|
|
67
66
|
trilogy/core/processing/nodes/filter_node.py,sha256=5VtRfKbCORx0dV-vQfgy3gOEkmmscL9f31ExvlODwvY,2461
|
|
@@ -99,7 +98,7 @@ trilogy/parsing/common.py,sha256=550-L0444GUuBFdiDWkOg_DxnMXtcJFUMES2R5zlwik,310
|
|
|
99
98
|
trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
|
|
100
99
|
trilogy/parsing/exceptions.py,sha256=Xwwsv2C9kSNv2q-HrrKC1f60JNHShXcCMzstTSEbiCw,154
|
|
101
100
|
trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
102
|
-
trilogy/parsing/parse_engine.py,sha256=
|
|
101
|
+
trilogy/parsing/parse_engine.py,sha256=FrmEXEBs54z1RIQuHWBonuqsqjMynzuP03NP9GdFN-g,80655
|
|
103
102
|
trilogy/parsing/render.py,sha256=HSNntD82GiiwHT-TWPLXAaIMWLYVV5B5zQEsgwrHIBE,19605
|
|
104
103
|
trilogy/parsing/trilogy.lark,sha256=e2YVSxqzRov08AydtDSA8aqSJU2M1eJaidMEkHCdsYE,15896
|
|
105
104
|
trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -112,8 +111,8 @@ trilogy/std/money.preql,sha256=XWwvAV3WxBsHX9zfptoYRnBigcfYwrYtBHXTME0xJuQ,2082
|
|
|
112
111
|
trilogy/std/net.preql,sha256=WZCuvH87_rZntZiuGJMmBDMVKkdhTtxeHOkrXNwJ1EE,416
|
|
113
112
|
trilogy/std/ranking.preql,sha256=LDoZrYyz4g3xsII9XwXfmstZD-_92i1Eox1UqkBIfi8,83
|
|
114
113
|
trilogy/std/report.preql,sha256=LbV-XlHdfw0jgnQ8pV7acG95xrd1-p65fVpiIc-S7W4,202
|
|
115
|
-
pytrilogy-0.0.3.
|
|
116
|
-
pytrilogy-0.0.3.
|
|
117
|
-
pytrilogy-0.0.3.
|
|
118
|
-
pytrilogy-0.0.3.
|
|
119
|
-
pytrilogy-0.0.3.
|
|
114
|
+
pytrilogy-0.0.3.94.dist-info/METADATA,sha256=DN1uvVPThwfS9W6P1Tgo8igcIUuVIXtN26iDO_sllSw,9746
|
|
115
|
+
pytrilogy-0.0.3.94.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
116
|
+
pytrilogy-0.0.3.94.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
|
|
117
|
+
pytrilogy-0.0.3.94.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
|
|
118
|
+
pytrilogy-0.0.3.94.dist-info/RECORD,,
|
trilogy/__init__.py
CHANGED
trilogy/core/env_processor.py
CHANGED
|
@@ -44,8 +44,10 @@ def add_concept(
|
|
|
44
44
|
continue
|
|
45
45
|
if pseudonym_node.split("@")[0] == node_name.split("@")[0]:
|
|
46
46
|
continue
|
|
47
|
-
g.add_edge(pseudonym_node, node_name
|
|
48
|
-
g.add_edge(node_name, pseudonym_node
|
|
47
|
+
g.add_edge(pseudonym_node, node_name)
|
|
48
|
+
g.add_edge(node_name, pseudonym_node)
|
|
49
|
+
g.pseudonyms.add((pseudonym_node, node_name))
|
|
50
|
+
g.pseudonyms.add((node_name, pseudonym_node))
|
|
49
51
|
add_concept(pseudonym, g, concept_mapping, default_concept_graph, seen)
|
|
50
52
|
|
|
51
53
|
|
trilogy/core/graph_models.py
CHANGED
|
@@ -1,50 +1,48 @@
|
|
|
1
|
+
from typing import Union
|
|
2
|
+
|
|
1
3
|
import networkx as nx
|
|
2
4
|
|
|
3
5
|
from trilogy.core.models.build import BuildConcept, BuildDatasource, BuildWhereClause
|
|
4
6
|
|
|
5
7
|
|
|
6
8
|
def get_graph_exact_match(
|
|
7
|
-
g: nx.DiGraph,
|
|
9
|
+
g: Union[nx.DiGraph, "ReferenceGraph"],
|
|
10
|
+
accept_partial: bool,
|
|
11
|
+
conditions: BuildWhereClause | None,
|
|
8
12
|
) -> set[str]:
|
|
9
|
-
datasources: dict[str, BuildDatasource | list[BuildDatasource]] = (
|
|
10
|
-
nx.get_node_attributes(g, "datasource")
|
|
11
|
-
)
|
|
12
13
|
exact: set[str] = set()
|
|
13
|
-
for node in g.
|
|
14
|
-
if
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
14
|
+
for node, ds in g.datasources.items():
|
|
15
|
+
if isinstance(ds, list):
|
|
16
|
+
exact.add(node)
|
|
17
|
+
continue
|
|
18
|
+
|
|
19
|
+
if not conditions and not ds.non_partial_for:
|
|
20
|
+
exact.add(node)
|
|
21
|
+
continue
|
|
22
|
+
elif not conditions and accept_partial and ds.non_partial_for:
|
|
23
|
+
exact.add(node)
|
|
24
|
+
continue
|
|
25
|
+
elif conditions:
|
|
26
|
+
if not ds.non_partial_for:
|
|
18
27
|
continue
|
|
19
|
-
|
|
20
|
-
if not conditions and not ds.non_partial_for:
|
|
21
|
-
exact.add(node)
|
|
22
|
-
continue
|
|
23
|
-
elif not conditions and accept_partial and ds.non_partial_for:
|
|
28
|
+
if ds.non_partial_for and conditions == ds.non_partial_for:
|
|
24
29
|
exact.add(node)
|
|
25
30
|
continue
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
continue
|
|
29
|
-
if ds.non_partial_for and conditions == ds.non_partial_for:
|
|
30
|
-
exact.add(node)
|
|
31
|
-
continue
|
|
32
|
-
else:
|
|
33
|
-
continue
|
|
31
|
+
else:
|
|
32
|
+
continue
|
|
34
33
|
|
|
35
34
|
return exact
|
|
36
35
|
|
|
37
36
|
|
|
38
37
|
def prune_sources_for_conditions(
|
|
39
|
-
g:
|
|
38
|
+
g: "ReferenceGraph",
|
|
40
39
|
accept_partial: bool,
|
|
41
40
|
conditions: BuildWhereClause | None,
|
|
42
41
|
):
|
|
43
|
-
|
|
44
42
|
complete = get_graph_exact_match(g, accept_partial, conditions)
|
|
45
43
|
to_remove = []
|
|
46
|
-
for node in g.
|
|
47
|
-
if node
|
|
44
|
+
for node in g.datasources:
|
|
45
|
+
if node not in complete:
|
|
48
46
|
to_remove.append(node)
|
|
49
47
|
|
|
50
48
|
for node in to_remove:
|
|
@@ -68,37 +66,58 @@ def datasource_to_node(input: BuildDatasource) -> str:
|
|
|
68
66
|
class ReferenceGraph(nx.DiGraph):
|
|
69
67
|
def __init__(self, *args, **kwargs):
|
|
70
68
|
super().__init__(*args, **kwargs)
|
|
71
|
-
|
|
72
|
-
|
|
69
|
+
self.concepts: dict[str, BuildConcept] = {}
|
|
70
|
+
self.datasources: dict[str, BuildDatasource] = {}
|
|
71
|
+
self.pseudonyms: set[tuple[str, str]] = set()
|
|
72
|
+
|
|
73
|
+
def copy(self):
|
|
74
|
+
g = ReferenceGraph()
|
|
75
|
+
g.concepts = self.concepts.copy()
|
|
76
|
+
g.datasources = self.datasources.copy()
|
|
77
|
+
g.pseudonyms = {*self.pseudonyms}
|
|
78
|
+
# g.add_nodes_from(self.nodes(data=True))
|
|
79
|
+
for node in self.nodes:
|
|
80
|
+
g.add_node(node, fast=True)
|
|
81
|
+
for edge in self.edges:
|
|
82
|
+
g.add_edge(edge[0], edge[1], fast=True)
|
|
83
|
+
# g.add_edges_from(self.edges(data=True))
|
|
84
|
+
return g
|
|
85
|
+
|
|
86
|
+
def remove_node(self, n):
|
|
87
|
+
if n in self.concepts:
|
|
88
|
+
del self.concepts[n]
|
|
89
|
+
if n in self.datasources:
|
|
90
|
+
del self.datasources[n]
|
|
91
|
+
super().remove_node(n)
|
|
92
|
+
|
|
93
|
+
def add_node(self, node_for_adding, fast: bool = False, **attr):
|
|
94
|
+
if fast:
|
|
95
|
+
return super().add_node(node_for_adding, **attr)
|
|
73
96
|
if isinstance(node_for_adding, BuildConcept):
|
|
74
97
|
node_name = concept_to_node(node_for_adding)
|
|
75
|
-
|
|
76
|
-
# return
|
|
77
|
-
attr["type"] = "concept"
|
|
78
|
-
attr["concept"] = node_for_adding
|
|
79
|
-
attr["grain"] = node_for_adding.grain
|
|
98
|
+
self.concepts[node_name] = node_for_adding
|
|
80
99
|
elif isinstance(node_for_adding, BuildDatasource):
|
|
81
100
|
node_name = datasource_to_node(node_for_adding)
|
|
82
|
-
|
|
83
|
-
# return
|
|
84
|
-
attr["type"] = "datasource"
|
|
85
|
-
attr["ds"] = node_for_adding
|
|
86
|
-
attr["grain"] = node_for_adding.grain
|
|
101
|
+
self.datasources[node_name] = node_for_adding
|
|
87
102
|
else:
|
|
88
103
|
node_name = node_for_adding
|
|
104
|
+
if attr.get("datasource"):
|
|
105
|
+
self.datasources[node_name] = attr["datasource"]
|
|
89
106
|
super().add_node(node_name, **attr)
|
|
90
107
|
|
|
91
|
-
def add_edge(self, u_of_edge, v_of_edge, **attr):
|
|
108
|
+
def add_edge(self, u_of_edge, v_of_edge, fast: bool = False, **attr):
|
|
109
|
+
if fast:
|
|
110
|
+
return super().add_edge(u_of_edge, v_of_edge, **attr)
|
|
92
111
|
if isinstance(u_of_edge, BuildConcept):
|
|
93
112
|
orig = u_of_edge
|
|
94
113
|
u_of_edge = concept_to_node(u_of_edge)
|
|
95
114
|
if u_of_edge not in self.nodes:
|
|
96
115
|
self.add_node(orig)
|
|
97
116
|
elif isinstance(u_of_edge, BuildDatasource):
|
|
98
|
-
|
|
117
|
+
origd = u_of_edge
|
|
99
118
|
u_of_edge = datasource_to_node(u_of_edge)
|
|
100
119
|
if u_of_edge not in self.nodes:
|
|
101
|
-
self.add_node(
|
|
120
|
+
self.add_node(origd)
|
|
102
121
|
|
|
103
122
|
if isinstance(v_of_edge, BuildConcept):
|
|
104
123
|
orig = v_of_edge
|
|
@@ -106,8 +125,8 @@ class ReferenceGraph(nx.DiGraph):
|
|
|
106
125
|
if v_of_edge not in self.nodes:
|
|
107
126
|
self.add_node(orig)
|
|
108
127
|
elif isinstance(v_of_edge, BuildDatasource):
|
|
109
|
-
|
|
128
|
+
origd = v_of_edge
|
|
110
129
|
v_of_edge = datasource_to_node(v_of_edge)
|
|
111
130
|
if v_of_edge not in self.nodes:
|
|
112
|
-
self.add_node(
|
|
113
|
-
super().add_edge(u_of_edge, v_of_edge
|
|
131
|
+
self.add_node(origd)
|
|
132
|
+
super().add_edge(u_of_edge, v_of_edge)
|
trilogy/core/models/author.py
CHANGED
|
@@ -902,7 +902,11 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
|
|
|
902
902
|
|
|
903
903
|
@property
|
|
904
904
|
def is_aggregate(self):
|
|
905
|
-
|
|
905
|
+
base = getattr(self, "_is_aggregate", None)
|
|
906
|
+
if base:
|
|
907
|
+
return base
|
|
908
|
+
setattr(self, "_is_aggregate", self.calculate_is_aggregate(self.lineage))
|
|
909
|
+
return self._is_aggregate
|
|
906
910
|
|
|
907
911
|
def with_merge(self, source: Self, target: Self, modifiers: List[Modifier]) -> Self:
|
|
908
912
|
if self.address == source.address:
|
|
@@ -1069,18 +1073,25 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
|
|
|
1069
1073
|
final_grain = grain if not self.grain.components else self.grain
|
|
1070
1074
|
keys = self.keys
|
|
1071
1075
|
|
|
1072
|
-
if self.is_aggregate and isinstance(new_lineage, Function)
|
|
1076
|
+
if self.is_aggregate and grain.components and isinstance(new_lineage, Function):
|
|
1073
1077
|
grain_components: list[ConceptRef | Concept] = [
|
|
1074
1078
|
environment.concepts[c].reference for c in grain.components
|
|
1075
1079
|
]
|
|
1076
|
-
new_lineage = AggregateWrapper(
|
|
1080
|
+
new_lineage = AggregateWrapper.model_construct(
|
|
1081
|
+
function=new_lineage, by=grain_components
|
|
1082
|
+
)
|
|
1077
1083
|
final_grain = grain
|
|
1078
1084
|
keys = set(grain.components)
|
|
1079
|
-
elif
|
|
1085
|
+
elif (
|
|
1086
|
+
grain
|
|
1087
|
+
and new_lineage
|
|
1088
|
+
and isinstance(new_lineage, AggregateWrapper)
|
|
1089
|
+
and not new_lineage.by
|
|
1090
|
+
):
|
|
1080
1091
|
grain_components = [
|
|
1081
1092
|
environment.concepts[c].reference for c in grain.components
|
|
1082
1093
|
]
|
|
1083
|
-
new_lineage = AggregateWrapper(
|
|
1094
|
+
new_lineage = AggregateWrapper.model_construct(
|
|
1084
1095
|
function=new_lineage.function, by=grain_components
|
|
1085
1096
|
)
|
|
1086
1097
|
final_grain = grain
|
|
@@ -1670,15 +1681,6 @@ class Function(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
|
1670
1681
|
def datatype(self):
|
|
1671
1682
|
return self.output_datatype
|
|
1672
1683
|
|
|
1673
|
-
@field_validator("output_datatype")
|
|
1674
|
-
@classmethod
|
|
1675
|
-
def parse_output_datatype(cls, v, info: ValidationInfo):
|
|
1676
|
-
values = info.data
|
|
1677
|
-
if values.get("operator") == FunctionType.ATTR_ACCESS:
|
|
1678
|
-
if isinstance(v, StructType):
|
|
1679
|
-
raise SyntaxError
|
|
1680
|
-
return v
|
|
1681
|
-
|
|
1682
1684
|
@field_validator("arguments", mode="before")
|
|
1683
1685
|
@classmethod
|
|
1684
1686
|
def parse_arguments(cls, v, info: ValidationInfo):
|
|
@@ -1845,17 +1847,6 @@ class Function(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
|
|
|
1845
1847
|
base += get_concept_arguments(arg)
|
|
1846
1848
|
return base
|
|
1847
1849
|
|
|
1848
|
-
@property
|
|
1849
|
-
def output_grain(self):
|
|
1850
|
-
# aggregates have an abstract grain
|
|
1851
|
-
base_grain = Grain(components=[])
|
|
1852
|
-
if self.operator in FunctionClass.AGGREGATE_FUNCTIONS.value:
|
|
1853
|
-
return base_grain
|
|
1854
|
-
# scalars have implicit grain of all arguments
|
|
1855
|
-
for input in self.concept_arguments:
|
|
1856
|
-
base_grain += input.grain
|
|
1857
|
-
return base_grain
|
|
1858
|
-
|
|
1859
1850
|
|
|
1860
1851
|
class FunctionCallWrapper(
|
|
1861
1852
|
DataTyped,
|
trilogy/core/models/build.py
CHANGED
|
@@ -1533,6 +1533,7 @@ class Factory:
|
|
|
1533
1533
|
)
|
|
1534
1534
|
self.local_non_build_concepts: dict[str, Concept] = {}
|
|
1535
1535
|
self.pseudonym_map = pseudonym_map or get_canonical_pseudonyms(environment)
|
|
1536
|
+
self.build_grain = self.build(self.grain) if self.grain else None
|
|
1536
1537
|
|
|
1537
1538
|
def instantiate_concept(
|
|
1538
1539
|
self,
|
|
@@ -1792,11 +1793,11 @@ class Factory:
|
|
|
1792
1793
|
address = base.concept.address
|
|
1793
1794
|
fetched = (
|
|
1794
1795
|
self._build_concept(
|
|
1795
|
-
self.environment.alias_origin_lookup[address]
|
|
1796
|
-
)
|
|
1796
|
+
self.environment.alias_origin_lookup[address]
|
|
1797
|
+
).with_grain(self.build_grain)
|
|
1797
1798
|
if address in self.environment.alias_origin_lookup
|
|
1798
|
-
else self._build_concept(
|
|
1799
|
-
self.
|
|
1799
|
+
else self._build_concept(self.environment.concepts[address]).with_grain(
|
|
1800
|
+
self.build_grain
|
|
1800
1801
|
)
|
|
1801
1802
|
)
|
|
1802
1803
|
|
|
@@ -6,7 +6,11 @@ from networkx.algorithms import approximation as ax
|
|
|
6
6
|
from trilogy.constants import logger
|
|
7
7
|
from trilogy.core.enums import Derivation
|
|
8
8
|
from trilogy.core.exceptions import AmbiguousRelationshipResolutionException
|
|
9
|
-
from trilogy.core.graph_models import
|
|
9
|
+
from trilogy.core.graph_models import (
|
|
10
|
+
ReferenceGraph,
|
|
11
|
+
concept_to_node,
|
|
12
|
+
prune_sources_for_conditions,
|
|
13
|
+
)
|
|
10
14
|
from trilogy.core.models.build import BuildConcept, BuildConditional, BuildWhereClause
|
|
11
15
|
from trilogy.core.models.build_environment import BuildEnvironment
|
|
12
16
|
from trilogy.core.processing.nodes import History, MergeNode, StrategyNode
|
|
@@ -17,11 +21,12 @@ LOGGER_PREFIX = "[GEN_MERGE_NODE]"
|
|
|
17
21
|
AMBIGUITY_CHECK_LIMIT = 20
|
|
18
22
|
|
|
19
23
|
|
|
20
|
-
def filter_pseudonyms_for_source(
|
|
24
|
+
def filter_pseudonyms_for_source(
|
|
25
|
+
ds_graph: nx.DiGraph, node: str, pseudonyms: set[tuple[str, str]]
|
|
26
|
+
):
|
|
21
27
|
to_remove = set()
|
|
22
|
-
|
|
23
28
|
for edge in ds_graph.edges:
|
|
24
|
-
if
|
|
29
|
+
if edge in pseudonyms:
|
|
25
30
|
lengths = {}
|
|
26
31
|
for n in edge:
|
|
27
32
|
lengths[n] = nx.shortest_path_length(ds_graph, node, n)
|
|
@@ -52,12 +57,14 @@ def filter_unique_graphs(graphs: list[list[str]]) -> list[list[str]]:
|
|
|
52
57
|
return [list(x) for x in unique_graphs]
|
|
53
58
|
|
|
54
59
|
|
|
55
|
-
def extract_ds_components(
|
|
60
|
+
def extract_ds_components(
|
|
61
|
+
g: nx.DiGraph, nodelist: list[str], pseudonyms: set[tuple[str, str]]
|
|
62
|
+
) -> list[list[str]]:
|
|
56
63
|
graphs = []
|
|
57
64
|
for node in g.nodes:
|
|
58
65
|
if node.startswith("ds~"):
|
|
59
66
|
local = g.copy()
|
|
60
|
-
filter_pseudonyms_for_source(local, node)
|
|
67
|
+
filter_pseudonyms_for_source(local, node, pseudonyms)
|
|
61
68
|
ds_graph: nx.DiGraph = nx.ego_graph(local, node, radius=10).copy()
|
|
62
69
|
graphs.append(
|
|
63
70
|
[
|
|
@@ -78,7 +85,7 @@ def extract_ds_components(g: nx.DiGraph, nodelist: list[str]) -> list[list[str]]
|
|
|
78
85
|
|
|
79
86
|
|
|
80
87
|
def determine_induced_minimal_nodes(
|
|
81
|
-
G:
|
|
88
|
+
G: ReferenceGraph,
|
|
82
89
|
nodelist: list[str],
|
|
83
90
|
environment: BuildEnvironment,
|
|
84
91
|
filter_downstream: bool,
|
|
@@ -86,23 +93,19 @@ def determine_induced_minimal_nodes(
|
|
|
86
93
|
) -> nx.DiGraph | None:
|
|
87
94
|
H: nx.Graph = nx.to_undirected(G).copy()
|
|
88
95
|
nodes_to_remove = []
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
if
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
)
|
|
102
|
-
nodes_to_remove.append(node)
|
|
103
|
-
# purge a node if we're already looking for all it's parents
|
|
104
|
-
if filter_downstream and lookup.derivation not in (Derivation.ROOT,):
|
|
105
|
-
nodes_to_remove.append(node)
|
|
96
|
+
for node, lookup in G.concepts.items():
|
|
97
|
+
# inclusion of aggregates can create ambiguous node relation chains
|
|
98
|
+
# there may be a better way to handle this
|
|
99
|
+
# can be revisited if we need to connect a derived synonym based on an aggregate
|
|
100
|
+
if lookup.derivation in (
|
|
101
|
+
Derivation.CONSTANT,
|
|
102
|
+
Derivation.AGGREGATE,
|
|
103
|
+
Derivation.FILTER,
|
|
104
|
+
):
|
|
105
|
+
nodes_to_remove.append(node)
|
|
106
|
+
# purge a node if we're already looking for all it's parents
|
|
107
|
+
if filter_downstream and lookup.derivation not in (Derivation.ROOT,):
|
|
108
|
+
nodes_to_remove.append(node)
|
|
106
109
|
if nodes_to_remove:
|
|
107
110
|
# logger.debug(f"Removing nodes {nodes_to_remove} from graph")
|
|
108
111
|
H.remove_nodes_from(nodes_to_remove)
|
|
@@ -259,7 +262,7 @@ def filter_duplicate_subgraphs(
|
|
|
259
262
|
def resolve_weak_components(
|
|
260
263
|
all_concepts: List[BuildConcept],
|
|
261
264
|
environment: BuildEnvironment,
|
|
262
|
-
environment_graph:
|
|
265
|
+
environment_graph: ReferenceGraph,
|
|
263
266
|
filter_downstream: bool = True,
|
|
264
267
|
accept_partial: bool = False,
|
|
265
268
|
search_conditions: BuildWhereClause | None = None,
|
|
@@ -316,8 +319,6 @@ def resolve_weak_components(
|
|
|
316
319
|
]
|
|
317
320
|
new = [x for x in all_graph_concepts if x.address not in all_concepts]
|
|
318
321
|
|
|
319
|
-
new_addresses = set([x.address for x in new if x.address not in synonyms])
|
|
320
|
-
|
|
321
322
|
if not new:
|
|
322
323
|
break_flag = True
|
|
323
324
|
# remove our new nodes for the next search path
|
|
@@ -329,6 +330,7 @@ def resolve_weak_components(
|
|
|
329
330
|
# from trilogy.hooks.graph_hook import GraphHook
|
|
330
331
|
# GraphHook().query_graph_built(g, highlight_nodes=[concept_to_node(c.with_default_grain()) for c in all_concepts if "__preql_internal" not in c.address])
|
|
331
332
|
found.append(g)
|
|
333
|
+
new_addresses = set([x.address for x in new if x.address not in synonyms])
|
|
332
334
|
reduced_concept_sets.append(new_addresses)
|
|
333
335
|
|
|
334
336
|
except nx.exception.NetworkXNoPath:
|
|
@@ -346,7 +348,7 @@ def resolve_weak_components(
|
|
|
346
348
|
subgraphs: list[list[BuildConcept]] = []
|
|
347
349
|
# components = nx.strongly_connected_components(g)
|
|
348
350
|
node_list = [x for x in g.nodes if x.startswith("c~")]
|
|
349
|
-
components = extract_ds_components(g, node_list)
|
|
351
|
+
components = extract_ds_components(g, node_list, environment_graph.pseudonyms)
|
|
350
352
|
logger.debug(f"Extracted components {components} from {node_list}")
|
|
351
353
|
for component in components:
|
|
352
354
|
# we need to take unique again as different addresses may map to the same concept
|
|
@@ -128,17 +128,32 @@ def simplify_conditions(
|
|
|
128
128
|
for condition in conditions:
|
|
129
129
|
if not isinstance(condition, BuildComparison):
|
|
130
130
|
return False
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
if
|
|
136
|
-
|
|
131
|
+
left_is_concept = False
|
|
132
|
+
left_is_reducable = False
|
|
133
|
+
right_is_concept = False
|
|
134
|
+
right_is_reducable = False
|
|
135
|
+
if isinstance(condition.left, BuildConcept):
|
|
136
|
+
left_is_concept = True
|
|
137
|
+
elif isinstance(condition.left, REDUCABLE_TYPES):
|
|
138
|
+
left_is_reducable = True
|
|
139
|
+
|
|
140
|
+
if isinstance(condition.right, BuildConcept):
|
|
141
|
+
right_is_concept = True
|
|
142
|
+
elif isinstance(condition.right, REDUCABLE_TYPES):
|
|
143
|
+
right_is_reducable = True
|
|
144
|
+
|
|
145
|
+
if not (
|
|
146
|
+
(left_is_concept and right_is_reducable)
|
|
147
|
+
or (right_is_concept and left_is_reducable)
|
|
137
148
|
):
|
|
138
149
|
return False
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
150
|
+
if left_is_concept:
|
|
151
|
+
concept = condition.left
|
|
152
|
+
raw_comparison = condition.right
|
|
153
|
+
else:
|
|
154
|
+
concept = condition.right
|
|
155
|
+
raw_comparison = condition.left
|
|
156
|
+
|
|
142
157
|
if isinstance(raw_comparison, BuildFunction):
|
|
143
158
|
if not raw_comparison.operator == FunctionType.CONSTANT:
|
|
144
159
|
return False
|
|
@@ -154,7 +169,7 @@ def simplify_conditions(
|
|
|
154
169
|
if not isinstance(comparison, REDUCABLE_TYPES):
|
|
155
170
|
return False
|
|
156
171
|
|
|
157
|
-
var = concept
|
|
172
|
+
var: BuildConcept = concept # type: ignore
|
|
158
173
|
op = condition.operator
|
|
159
174
|
grouped[var].append((op, comparison))
|
|
160
175
|
|
|
@@ -240,7 +255,6 @@ def get_union_sources(
|
|
|
240
255
|
assocs[merge_key.address].append(x)
|
|
241
256
|
final: list[list[BuildDatasource]] = []
|
|
242
257
|
for _, dses in assocs.items():
|
|
243
|
-
|
|
244
258
|
conditions = [c.non_partial_for.conditional for c in dses if c.non_partial_for]
|
|
245
259
|
if simplify_conditions(conditions):
|
|
246
260
|
final.append(dses)
|