pytrilogy 0.0.2.23__py3-none-any.whl → 0.0.2.25__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.2.23.dist-info → pytrilogy-0.0.2.25.dist-info}/METADATA +1 -1
- {pytrilogy-0.0.2.23.dist-info → pytrilogy-0.0.2.25.dist-info}/RECORD +14 -14
- trilogy/__init__.py +1 -1
- trilogy/core/env_processor.py +12 -6
- trilogy/core/environment_helpers.py +0 -1
- trilogy/core/models.py +57 -19
- trilogy/core/processing/concept_strategies_v3.py +23 -4
- trilogy/core/processing/node_generators/node_merge_node.py +4 -4
- trilogy/core/processing/utility.py +9 -6
- trilogy/dialect/base.py +5 -1
- {pytrilogy-0.0.2.23.dist-info → pytrilogy-0.0.2.25.dist-info}/LICENSE.md +0 -0
- {pytrilogy-0.0.2.23.dist-info → pytrilogy-0.0.2.25.dist-info}/WHEEL +0 -0
- {pytrilogy-0.0.2.23.dist-info → pytrilogy-0.0.2.25.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.2.23.dist-info → pytrilogy-0.0.2.25.dist-info}/top_level.txt +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
trilogy/__init__.py,sha256=
|
|
1
|
+
trilogy/__init__.py,sha256=5XwNYAlRMOuSKIFUPwSVWUdNK1RpSEPCsc_H7W06R7w,291
|
|
2
2
|
trilogy/compiler.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
3
3
|
trilogy/constants.py,sha256=rHCe0Pe3LuB-VwCr2765QhzkUrTqZKEYPJ7rS0ykxYw,1273
|
|
4
4
|
trilogy/engine.py,sha256=R5ubIxYyrxRExz07aZCUfrTsoXCHQ8DKFTDsobXdWdA,1102
|
|
@@ -9,14 +9,14 @@ trilogy/utility.py,sha256=zM__8r29EsyDW7K9VOHz8yvZC2bXFzh7xKy3cL7GKsk,707
|
|
|
9
9
|
trilogy/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
10
10
|
trilogy/core/constants.py,sha256=7XaCpZn5mQmjTobbeBn56SzPWq9eMNDfzfsRU-fP0VE,171
|
|
11
11
|
trilogy/core/enums.py,sha256=y0Z0m-xtcVw1ktkQ5yD3fJYWfOa4ncN_MzCTpREAxy0,6374
|
|
12
|
-
trilogy/core/env_processor.py,sha256=
|
|
13
|
-
trilogy/core/environment_helpers.py,sha256=
|
|
12
|
+
trilogy/core/env_processor.py,sha256=SHVB3nkidIlFc5dz-sofRMKXx66stpLQNuVdQSjC-So,2586
|
|
13
|
+
trilogy/core/environment_helpers.py,sha256=DIsoo-GcXmXVPB1JbNh8Oku25Nyef9mexPIdy2ur_sk,7159
|
|
14
14
|
trilogy/core/ergonomics.py,sha256=ASLDd0RqKWrZiG3XcKHo8nyTjaB_8xfE9t4NZ1UvGpc,1639
|
|
15
15
|
trilogy/core/exceptions.py,sha256=NvV_4qLOgKXbpotgRf7c8BANDEvHxlqRPaA53IThQ2o,561
|
|
16
16
|
trilogy/core/functions.py,sha256=IhVpt3n6wEanKHnGu3oA2w6-hKIlxWpEyz7fHN66mpo,10720
|
|
17
17
|
trilogy/core/graph_models.py,sha256=oJUMSpmYhqXlavckHLpR07GJxuQ8dZ1VbB1fB0KaS8c,2036
|
|
18
18
|
trilogy/core/internal.py,sha256=jNGFHKENnbMiMCtAgsnLZYVSENDK4b5ALecXFZpTDzQ,1075
|
|
19
|
-
trilogy/core/models.py,sha256=
|
|
19
|
+
trilogy/core/models.py,sha256=LUaoxk4twHRY9_Qatdbo1GjjCZRo_91Hql7BwKjLbfM,156934
|
|
20
20
|
trilogy/core/optimization.py,sha256=od_60A9F8J8Nj24MHgrxl4vwRwmBFH13TMdoMQvgVKs,7717
|
|
21
21
|
trilogy/core/query_processor.py,sha256=sdG0XcHNBS0kuqUPztDZ1i-kpDV5LJLrO55Og2Y8hSg,17140
|
|
22
22
|
trilogy/core/optimizations/__init__.py,sha256=bWQecbeiwiDx9LJnLsa7dkWxdbl2wcnkcTN69JyP8iI,356
|
|
@@ -25,9 +25,9 @@ trilogy/core/optimizations/inline_constant.py,sha256=kHNyc2UoaPVdYfVAPAFwnWuk4sJ
|
|
|
25
25
|
trilogy/core/optimizations/inline_datasource.py,sha256=AATzQ6YrtW_1-aQFjQyTYqEYKBoMFhek7ADfBr4uUdQ,3634
|
|
26
26
|
trilogy/core/optimizations/predicate_pushdown.py,sha256=1l9WnFOSv79e341typG3tTdk0XGl1J_ToQih3LYoGIY,8435
|
|
27
27
|
trilogy/core/processing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
28
|
-
trilogy/core/processing/concept_strategies_v3.py,sha256=
|
|
28
|
+
trilogy/core/processing/concept_strategies_v3.py,sha256=7MT_x6QFHrbSDmjz21pYdQB5ux419ES4QS-8lO16eyw,36091
|
|
29
29
|
trilogy/core/processing/graph_utils.py,sha256=aq-kqk4Iado2HywDxWEejWc-7PGO6Oa-ZQLAM6XWPHw,1199
|
|
30
|
-
trilogy/core/processing/utility.py,sha256=
|
|
30
|
+
trilogy/core/processing/utility.py,sha256=hzuEsNqP5dq1GBT96lWYX1UkakMl6XItp-nIKFH6wLg,19617
|
|
31
31
|
trilogy/core/processing/node_generators/__init__.py,sha256=-mzYkRsaRNa_dfTckYkKVFSR8h8a3ihEiPJDU_tAmDo,672
|
|
32
32
|
trilogy/core/processing/node_generators/basic_node.py,sha256=WQNgJ1MwrMS_BQ-b3XwGGB6eToDykelAVj_fesJuqe0,2069
|
|
33
33
|
trilogy/core/processing/node_generators/common.py,sha256=LwDgPlhWeuw0t07f3kX9IE5LXBdZhXfh-aY0XGk50ak,8946
|
|
@@ -35,7 +35,7 @@ trilogy/core/processing/node_generators/filter_node.py,sha256=Vz9Rb67e1dfZgnliek
|
|
|
35
35
|
trilogy/core/processing/node_generators/group_node.py,sha256=r54IVEhXW-tzod6uEHIQObrxgQt6aNySk5emWkWyqCU,4938
|
|
36
36
|
trilogy/core/processing/node_generators/group_to_node.py,sha256=R9i_wHipxjXJyfYEwfeTw2EPpuanXVA327XyfcP2tBg,2537
|
|
37
37
|
trilogy/core/processing/node_generators/multiselect_node.py,sha256=_KO9lqzHQoy4VAviO0ttQlmK0tjaqrJj4SJPhmoIYm8,6229
|
|
38
|
-
trilogy/core/processing/node_generators/node_merge_node.py,sha256=
|
|
38
|
+
trilogy/core/processing/node_generators/node_merge_node.py,sha256=dIEv5P2MTViAES2MzqJgccYzM3HldjHrQYFwH00cqyc,14003
|
|
39
39
|
trilogy/core/processing/node_generators/rowset_node.py,sha256=KtdN6t2xM8CJxobc4aQX4W8uX98U6IabeuBF_FtBLR4,4583
|
|
40
40
|
trilogy/core/processing/node_generators/select_merge_node.py,sha256=MKjlXqFBSin6cTnS6n5lEcNBJsMvSefDIXOwYNVbM0s,10371
|
|
41
41
|
trilogy/core/processing/node_generators/select_node.py,sha256=nwXHQF6C-aQUIelx9dyxN2pK3muL-4-6RIqnqQqNwtw,1808
|
|
@@ -50,7 +50,7 @@ trilogy/core/processing/nodes/select_node_v2.py,sha256=gS9OQgS2TSEK59BQ9R0i83pTH
|
|
|
50
50
|
trilogy/core/processing/nodes/unnest_node.py,sha256=mAmFluzm2yeeiQ6NfIB7BU_8atRGh-UJfPf9ROwbhr8,2152
|
|
51
51
|
trilogy/core/processing/nodes/window_node.py,sha256=ro0QfMFi4ZmIn5Q4D0M_vJWfnHH_C0MN7XkVkx8Gygg,1214
|
|
52
52
|
trilogy/dialect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
53
|
-
trilogy/dialect/base.py,sha256=
|
|
53
|
+
trilogy/dialect/base.py,sha256=zqPBWv5gsnk4KhyzHdxcpDkVXU7luEVvHepUWRzjUfo,34019
|
|
54
54
|
trilogy/dialect/bigquery.py,sha256=15KJ-cOpBlk9O7FPviPgmg8xIydJeKx7WfmL3SSsPE8,2953
|
|
55
55
|
trilogy/dialect/common.py,sha256=Hr0mxcNxjSvhpBM5Wvb_Q7aklAuYj5aBDrW433py0Zs,4403
|
|
56
56
|
trilogy/dialect/config.py,sha256=tLVEMctaTDhUgARKXUNfHUcIolGaALkQ0RavUvXAY4w,2994
|
|
@@ -75,9 +75,9 @@ trilogy/parsing/render.py,sha256=7mEEe5DWVAafaGl__oQE7FPn_4QhcsGT2VVp-nk1Lr8,130
|
|
|
75
75
|
trilogy/parsing/trilogy.lark,sha256=ZP9USPgD8-Fq5UzIl4iGpAeGuh2JLGzSoYJhvEGOi2c,12188
|
|
76
76
|
trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
77
77
|
trilogy/scripts/trilogy.py,sha256=PHxvv6f2ODv0esyyhWxlARgra8dVhqQhYl0lTrSyVNo,3729
|
|
78
|
-
pytrilogy-0.0.2.
|
|
79
|
-
pytrilogy-0.0.2.
|
|
80
|
-
pytrilogy-0.0.2.
|
|
81
|
-
pytrilogy-0.0.2.
|
|
82
|
-
pytrilogy-0.0.2.
|
|
83
|
-
pytrilogy-0.0.2.
|
|
78
|
+
pytrilogy-0.0.2.25.dist-info/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
|
|
79
|
+
pytrilogy-0.0.2.25.dist-info/METADATA,sha256=GtN5gVcDusbMKaXTn5O_CLcZAk00h5Xe5df4i5QqZzs,8403
|
|
80
|
+
pytrilogy-0.0.2.25.dist-info/WHEEL,sha256=OVMc5UfuAQiSplgO0_WdW7vXVGAt9Hdd6qtN4HotdyA,91
|
|
81
|
+
pytrilogy-0.0.2.25.dist-info/entry_points.txt,sha256=0petKryjvvtEfTlbZC1AuMFumH_WQ9v8A19LvoS6G6c,54
|
|
82
|
+
pytrilogy-0.0.2.25.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
|
|
83
|
+
pytrilogy-0.0.2.25.dist-info/RECORD,,
|
trilogy/__init__.py
CHANGED
trilogy/core/env_processor.py
CHANGED
|
@@ -6,17 +6,20 @@ from trilogy.core.graph_models import (
|
|
|
6
6
|
from trilogy.core.models import Environment, Concept, Datasource
|
|
7
7
|
|
|
8
8
|
|
|
9
|
-
def add_concept(
|
|
9
|
+
def add_concept(
|
|
10
|
+
concept: Concept, g: ReferenceGraph, concept_mapping: dict[str, Concept]
|
|
11
|
+
):
|
|
10
12
|
g.add_node(concept)
|
|
11
13
|
# if we have sources, recursively add them
|
|
12
14
|
node_name = concept_to_node(concept)
|
|
13
15
|
if concept.concept_arguments:
|
|
14
16
|
for source in concept.concept_arguments:
|
|
15
17
|
generic = source.with_default_grain()
|
|
16
|
-
add_concept(generic, g)
|
|
18
|
+
add_concept(generic, g, concept_mapping)
|
|
17
19
|
|
|
18
20
|
g.add_edge(generic, node_name)
|
|
19
|
-
for
|
|
21
|
+
for ps_address in concept.pseudonyms:
|
|
22
|
+
pseudonym = concept_mapping[ps_address]
|
|
20
23
|
pseudonym = pseudonym.with_default_grain()
|
|
21
24
|
pseudonym_node = concept_to_node(pseudonym)
|
|
22
25
|
if (pseudonym_node, node_name) in g.edges and (
|
|
@@ -28,7 +31,7 @@ def add_concept(concept: Concept, g: ReferenceGraph):
|
|
|
28
31
|
continue
|
|
29
32
|
g.add_edge(pseudonym_node, node_name, pseudonym=True)
|
|
30
33
|
g.add_edge(node_name, pseudonym_node, pseudonym=True)
|
|
31
|
-
add_concept(pseudonym, g)
|
|
34
|
+
add_concept(pseudonym, g, concept_mapping)
|
|
32
35
|
|
|
33
36
|
|
|
34
37
|
def generate_adhoc_graph(
|
|
@@ -37,10 +40,11 @@ def generate_adhoc_graph(
|
|
|
37
40
|
restrict_to_listed: bool = False,
|
|
38
41
|
) -> ReferenceGraph:
|
|
39
42
|
g = ReferenceGraph()
|
|
43
|
+
concept_mapping = {x.address: x for x in concepts}
|
|
40
44
|
|
|
41
45
|
# add all parsed concepts
|
|
42
46
|
for concept in concepts:
|
|
43
|
-
add_concept(concept, g)
|
|
47
|
+
add_concept(concept, g, concept_mapping)
|
|
44
48
|
|
|
45
49
|
for dataset in datasources:
|
|
46
50
|
node = datasource_to_node(dataset)
|
|
@@ -66,5 +70,7 @@ def generate_graph(
|
|
|
66
70
|
) -> ReferenceGraph:
|
|
67
71
|
|
|
68
72
|
return generate_adhoc_graph(
|
|
69
|
-
list(environment.concepts.values())
|
|
73
|
+
list(environment.concepts.values())
|
|
74
|
+
+ list(environment.alias_origin_lookup.values()),
|
|
75
|
+
list(environment.datasources.values()),
|
|
70
76
|
)
|
trilogy/core/models.py
CHANGED
|
@@ -102,6 +102,12 @@ def get_version():
|
|
|
102
102
|
return __version__
|
|
103
103
|
|
|
104
104
|
|
|
105
|
+
def address_with_namespace(address: str, namespace: str) -> str:
|
|
106
|
+
if address.split(".", 1)[0] == DEFAULT_NAMESPACE:
|
|
107
|
+
return f"{namespace}.{address.split('.',1)[1]}"
|
|
108
|
+
return f"{namespace}.{address}"
|
|
109
|
+
|
|
110
|
+
|
|
105
111
|
def get_concept_arguments(expr) -> List["Concept"]:
|
|
106
112
|
output = []
|
|
107
113
|
if isinstance(expr, Concept):
|
|
@@ -436,7 +442,7 @@ class Concept(Mergeable, Namespaced, SelectContext, BaseModel):
|
|
|
436
442
|
keys: Optional[Tuple["Concept", ...]] = None
|
|
437
443
|
grain: "Grain" = Field(default=None, validate_default=True)
|
|
438
444
|
modifiers: Optional[List[Modifier]] = Field(default_factory=list)
|
|
439
|
-
pseudonyms:
|
|
445
|
+
pseudonyms: set[str] = Field(default_factory=set)
|
|
440
446
|
_address_cache: str | None = None
|
|
441
447
|
|
|
442
448
|
def __hash__(self):
|
|
@@ -462,7 +468,7 @@ class Concept(Mergeable, Namespaced, SelectContext, BaseModel):
|
|
|
462
468
|
def with_merge(self, source: Concept, target: Concept, modifiers: List[Modifier]):
|
|
463
469
|
if self.address == source.address:
|
|
464
470
|
new = target.with_grain(self.grain.with_merge(source, target, modifiers))
|
|
465
|
-
new.pseudonyms
|
|
471
|
+
new.pseudonyms.add(self.address)
|
|
466
472
|
return new
|
|
467
473
|
return self.__class__(
|
|
468
474
|
name=self.name,
|
|
@@ -616,9 +622,7 @@ class Concept(Mergeable, Namespaced, SelectContext, BaseModel):
|
|
|
616
622
|
else None
|
|
617
623
|
),
|
|
618
624
|
modifiers=self.modifiers,
|
|
619
|
-
pseudonyms={
|
|
620
|
-
k: v.with_namespace(namespace) for k, v in self.pseudonyms.items()
|
|
621
|
-
},
|
|
625
|
+
pseudonyms={address_with_namespace(v, namespace) for v in self.pseudonyms},
|
|
622
626
|
)
|
|
623
627
|
|
|
624
628
|
def with_select_context(
|
|
@@ -862,7 +866,7 @@ class Grain(Mergeable, BaseModel):
|
|
|
862
866
|
)
|
|
863
867
|
else:
|
|
864
868
|
v2 = unique(v, "address")
|
|
865
|
-
final = []
|
|
869
|
+
final: List[Concept] = []
|
|
866
870
|
for sub in v2:
|
|
867
871
|
if sub.purpose in (Purpose.PROPERTY, Purpose.METRIC) and sub.keys:
|
|
868
872
|
if all([c in v2 for c in sub.keys]):
|
|
@@ -916,6 +920,20 @@ class Grain(Mergeable, BaseModel):
|
|
|
916
920
|
[c.name == ALL_ROWS_CONCEPT for c in self.components]
|
|
917
921
|
)
|
|
918
922
|
|
|
923
|
+
@property
|
|
924
|
+
def synonym_set(self) -> set[str]:
|
|
925
|
+
base = []
|
|
926
|
+
for x in self.components_copy:
|
|
927
|
+
if isinstance(x.lineage, RowsetItem):
|
|
928
|
+
base.append(x.lineage.content.address)
|
|
929
|
+
for c in x.lineage.content.pseudonyms:
|
|
930
|
+
base.append(c)
|
|
931
|
+
else:
|
|
932
|
+
base.append(x.address)
|
|
933
|
+
for c in x.pseudonyms:
|
|
934
|
+
base.append(c)
|
|
935
|
+
return set(base)
|
|
936
|
+
|
|
919
937
|
@cached_property
|
|
920
938
|
def set(self) -> set[str]:
|
|
921
939
|
base = []
|
|
@@ -931,7 +949,11 @@ class Grain(Mergeable, BaseModel):
|
|
|
931
949
|
return self.set == set([c.address for c in other])
|
|
932
950
|
if not isinstance(other, Grain):
|
|
933
951
|
return False
|
|
934
|
-
|
|
952
|
+
if self.set == other.set:
|
|
953
|
+
return True
|
|
954
|
+
elif self.synonym_set == other.synonym_set:
|
|
955
|
+
return True
|
|
956
|
+
return False
|
|
935
957
|
|
|
936
958
|
def issubset(self, other: "Grain"):
|
|
937
959
|
return self.set.issubset(other.set)
|
|
@@ -1584,13 +1606,6 @@ class RawSQLStatement(BaseModel):
|
|
|
1584
1606
|
meta: Optional[Metadata] = Field(default_factory=lambda: Metadata())
|
|
1585
1607
|
|
|
1586
1608
|
|
|
1587
|
-
class CopyStatement(BaseModel):
|
|
1588
|
-
target: str
|
|
1589
|
-
target_type: IOType
|
|
1590
|
-
meta: Optional[Metadata] = Field(default_factory=lambda: Metadata())
|
|
1591
|
-
select: SelectStatement
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
1609
|
class SelectStatement(Mergeable, Namespaced, SelectTypeMixin, BaseModel):
|
|
1595
1610
|
selection: List[SelectItem]
|
|
1596
1611
|
order_by: Optional[OrderBy] = None
|
|
@@ -1789,6 +1804,16 @@ class SelectStatement(Mergeable, Namespaced, SelectTypeMixin, BaseModel):
|
|
|
1789
1804
|
)
|
|
1790
1805
|
|
|
1791
1806
|
|
|
1807
|
+
class CopyStatement(BaseModel):
|
|
1808
|
+
target: str
|
|
1809
|
+
target_type: IOType
|
|
1810
|
+
meta: Optional[Metadata] = Field(default_factory=lambda: Metadata())
|
|
1811
|
+
select: SelectStatement
|
|
1812
|
+
|
|
1813
|
+
def refresh_bindings(self, environment: Environment):
|
|
1814
|
+
self.select.refresh_bindings(environment)
|
|
1815
|
+
|
|
1816
|
+
|
|
1792
1817
|
class AlignItem(Namespaced, BaseModel):
|
|
1793
1818
|
alias: str
|
|
1794
1819
|
concepts: List[Concept]
|
|
@@ -2259,7 +2284,7 @@ class BaseJoin(BaseModel):
|
|
|
2259
2284
|
for ds in [self.left_datasource, self.right_datasource]:
|
|
2260
2285
|
synonyms = []
|
|
2261
2286
|
for c in ds.output_concepts:
|
|
2262
|
-
synonyms += list(c.pseudonyms
|
|
2287
|
+
synonyms += list(c.pseudonyms)
|
|
2263
2288
|
if (
|
|
2264
2289
|
concept.address not in [c.address for c in ds.output_concepts]
|
|
2265
2290
|
and concept.address not in synonyms
|
|
@@ -2834,9 +2859,21 @@ class CTE(BaseModel):
|
|
|
2834
2859
|
return self.parent_ctes[0].name
|
|
2835
2860
|
return self.name
|
|
2836
2861
|
|
|
2862
|
+
def get_concept(self, address: str) -> Concept | None:
|
|
2863
|
+
for cte in self.parent_ctes:
|
|
2864
|
+
if address in cte.output_columns:
|
|
2865
|
+
match = [x for x in cte.output_columns if x.address == address].pop()
|
|
2866
|
+
return match
|
|
2867
|
+
|
|
2868
|
+
for array in [self.source.input_concepts, self.source.output_concepts]:
|
|
2869
|
+
match_list = [x for x in array if x.address == address]
|
|
2870
|
+
if match_list:
|
|
2871
|
+
return match_list.pop()
|
|
2872
|
+
return None
|
|
2873
|
+
|
|
2837
2874
|
def get_alias(self, concept: Concept, source: str | None = None) -> str:
|
|
2838
2875
|
for cte in self.parent_ctes:
|
|
2839
|
-
if concept.address in
|
|
2876
|
+
if concept.address in cte.output_columns:
|
|
2840
2877
|
if source and source != cte.name:
|
|
2841
2878
|
continue
|
|
2842
2879
|
return concept.safe_address
|
|
@@ -2988,7 +3025,7 @@ class UndefinedConcept(Concept, Mergeable, Namespaced):
|
|
|
2988
3025
|
) -> "UndefinedConcept" | Concept:
|
|
2989
3026
|
if self.address == source.address:
|
|
2990
3027
|
new = target.with_grain(self.grain.with_merge(source, target, modifiers))
|
|
2991
|
-
new.pseudonyms
|
|
3028
|
+
new.pseudonyms.add(self.address)
|
|
2992
3029
|
return new
|
|
2993
3030
|
return self.__class__(
|
|
2994
3031
|
name=self.name,
|
|
@@ -3520,6 +3557,7 @@ class Environment(BaseModel):
|
|
|
3520
3557
|
self, source: Concept, target: Concept, modifiers: List[Modifier]
|
|
3521
3558
|
):
|
|
3522
3559
|
replacements = {}
|
|
3560
|
+
|
|
3523
3561
|
# exit early if we've run this
|
|
3524
3562
|
if source.address in self.alias_origin_lookup:
|
|
3525
3563
|
if self.concepts[source.address] == target:
|
|
@@ -3528,11 +3566,11 @@ class Environment(BaseModel):
|
|
|
3528
3566
|
for k, v in self.concepts.items():
|
|
3529
3567
|
|
|
3530
3568
|
if v.address == target.address:
|
|
3531
|
-
v.pseudonyms
|
|
3569
|
+
v.pseudonyms.add(source.address)
|
|
3532
3570
|
if v.address == source.address:
|
|
3533
3571
|
replacements[k] = target
|
|
3534
3572
|
self.canonical_map[k] = target.address
|
|
3535
|
-
v.pseudonyms
|
|
3573
|
+
v.pseudonyms.add(target.address)
|
|
3536
3574
|
# we need to update keys and grains of all concepts
|
|
3537
3575
|
else:
|
|
3538
3576
|
replacements[k] = v.with_merge(source, target, modifiers)
|
|
@@ -455,7 +455,20 @@ def generate_node(
|
|
|
455
455
|
if x.address not in [y.address for y in root_targets]
|
|
456
456
|
and x not in ex_resolve.grain.components
|
|
457
457
|
]
|
|
458
|
-
|
|
458
|
+
|
|
459
|
+
pseudonyms = [
|
|
460
|
+
x
|
|
461
|
+
for x in extra
|
|
462
|
+
if any(x.address in y.pseudonyms for y in root_targets)
|
|
463
|
+
]
|
|
464
|
+
# if we're only connected by a pseudonym, keep those in output
|
|
465
|
+
expanded.set_output_concepts(root_targets + pseudonyms)
|
|
466
|
+
# but hide them
|
|
467
|
+
if pseudonyms:
|
|
468
|
+
logger.info(
|
|
469
|
+
f"{depth_to_prefix(depth)}{LOGGER_PREFIX} Hiding pseudonyms{[c.address for c in pseudonyms]}"
|
|
470
|
+
)
|
|
471
|
+
expanded.hide_output_concepts(pseudonyms)
|
|
459
472
|
|
|
460
473
|
logger.info(
|
|
461
474
|
f"{depth_to_prefix(depth)}{LOGGER_PREFIX} Found connections for {[c.address for c in root_targets]} via concept addition; removing extra {[c.address for c in extra]}"
|
|
@@ -480,6 +493,7 @@ def validate_concept(
|
|
|
480
493
|
found_map: dict[str, set[Concept]],
|
|
481
494
|
accept_partial: bool,
|
|
482
495
|
seen: set[str],
|
|
496
|
+
environment: Environment,
|
|
483
497
|
):
|
|
484
498
|
|
|
485
499
|
found_map[str(node)].add(concept)
|
|
@@ -500,10 +514,11 @@ def validate_concept(
|
|
|
500
514
|
if accept_partial:
|
|
501
515
|
found_addresses.add(concept.address)
|
|
502
516
|
found_map[str(node)].add(concept)
|
|
503
|
-
for
|
|
504
|
-
|
|
517
|
+
for v_address in concept.pseudonyms:
|
|
518
|
+
v = environment.concepts[v_address]
|
|
519
|
+
if v == concept.address:
|
|
505
520
|
return
|
|
506
|
-
if v
|
|
521
|
+
if v in seen:
|
|
507
522
|
return
|
|
508
523
|
validate_concept(
|
|
509
524
|
v,
|
|
@@ -515,10 +530,12 @@ def validate_concept(
|
|
|
515
530
|
found_map,
|
|
516
531
|
accept_partial,
|
|
517
532
|
seen=seen,
|
|
533
|
+
environment=environment,
|
|
518
534
|
)
|
|
519
535
|
|
|
520
536
|
|
|
521
537
|
def validate_stack(
|
|
538
|
+
environment: Environment,
|
|
522
539
|
stack: List[StrategyNode],
|
|
523
540
|
concepts: List[Concept],
|
|
524
541
|
mandatory_with_filter: List[Concept],
|
|
@@ -546,6 +563,7 @@ def validate_stack(
|
|
|
546
563
|
found_map,
|
|
547
564
|
accept_partial,
|
|
548
565
|
seen,
|
|
566
|
+
environment,
|
|
549
567
|
)
|
|
550
568
|
for concept in node.virtual_output_concepts:
|
|
551
569
|
if concept.address in non_partial_addresses:
|
|
@@ -807,6 +825,7 @@ def _search_concepts(
|
|
|
807
825
|
break
|
|
808
826
|
attempted.add(priority_concept.address)
|
|
809
827
|
complete, found, missing, partial, virtual = validate_stack(
|
|
828
|
+
environment,
|
|
810
829
|
stack,
|
|
811
830
|
mandatory_list,
|
|
812
831
|
completion_mandatory,
|
|
@@ -209,9 +209,9 @@ def resolve_weak_components(
|
|
|
209
209
|
for c in all_concepts
|
|
210
210
|
if "__preql_internal" not in c.address
|
|
211
211
|
]
|
|
212
|
-
synonyms:
|
|
212
|
+
synonyms: set[str] = set()
|
|
213
213
|
for x in all_concepts:
|
|
214
|
-
synonyms
|
|
214
|
+
synonyms = synonyms.union(x.pseudonyms)
|
|
215
215
|
while break_flag is not True:
|
|
216
216
|
count += 1
|
|
217
217
|
if count > AMBIGUITY_CHECK_LIMIT:
|
|
@@ -385,9 +385,9 @@ def gen_merge_node(
|
|
|
385
385
|
# one concept handling may need to be kicked to alias
|
|
386
386
|
if len(all_concepts) == 1:
|
|
387
387
|
concept = all_concepts[0]
|
|
388
|
-
for
|
|
388
|
+
for v in concept.pseudonyms:
|
|
389
389
|
test = subgraphs_to_merge_node(
|
|
390
|
-
[[concept, v]],
|
|
390
|
+
[[concept, environment.alias_origin_lookup[v]]],
|
|
391
391
|
g=g,
|
|
392
392
|
all_concepts=[concept],
|
|
393
393
|
environment=environment,
|
|
@@ -162,17 +162,21 @@ def add_node_join_concept(
|
|
|
162
162
|
concept: Concept,
|
|
163
163
|
datasource: Datasource | QueryDatasource,
|
|
164
164
|
concepts: List[Concept],
|
|
165
|
+
environment: Environment,
|
|
165
166
|
):
|
|
166
167
|
|
|
167
168
|
concepts.append(concept)
|
|
168
169
|
|
|
169
170
|
graph.add_node(concept.address, type=NodeType.CONCEPT)
|
|
170
171
|
graph.add_edge(datasource.identifier, concept.address)
|
|
171
|
-
for
|
|
172
|
+
for v_address in concept.pseudonyms:
|
|
173
|
+
v = environment.alias_origin_lookup.get(
|
|
174
|
+
v_address, environment.concepts[v_address]
|
|
175
|
+
)
|
|
172
176
|
if v in concepts:
|
|
173
177
|
continue
|
|
174
|
-
if v
|
|
175
|
-
add_node_join_concept(graph, v, datasource, concepts)
|
|
178
|
+
if v != concept.address:
|
|
179
|
+
add_node_join_concept(graph, v, datasource, concepts, environment)
|
|
176
180
|
|
|
177
181
|
|
|
178
182
|
def get_node_joins(
|
|
@@ -186,7 +190,7 @@ def get_node_joins(
|
|
|
186
190
|
for datasource in datasources:
|
|
187
191
|
graph.add_node(datasource.identifier, type=NodeType.NODE)
|
|
188
192
|
for concept in datasource.output_concepts:
|
|
189
|
-
add_node_join_concept(graph, concept, datasource, concepts)
|
|
193
|
+
add_node_join_concept(graph, concept, datasource, concepts, environment)
|
|
190
194
|
|
|
191
195
|
# add edges for every constant to every datasource
|
|
192
196
|
for datasource in datasources:
|
|
@@ -195,7 +199,6 @@ def get_node_joins(
|
|
|
195
199
|
for node in graph.nodes:
|
|
196
200
|
if graph.nodes[node]["type"] == NodeType.NODE:
|
|
197
201
|
graph.add_edge(node, concept.address)
|
|
198
|
-
|
|
199
202
|
joins: defaultdict[str, set] = defaultdict(set)
|
|
200
203
|
identifier_map: dict[str, Datasource | QueryDatasource] = {
|
|
201
204
|
x.identifier: x for x in datasources
|
|
@@ -206,7 +209,7 @@ def get_node_joins(
|
|
|
206
209
|
# if we're looking up a pseudonym, we would have gotten the remapped value
|
|
207
210
|
# so double check we got what we were looking for
|
|
208
211
|
if env_lookup.address == g.address:
|
|
209
|
-
grain_pseudonyms.update(env_lookup.pseudonyms
|
|
212
|
+
grain_pseudonyms.update(env_lookup.pseudonyms)
|
|
210
213
|
|
|
211
214
|
node_list = sorted(
|
|
212
215
|
[x for x in graph.nodes if graph.nodes[x]["type"] == NodeType.NODE],
|
trilogy/dialect/base.py
CHANGED
|
@@ -263,7 +263,11 @@ class BaseDialect:
|
|
|
263
263
|
) -> str:
|
|
264
264
|
result = None
|
|
265
265
|
if c.pseudonyms:
|
|
266
|
-
for
|
|
266
|
+
candidates = [y for y in [cte.get_concept(x) for x in c.pseudonyms] if y]
|
|
267
|
+
logger.debug(
|
|
268
|
+
f"{LOGGER_PREFIX} [{c.address}] pseudonym candidates are {[x.address for x in candidates]}"
|
|
269
|
+
)
|
|
270
|
+
for candidate in [c] + candidates:
|
|
267
271
|
try:
|
|
268
272
|
logger.debug(
|
|
269
273
|
f"{LOGGER_PREFIX} [{c.address}] Attempting rendering w/ candidate {candidate.address}"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|