pytrilogy 0.0.3.112__py3-none-any.whl → 0.0.3.115__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.112.dist-info → pytrilogy-0.0.3.115.dist-info}/METADATA +14 -34
- {pytrilogy-0.0.3.112.dist-info → pytrilogy-0.0.3.115.dist-info}/RECORD +25 -25
- trilogy/__init__.py +1 -1
- trilogy/constants.py +28 -0
- trilogy/core/enums.py +7 -1
- trilogy/core/functions.py +51 -0
- trilogy/core/models/author.py +10 -2
- trilogy/core/models/build.py +17 -2
- trilogy/core/models/environment.py +1 -0
- trilogy/core/processing/concept_strategies_v3.py +24 -5
- trilogy/core/processing/discovery_node_factory.py +2 -2
- trilogy/core/processing/discovery_utility.py +11 -4
- trilogy/core/processing/node_generators/filter_node.py +7 -0
- trilogy/core/processing/node_generators/unnest_node.py +77 -6
- trilogy/core/statements/author.py +18 -4
- trilogy/dialect/base.py +14 -0
- trilogy/dialect/duckdb.py +12 -1
- trilogy/parsing/common.py +52 -17
- trilogy/parsing/parse_engine.py +76 -7
- trilogy/parsing/render.py +2 -1
- trilogy/parsing/trilogy.lark +15 -5
- {pytrilogy-0.0.3.112.dist-info → pytrilogy-0.0.3.115.dist-info}/WHEEL +0 -0
- {pytrilogy-0.0.3.112.dist-info → pytrilogy-0.0.3.115.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.3.112.dist-info → pytrilogy-0.0.3.115.dist-info}/licenses/LICENSE.md +0 -0
- {pytrilogy-0.0.3.112.dist-info → pytrilogy-0.0.3.115.dist-info}/top_level.txt +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: pytrilogy
|
|
3
|
-
Version: 0.0.3.
|
|
3
|
+
Version: 0.0.3.115
|
|
4
4
|
Summary: Declarative, typed query language that compiles to SQL.
|
|
5
5
|
Classifier: Programming Language :: Python
|
|
6
6
|
Classifier: Programming Language :: Python :: 3
|
|
@@ -51,61 +51,41 @@ The Trilogy language is an experiment in better SQL for analytics - a streamline
|
|
|
51
51
|
|
|
52
52
|
Trilogy is especially powerful for data consumption, providing a rich metadata layer that makes creating, interpreting, and visualizing queries easy and expressive.
|
|
53
53
|
|
|
54
|
+
|
|
55
|
+
We recommend starting with the studio to explore Trilogy. For integration, `pytrilogy` can be run locally to parse and execute trilogy model [.preql] files using the `trilogy` CLI tool, or can be run in python by importing the `trilogy` package.
|
|
56
|
+
|
|
57
|
+
|
|
54
58
|
## Quick Start
|
|
55
59
|
|
|
56
60
|
> [!TIP]
|
|
57
61
|
> **Try it now:** [Open-source studio](https://trilogydata.dev/trilogy-studio-core/) | [Interactive demo](https://trilogydata.dev/demo/) | [Documentation](https://trilogydata.dev/)
|
|
58
62
|
|
|
59
|
-
**Install
|
|
63
|
+
**Install**
|
|
60
64
|
```bash
|
|
61
65
|
pip install pytrilogy
|
|
62
66
|
```
|
|
63
67
|
|
|
64
|
-
**
|
|
68
|
+
**Save in hello.preql**
|
|
65
69
|
```sql
|
|
66
|
-
|
|
67
|
-
import names;
|
|
68
|
-
|
|
69
|
-
const top_names <- ['Elvis', 'Elvira', 'Elrond', 'Sam'];
|
|
70
|
+
const prime <- unnest([2, 3, 5, 7, 11, 13, 17, 19, 23, 29]);
|
|
70
71
|
|
|
71
|
-
def
|
|
72
|
+
def cube_plus_one(x) -> (x * x * x + 1);
|
|
72
73
|
|
|
73
74
|
WHERE
|
|
74
|
-
|
|
75
|
+
prime_cubed_plus_one % 7 = 0
|
|
75
76
|
SELECT
|
|
76
|
-
|
|
77
|
-
|
|
77
|
+
prime,
|
|
78
|
+
@cube_plus_one(prime) as prime_cubed_plus_one
|
|
78
79
|
ORDER BY
|
|
79
|
-
|
|
80
|
+
prime asc
|
|
80
81
|
LIMIT 10;
|
|
81
82
|
```
|
|
82
83
|
|
|
83
|
-
**Run it
|
|
84
|
+
**Run it in DuckDB**
|
|
84
85
|
```bash
|
|
85
86
|
trilogy run hello.preql duckdb
|
|
86
87
|
```
|
|
87
88
|
|
|
88
|
-
We recommend starting with the studio to explore Trilogy. For integration, `pytrilogy` can be run locally to parse and execute trilogy model [.preql] files using the `trilogy` CLI tool, or can be run in python by importing the `trilogy` package.
|
|
89
|
-
|
|
90
|
-
## Trilogy Looks Like SQL
|
|
91
|
-
|
|
92
|
-
```sql
|
|
93
|
-
import names;
|
|
94
|
-
|
|
95
|
-
const top_names <- ['Elvis', 'Elvira', 'Elrond', 'Sam'];
|
|
96
|
-
|
|
97
|
-
def initcap(word) -> upper(substring(word, 1, 1)) || substring(word, 2, len(word));
|
|
98
|
-
|
|
99
|
-
WHERE
|
|
100
|
-
@initcap(name) in top_names
|
|
101
|
-
SELECT
|
|
102
|
-
name,
|
|
103
|
-
sum(births) as name_count
|
|
104
|
-
ORDER BY
|
|
105
|
-
name_count desc
|
|
106
|
-
LIMIT 10;
|
|
107
|
-
```
|
|
108
|
-
|
|
109
89
|
## Trilogy is Easy to Write
|
|
110
90
|
For humans *and* AI. Enjoy flexible, one-shot query generation without any DB access or security risks.
|
|
111
91
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
pytrilogy-0.0.3.
|
|
2
|
-
trilogy/__init__.py,sha256=
|
|
3
|
-
trilogy/constants.py,sha256=
|
|
1
|
+
pytrilogy-0.0.3.115.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
|
|
2
|
+
trilogy/__init__.py,sha256=L11AizxIDnbCCjCvAE55VTsy0d4QmvAZfNp61uAPu00,304
|
|
3
|
+
trilogy/constants.py,sha256=2ERigKZBmULg3Qr43nWKgdwqiCnT8XPQVeH-O-k66wc,2640
|
|
4
4
|
trilogy/engine.py,sha256=v4TpNktM4zZ9OX7jZH2nde4dpX5uAH2U23ELfULTCSg,2280
|
|
5
5
|
trilogy/executor.py,sha256=q3EsAjzgxNxPn-yTHD_FTFzm7bJ2mlf9CrJEjyt6-pE,17884
|
|
6
6
|
trilogy/parser.py,sha256=o4cfk3j3yhUFoiDKq9ZX_GjBF3dKhDjXEwb63rcBkBM,293
|
|
@@ -23,24 +23,24 @@ trilogy/ai/providers/utils.py,sha256=yttP6y2E_XzdytBCwhaKekfXfxM6gE6MRce4AtyLL60
|
|
|
23
23
|
trilogy/authoring/__init__.py,sha256=TABMOETSMERrWuyDLR0nK4ISlqR0yaqeXrmuOdrSvAY,3060
|
|
24
24
|
trilogy/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
25
25
|
trilogy/core/constants.py,sha256=nizWYDCJQ1bigQMtkNIEMNTcN0NoEAXiIHLzpelxQ24,201
|
|
26
|
-
trilogy/core/enums.py,sha256=
|
|
26
|
+
trilogy/core/enums.py,sha256=R_iNsK8j0uGJ5fzfFUwoe1vhIHTX28VJfDQd99WBQ4E,9064
|
|
27
27
|
trilogy/core/env_processor.py,sha256=H-rr2ALj31l5oh3FqeI47Qju6OOfiXBacXNJGNZ92zQ,4521
|
|
28
28
|
trilogy/core/environment_helpers.py,sha256=TRlqVctqIRBxzfjRBmpQsAVoiCcsEKBhG1B6PUE0l1M,12743
|
|
29
29
|
trilogy/core/ergonomics.py,sha256=e-7gE29vPLFdg0_A1smQ7eOrUwKl5VYdxRSTddHweRA,1631
|
|
30
30
|
trilogy/core/exceptions.py,sha256=axkVXYJYQXCCwMHwlyDA232g4tCOwdCZUt7eHeUMDMg,2829
|
|
31
|
-
trilogy/core/functions.py,sha256=
|
|
31
|
+
trilogy/core/functions.py,sha256=hURJBM5dNcHfXuPHaHM_tz_ahsEktnmL4At0zebfjMk,34668
|
|
32
32
|
trilogy/core/graph_models.py,sha256=4EWFTHGfYd72zvS2HYoV6hm7nMC_VEd7vWr6txY-ig0,3400
|
|
33
33
|
trilogy/core/internal.py,sha256=r9QagDB2GvpqlyD_I7VrsfbVfIk5mnok2znEbv72Aa4,2681
|
|
34
34
|
trilogy/core/optimization.py,sha256=Km0ITEx9n6Iv5ReX6tm4uXO5uniSv_ooahycNNiET3g,9212
|
|
35
35
|
trilogy/core/query_processor.py,sha256=rMrtLSQxVm7yeyh0nWjDNI9nnu4Xi0NgHvBJ14gvu4I,20384
|
|
36
36
|
trilogy/core/utility.py,sha256=3VC13uSQWcZNghgt7Ot0ZTeEmNqs__cx122abVq9qhM,410
|
|
37
37
|
trilogy/core/models/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
38
|
-
trilogy/core/models/author.py,sha256=
|
|
39
|
-
trilogy/core/models/build.py,sha256=
|
|
38
|
+
trilogy/core/models/author.py,sha256=DFpIGlIruz0JYIOLJ0qZQrsdT4BvQX0augow1G8pD54,81841
|
|
39
|
+
trilogy/core/models/build.py,sha256=LN8G1JRk8RDSFQwqS3CUszIIUgB5v9Nxy6EpxvAcJ1A,71514
|
|
40
40
|
trilogy/core/models/build_environment.py,sha256=mpx7MKGc60fnZLVdeLi2YSREy7eQbQYycCrP4zF-rHU,5258
|
|
41
41
|
trilogy/core/models/core.py,sha256=iT9WdZoiXeglmUHWn6bZyXCTBpkApTGPKtNm_Mhbu_g,12987
|
|
42
42
|
trilogy/core/models/datasource.py,sha256=wogTevZ-9CyUW2a8gjzqMCieircxi-J5lkI7EOAZnck,9596
|
|
43
|
-
trilogy/core/models/environment.py,sha256=
|
|
43
|
+
trilogy/core/models/environment.py,sha256=do1Xvr9oyjDj0knAxgIqexSbt0HMrHbLJNyl9utkSvs,28760
|
|
44
44
|
trilogy/core/models/execute.py,sha256=3fgEdho2e7S0outq91cCzb9jFwz6L1hTbsTrJwGvIFs,42311
|
|
45
45
|
trilogy/core/optimizations/__init__.py,sha256=yspWc25M5SgAuvXYoSt5J8atyPbDlOfsKjIo5yGD9s4,368
|
|
46
46
|
trilogy/core/optimizations/base_optimization.py,sha256=gzDOKImoFn36k7XBD3ysEYDnbnb6vdVIztUfFQZsGnM,513
|
|
@@ -48,9 +48,9 @@ trilogy/core/optimizations/hide_unused_concept.py,sha256=DbsP8NqQOxmPv9omDOoFNPU
|
|
|
48
48
|
trilogy/core/optimizations/inline_datasource.py,sha256=2sWNRpoRInnTgo9wExVT_r9RfLAQHI57reEV5cGHUcg,4329
|
|
49
49
|
trilogy/core/optimizations/predicate_pushdown.py,sha256=5ubatgq1IwWQ4L2FDt4--y168YLuGP-vwqH0m8IeTIw,9786
|
|
50
50
|
trilogy/core/processing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
51
|
-
trilogy/core/processing/concept_strategies_v3.py,sha256
|
|
52
|
-
trilogy/core/processing/discovery_node_factory.py,sha256=
|
|
53
|
-
trilogy/core/processing/discovery_utility.py,sha256=
|
|
51
|
+
trilogy/core/processing/concept_strategies_v3.py,sha256=Y6AG10NGU_mUiTfXmxkss7QSO5iLpOdzGN6-7Hc5XiQ,23294
|
|
52
|
+
trilogy/core/processing/discovery_node_factory.py,sha256=llnLxZo1NqBRIuuPz0GUohym6LZFhVkPT3xSiORi3k4,15446
|
|
53
|
+
trilogy/core/processing/discovery_utility.py,sha256=mO0npZMRlQSzxt3l4m8garKBAOrXFkzt3eiiUyUSoIU,13528
|
|
54
54
|
trilogy/core/processing/discovery_validation.py,sha256=eZ4HfHMpqZLI8MGG2jez8arS8THs6ceuVrQFIY6gXrU,5364
|
|
55
55
|
trilogy/core/processing/graph_utils.py,sha256=8QUVrkE9j-9C1AyrCb1nQEh8daCe0u1HuXl-Te85lag,1205
|
|
56
56
|
trilogy/core/processing/utility.py,sha256=ESs6pKqVP2c9eMdfB2JNjw7D7YnoezVwbLFx1D6OUYA,26088
|
|
@@ -58,7 +58,7 @@ trilogy/core/processing/node_generators/__init__.py,sha256=iVJ-crowPxYeut-hFjyEj
|
|
|
58
58
|
trilogy/core/processing/node_generators/basic_node.py,sha256=rv53il-W633by0plvbN5qaqznJfl60yPinLZGBAC_MI,5707
|
|
59
59
|
trilogy/core/processing/node_generators/common.py,sha256=xF32Kf6B08dZgKs2SOow1HomptSiSC057GCUCHFlS5s,9464
|
|
60
60
|
trilogy/core/processing/node_generators/constant_node.py,sha256=LfpDq2WrBRZ3tGsLxw77LuigKfhbteWWh9L8BGdMGwk,1146
|
|
61
|
-
trilogy/core/processing/node_generators/filter_node.py,sha256=
|
|
61
|
+
trilogy/core/processing/node_generators/filter_node.py,sha256=EiP_tafx-X0gM-BIVRCy2rnp1_apt2cbhVfv8cg9dVg,11259
|
|
62
62
|
trilogy/core/processing/node_generators/group_node.py,sha256=sIm1QYrF4EY6sk56A48B6MieCZqvaJLSQebih_aiKnQ,8567
|
|
63
63
|
trilogy/core/processing/node_generators/group_to_node.py,sha256=jKcNCDOY6fNblrdZwaRU0sbUSr9H0moQbAxrGgX6iGA,3832
|
|
64
64
|
trilogy/core/processing/node_generators/multiselect_node.py,sha256=dHPDoSKU0FF6Ue_t_LkZxTd0Q-Sf-EpYdsMYdyUlFQc,7120
|
|
@@ -69,7 +69,7 @@ trilogy/core/processing/node_generators/select_merge_node.py,sha256=9MZdQJtkoe5N
|
|
|
69
69
|
trilogy/core/processing/node_generators/select_node.py,sha256=Ta1G39V94gjX_AgyZDz9OqnwLz4BjY3D6Drx9YpziMQ,3555
|
|
70
70
|
trilogy/core/processing/node_generators/synonym_node.py,sha256=AnAsa_Wj50NJ_IK0HSgab_7klYmKVrv0WI1uUe-GvEY,3766
|
|
71
71
|
trilogy/core/processing/node_generators/union_node.py,sha256=NxQbnRRoYMI4WjMeph41yk4E6yipj53qdGuNt-Mozxw,2818
|
|
72
|
-
trilogy/core/processing/node_generators/unnest_node.py,sha256=
|
|
72
|
+
trilogy/core/processing/node_generators/unnest_node.py,sha256=Q9E6MF6uVLqP15tlx_Bwa6mIWSNW68LJpkSYyr_TArs,6229
|
|
73
73
|
trilogy/core/processing/node_generators/window_node.py,sha256=wNvmumGO6AIQ7C9bDUYYZ6LJvDvPQPfFVX82pTxjV-k,6767
|
|
74
74
|
trilogy/core/processing/node_generators/select_helpers/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
75
75
|
trilogy/core/processing/node_generators/select_helpers/datasource_injection.py,sha256=m2YQ4OmG0N2O61a7NEq1ZzbTa7JsCC00lxB2ymjcYRI,8224
|
|
@@ -84,7 +84,7 @@ trilogy/core/processing/nodes/union_node.py,sha256=hLAXXVWqEgMWi7dlgSHfCF59fon64
|
|
|
84
84
|
trilogy/core/processing/nodes/unnest_node.py,sha256=oLKMMNMx6PLDPlt2V5neFMFrFWxET8r6XZElAhSNkO0,2181
|
|
85
85
|
trilogy/core/processing/nodes/window_node.py,sha256=JXJ0iVRlSEM2IBr1TANym2RaUf_p5E_l2sNykRzXWDo,1710
|
|
86
86
|
trilogy/core/statements/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
87
|
-
trilogy/core/statements/author.py,sha256=
|
|
87
|
+
trilogy/core/statements/author.py,sha256=zLd-1IZKF5UE7oWQYqifn3b3JcW2wCbonlELjJiZhfo,16436
|
|
88
88
|
trilogy/core/statements/build.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
89
89
|
trilogy/core/statements/common.py,sha256=VnVLULQg1TJLNUFzJaROT1tsf2ewk3IpuhvZaP36R6A,535
|
|
90
90
|
trilogy/core/statements/execute.py,sha256=kiwJcVeMa4wZR-xLfM2oYOJ9DeyJkP8An38WFyJxktM,2413
|
|
@@ -95,12 +95,12 @@ trilogy/core/validation/datasource.py,sha256=nJeEFyb6iMBwlEVdYVy1vLzAbdRZwOsUjGx
|
|
|
95
95
|
trilogy/core/validation/environment.py,sha256=ymvhQyt7jLK641JAAIQkqjQaAmr9C5022ILzYvDgPP0,2835
|
|
96
96
|
trilogy/core/validation/fix.py,sha256=Z818UFNLxndMTLiyhB3doLxIfnOZ-16QGvVFWuD7UsA,3750
|
|
97
97
|
trilogy/dialect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
98
|
-
trilogy/dialect/base.py,sha256=
|
|
98
|
+
trilogy/dialect/base.py,sha256=Eo8n5fJiHOYkfDDDFgnrqMuIZTxRorWwVer_1-DRG4c,51178
|
|
99
99
|
trilogy/dialect/bigquery.py,sha256=XS3hpybeowgfrOrkycAigAF3NX2YUzTzfgE6f__2fT4,4316
|
|
100
100
|
trilogy/dialect/common.py,sha256=I5Ku_TR5MwJTB3ZhcQenrtvXhH2RvTQ8wQe9w5lfkfA,5708
|
|
101
101
|
trilogy/dialect/config.py,sha256=olnyeVU5W5T6b9-dMeNAnvxuPlyc2uefb7FRME094Ec,3834
|
|
102
102
|
trilogy/dialect/dataframe.py,sha256=nDTHMSd7GiGjEhjAobrZND0w4zjr-vgOalM2Cdxjets,1596
|
|
103
|
-
trilogy/dialect/duckdb.py,sha256=
|
|
103
|
+
trilogy/dialect/duckdb.py,sha256=Z_mxrfQXS3KP8PEbkKsCLolyjJgPEM-aBkVCdWiLph0,6312
|
|
104
104
|
trilogy/dialect/enums.py,sha256=FRNYQ5-w-B6-X0yXKNU5g9GowsMlERFogTC5u2nxL_s,4740
|
|
105
105
|
trilogy/dialect/metadata.py,sha256=p_V-MYPQ2iR6fcTjagnptCIWtsZe4fTfoS_iXpavPzY,7098
|
|
106
106
|
trilogy/dialect/postgres.py,sha256=el2PKwfyvWGk5EZtLudqAH5ewLitY1sFHJiocBSyxyM,3393
|
|
@@ -113,13 +113,13 @@ trilogy/hooks/graph_hook.py,sha256=5BfR7Dt0bgEsCLgwjowgCsVkboGYfVJGOz8g9mqpnos,4
|
|
|
113
113
|
trilogy/hooks/query_debugger.py,sha256=1npRjww94sPV5RRBBlLqMJRaFkH9vhEY6o828MeoEcw,5583
|
|
114
114
|
trilogy/metadata/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
115
115
|
trilogy/parsing/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
116
|
-
trilogy/parsing/common.py,sha256=
|
|
116
|
+
trilogy/parsing/common.py,sha256=r9EmWzN2ozMKtWCY1HfjOREJnLKvaURT6h6edS5U4x8,32508
|
|
117
117
|
trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
|
|
118
118
|
trilogy/parsing/exceptions.py,sha256=Xwwsv2C9kSNv2q-HrrKC1f60JNHShXcCMzstTSEbiCw,154
|
|
119
119
|
trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
120
|
-
trilogy/parsing/parse_engine.py,sha256=
|
|
121
|
-
trilogy/parsing/render.py,sha256=
|
|
122
|
-
trilogy/parsing/trilogy.lark,sha256=
|
|
120
|
+
trilogy/parsing/parse_engine.py,sha256=lMo73QD37ih5kgvAOToMLCBbCkBzo_G7QMOaQaHprV8,84333
|
|
121
|
+
trilogy/parsing/render.py,sha256=k7MNp8EBTqVBSVqFlgTHSwIhfSKLyJfSeb2fSbt9dVA,24307
|
|
122
|
+
trilogy/parsing/trilogy.lark,sha256=kPOIrwa__kdXvxS3uM5GzdlgIa2cQxNW_0DNRqsRBeg,16939
|
|
123
123
|
trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
124
124
|
trilogy/scripts/trilogy.py,sha256=1L0XrH4mVHRt1C9T1HnaDv2_kYEfbWTb5_-cBBke79w,3774
|
|
125
125
|
trilogy/std/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -132,8 +132,8 @@ trilogy/std/money.preql,sha256=XWwvAV3WxBsHX9zfptoYRnBigcfYwrYtBHXTME0xJuQ,2082
|
|
|
132
132
|
trilogy/std/net.preql,sha256=WZCuvH87_rZntZiuGJMmBDMVKkdhTtxeHOkrXNwJ1EE,416
|
|
133
133
|
trilogy/std/ranking.preql,sha256=LDoZrYyz4g3xsII9XwXfmstZD-_92i1Eox1UqkBIfi8,83
|
|
134
134
|
trilogy/std/report.preql,sha256=LbV-XlHdfw0jgnQ8pV7acG95xrd1-p65fVpiIc-S7W4,202
|
|
135
|
-
pytrilogy-0.0.3.
|
|
136
|
-
pytrilogy-0.0.3.
|
|
137
|
-
pytrilogy-0.0.3.
|
|
138
|
-
pytrilogy-0.0.3.
|
|
139
|
-
pytrilogy-0.0.3.
|
|
135
|
+
pytrilogy-0.0.3.115.dist-info/METADATA,sha256=pB3OdY3t5s8GkX73rniQKQkMSy8JcXjzKZ0w1va3k5E,12911
|
|
136
|
+
pytrilogy-0.0.3.115.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
137
|
+
pytrilogy-0.0.3.115.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
|
|
138
|
+
pytrilogy-0.0.3.115.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
|
|
139
|
+
pytrilogy-0.0.3.115.dist-info/RECORD,,
|
trilogy/__init__.py
CHANGED
trilogy/constants.py
CHANGED
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import random
|
|
2
|
+
from contextlib import contextmanager
|
|
2
3
|
from dataclasses import dataclass, field
|
|
3
4
|
from enum import Enum
|
|
4
5
|
from logging import getLogger
|
|
6
|
+
from typing import Any
|
|
5
7
|
|
|
6
8
|
logger = getLogger("trilogy")
|
|
7
9
|
|
|
@@ -50,6 +52,32 @@ class Rendering:
|
|
|
50
52
|
parameters: bool = True
|
|
51
53
|
concise: bool = False
|
|
52
54
|
|
|
55
|
+
@contextmanager
|
|
56
|
+
def temporary(self, **kwargs: Any):
|
|
57
|
+
"""
|
|
58
|
+
Context manager to temporarily set attributes and revert them afterwards.
|
|
59
|
+
|
|
60
|
+
Usage:
|
|
61
|
+
r = Rendering()
|
|
62
|
+
with r.temporary(parameters=False, concise=True):
|
|
63
|
+
# parameters is False, concise is True here
|
|
64
|
+
do_something()
|
|
65
|
+
# parameters and concise are back to their original values
|
|
66
|
+
"""
|
|
67
|
+
# Store original values
|
|
68
|
+
original_values = {key: getattr(self, key) for key in kwargs}
|
|
69
|
+
|
|
70
|
+
# Set new values
|
|
71
|
+
for key, value in kwargs.items():
|
|
72
|
+
setattr(self, key, value)
|
|
73
|
+
|
|
74
|
+
try:
|
|
75
|
+
yield self
|
|
76
|
+
finally:
|
|
77
|
+
# Restore original values
|
|
78
|
+
for key, value in original_values.items():
|
|
79
|
+
setattr(self, key, value)
|
|
80
|
+
|
|
53
81
|
|
|
54
82
|
@dataclass
|
|
55
83
|
class Parsing:
|
trilogy/core/enums.py
CHANGED
|
@@ -169,6 +169,7 @@ class FunctionType(Enum):
|
|
|
169
169
|
ARRAY_SORT = "array_sort"
|
|
170
170
|
ARRAY_TRANSFORM = "array_transform"
|
|
171
171
|
ARRAY_TO_STRING = "array_to_string"
|
|
172
|
+
ARRAY_FILTER = "array_filter"
|
|
172
173
|
|
|
173
174
|
# MAP
|
|
174
175
|
MAP_KEYS = "map_keys"
|
|
@@ -204,6 +205,7 @@ class FunctionType(Enum):
|
|
|
204
205
|
MIN = "min"
|
|
205
206
|
AVG = "avg"
|
|
206
207
|
ARRAY_AGG = "array_agg"
|
|
208
|
+
ANY = "any"
|
|
207
209
|
|
|
208
210
|
# String
|
|
209
211
|
LIKE = "like"
|
|
@@ -236,12 +238,15 @@ class FunctionType(Enum):
|
|
|
236
238
|
MONTH = "month"
|
|
237
239
|
QUARTER = "quarter"
|
|
238
240
|
YEAR = "year"
|
|
241
|
+
MONTH_NAME = "month_name"
|
|
242
|
+
DAY_NAME = "day_name"
|
|
239
243
|
|
|
240
244
|
DATE_PART = "date_part"
|
|
241
245
|
DATE_TRUNCATE = "date_truncate"
|
|
242
246
|
DATE_ADD = "date_add"
|
|
243
247
|
DATE_SUB = "date_sub"
|
|
244
248
|
DATE_DIFF = "date_diff"
|
|
249
|
+
DATE_SPINE = "date_spine"
|
|
245
250
|
|
|
246
251
|
# UNIX
|
|
247
252
|
UNIX_TO_TIMESTAMP = "unix_to_timestamp"
|
|
@@ -261,6 +266,7 @@ class FunctionClass(Enum):
|
|
|
261
266
|
FunctionType.ARRAY_AGG,
|
|
262
267
|
FunctionType.COUNT,
|
|
263
268
|
FunctionType.COUNT_DISTINCT,
|
|
269
|
+
FunctionType.ANY,
|
|
264
270
|
]
|
|
265
271
|
SINGLE_ROW = [
|
|
266
272
|
FunctionType.CONSTANT,
|
|
@@ -268,7 +274,7 @@ class FunctionClass(Enum):
|
|
|
268
274
|
FunctionType.CURRENT_DATETIME,
|
|
269
275
|
]
|
|
270
276
|
|
|
271
|
-
ONE_TO_MANY = [FunctionType.UNNEST]
|
|
277
|
+
ONE_TO_MANY = [FunctionType.UNNEST, FunctionType.DATE_SPINE]
|
|
272
278
|
|
|
273
279
|
RECURSIVE = [FunctionType.RECURSE_EDGE]
|
|
274
280
|
|
trilogy/core/functions.py
CHANGED
|
@@ -175,6 +175,10 @@ def get_date_trunc_output(
|
|
|
175
175
|
return DataType.DATETIME
|
|
176
176
|
elif target == DatePart.SECOND:
|
|
177
177
|
return DataType.DATETIME
|
|
178
|
+
elif target == DatePart.WEEK:
|
|
179
|
+
return DataType.DATE
|
|
180
|
+
elif target == DatePart.QUARTER:
|
|
181
|
+
return DataType.DATE
|
|
178
182
|
else:
|
|
179
183
|
raise InvalidSyntaxException(f"Date truncation not supported for {target}")
|
|
180
184
|
|
|
@@ -208,6 +212,14 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
208
212
|
output_type_function=get_unnest_output_type,
|
|
209
213
|
arg_count=1,
|
|
210
214
|
),
|
|
215
|
+
FunctionType.DATE_SPINE: FunctionConfig(
|
|
216
|
+
valid_inputs={
|
|
217
|
+
DataType.DATE,
|
|
218
|
+
},
|
|
219
|
+
output_purpose=Purpose.KEY,
|
|
220
|
+
output_type=DataType.DATE,
|
|
221
|
+
arg_count=2,
|
|
222
|
+
),
|
|
211
223
|
FunctionType.RECURSE_EDGE: FunctionConfig(
|
|
212
224
|
arg_count=2,
|
|
213
225
|
),
|
|
@@ -314,6 +326,18 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
314
326
|
output_type_function=get_transform_output_type,
|
|
315
327
|
arg_count=3,
|
|
316
328
|
),
|
|
329
|
+
FunctionType.ARRAY_FILTER: FunctionConfig(
|
|
330
|
+
valid_inputs=[
|
|
331
|
+
{
|
|
332
|
+
DataType.ARRAY,
|
|
333
|
+
},
|
|
334
|
+
{*DataType},
|
|
335
|
+
{*DataType},
|
|
336
|
+
],
|
|
337
|
+
output_purpose=Purpose.PROPERTY,
|
|
338
|
+
output_type_function=get_transform_output_type,
|
|
339
|
+
arg_count=3,
|
|
340
|
+
),
|
|
317
341
|
FunctionType.ARRAY_TO_STRING: FunctionConfig(
|
|
318
342
|
valid_inputs={
|
|
319
343
|
DataType.ARRAY,
|
|
@@ -640,6 +664,17 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
640
664
|
output_type=TraitDataType(type=DataType.INTEGER, traits=["day"]),
|
|
641
665
|
arg_count=1,
|
|
642
666
|
),
|
|
667
|
+
FunctionType.DAY_NAME: FunctionConfig(
|
|
668
|
+
valid_inputs={
|
|
669
|
+
DataType.DATE,
|
|
670
|
+
DataType.TIMESTAMP,
|
|
671
|
+
DataType.DATETIME,
|
|
672
|
+
# DataType.STRING,
|
|
673
|
+
},
|
|
674
|
+
output_purpose=Purpose.PROPERTY,
|
|
675
|
+
output_type=TraitDataType(type=DataType.STRING, traits=["day_name"]),
|
|
676
|
+
arg_count=1,
|
|
677
|
+
),
|
|
643
678
|
FunctionType.WEEK: FunctionConfig(
|
|
644
679
|
valid_inputs={
|
|
645
680
|
DataType.DATE,
|
|
@@ -662,6 +697,17 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
662
697
|
output_type=TraitDataType(type=DataType.INTEGER, traits=["month"]),
|
|
663
698
|
arg_count=1,
|
|
664
699
|
),
|
|
700
|
+
FunctionType.MONTH_NAME: FunctionConfig(
|
|
701
|
+
valid_inputs={
|
|
702
|
+
DataType.DATE,
|
|
703
|
+
DataType.TIMESTAMP,
|
|
704
|
+
DataType.DATETIME,
|
|
705
|
+
# DataType.STRING,
|
|
706
|
+
},
|
|
707
|
+
output_purpose=Purpose.PROPERTY,
|
|
708
|
+
output_type=TraitDataType(type=DataType.STRING, traits=["month_name"]),
|
|
709
|
+
arg_count=1,
|
|
710
|
+
),
|
|
665
711
|
FunctionType.QUARTER: FunctionConfig(
|
|
666
712
|
valid_inputs={
|
|
667
713
|
DataType.DATE,
|
|
@@ -861,6 +907,11 @@ FUNCTION_REGISTRY: dict[FunctionType, FunctionConfig] = {
|
|
|
861
907
|
),
|
|
862
908
|
arg_count=1,
|
|
863
909
|
),
|
|
910
|
+
FunctionType.ANY: FunctionConfig(
|
|
911
|
+
valid_inputs={*DataType},
|
|
912
|
+
output_purpose=Purpose.PROPERTY,
|
|
913
|
+
arg_count=1,
|
|
914
|
+
),
|
|
864
915
|
FunctionType.AVG: FunctionConfig(
|
|
865
916
|
valid_inputs={
|
|
866
917
|
DataType.INTEGER,
|
trilogy/core/models/author.py
CHANGED
|
@@ -461,6 +461,11 @@ class WhereClause(Mergeable, ConceptArgs, Namespaced, BaseModel):
|
|
|
461
461
|
conditional=self.conditional.with_namespace(namespace)
|
|
462
462
|
)
|
|
463
463
|
|
|
464
|
+
def with_reference_replacement(self, source, target):
|
|
465
|
+
return self.__class__.model_construct(
|
|
466
|
+
conditional=self.conditional.with_reference_replacement(source, target)
|
|
467
|
+
)
|
|
468
|
+
|
|
464
469
|
|
|
465
470
|
class HavingClause(WhereClause):
|
|
466
471
|
pass
|
|
@@ -1212,6 +1217,8 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
|
|
|
1212
1217
|
return Derivation.FILTER
|
|
1213
1218
|
elif lineage and isinstance(lineage, (BuildAggregateWrapper, AggregateWrapper)):
|
|
1214
1219
|
return Derivation.AGGREGATE
|
|
1220
|
+
# elif lineage and isinstance(lineage, (BuildParenthetical, Parenthetical)):
|
|
1221
|
+
# return Derivation.PARENTHETICAL
|
|
1215
1222
|
elif lineage and isinstance(lineage, (BuildRowsetItem, RowsetItem)):
|
|
1216
1223
|
return Derivation.ROWSET
|
|
1217
1224
|
elif lineage and isinstance(
|
|
@@ -1227,7 +1234,7 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
|
|
|
1227
1234
|
elif (
|
|
1228
1235
|
lineage
|
|
1229
1236
|
and isinstance(lineage, (BuildFunction, Function))
|
|
1230
|
-
and lineage.operator
|
|
1237
|
+
and lineage.operator in FunctionClass.ONE_TO_MANY.value
|
|
1231
1238
|
):
|
|
1232
1239
|
return Derivation.UNNEST
|
|
1233
1240
|
elif (
|
|
@@ -1279,7 +1286,8 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
|
|
|
1279
1286
|
elif (
|
|
1280
1287
|
lineage
|
|
1281
1288
|
and isinstance(lineage, (Function, BuildFunction))
|
|
1282
|
-
and lineage.operator
|
|
1289
|
+
and lineage.operator
|
|
1290
|
+
in (FunctionType.UNNEST, FunctionType.UNION, FunctionType.DATE_SPINE)
|
|
1283
1291
|
):
|
|
1284
1292
|
return Granularity.MULTI_ROW
|
|
1285
1293
|
elif lineage and all(
|
trilogy/core/models/build.py
CHANGED
|
@@ -134,8 +134,9 @@ def concept_is_relevant(
|
|
|
134
134
|
if concept.purpose in (Purpose.METRIC,):
|
|
135
135
|
if all([c in others for c in concept.grain.components]):
|
|
136
136
|
return False
|
|
137
|
+
if concept.derivation in (Derivation.UNNEST,):
|
|
138
|
+
return True
|
|
137
139
|
if concept.derivation in (Derivation.BASIC,):
|
|
138
|
-
|
|
139
140
|
return any(concept_is_relevant(c, others) for c in concept.concept_arguments)
|
|
140
141
|
if concept.granularity == Granularity.SINGLE_ROW:
|
|
141
142
|
return False
|
|
@@ -1668,7 +1669,6 @@ class Factory:
|
|
|
1668
1669
|
valid_inputs=base.valid_inputs,
|
|
1669
1670
|
arg_count=base.arg_count,
|
|
1670
1671
|
)
|
|
1671
|
-
|
|
1672
1672
|
new = BuildFunction(
|
|
1673
1673
|
operator=base.operator,
|
|
1674
1674
|
arguments=[self.handle_constant(self.build(c)) for c in raw_args],
|
|
@@ -1724,6 +1724,14 @@ class Factory:
|
|
|
1724
1724
|
return self._build_concept(base)
|
|
1725
1725
|
|
|
1726
1726
|
def _build_concept(self, base: Concept) -> BuildConcept:
|
|
1727
|
+
try:
|
|
1728
|
+
return self.__build_concept(base)
|
|
1729
|
+
except RecursionError as e:
|
|
1730
|
+
raise RecursionError(
|
|
1731
|
+
f"Recursion error building concept {base.address}. This is likely due to a circular reference."
|
|
1732
|
+
) from e
|
|
1733
|
+
|
|
1734
|
+
def __build_concept(self, base: Concept) -> BuildConcept:
|
|
1727
1735
|
# TODO: if we are using parameters, wrap it in a new model and use that in rendering
|
|
1728
1736
|
if base.address in self.local_concepts:
|
|
1729
1737
|
return self.local_concepts[base.address]
|
|
@@ -2002,6 +2010,13 @@ class Factory:
|
|
|
2002
2010
|
def _build_tuple_wrapper(self, base: TupleWrapper) -> TupleWrapper:
|
|
2003
2011
|
return TupleWrapper(val=[self.build(x) for x in base.val], type=base.type)
|
|
2004
2012
|
|
|
2013
|
+
@build.register
|
|
2014
|
+
def _(self, base: ListWrapper) -> ListWrapper:
|
|
2015
|
+
return self._build_list_wrapper(base)
|
|
2016
|
+
|
|
2017
|
+
def _build_list_wrapper(self, base: ListWrapper) -> ListWrapper:
|
|
2018
|
+
return ListWrapper([self.build(x) for x in base], type=base.type)
|
|
2019
|
+
|
|
2005
2020
|
@build.register
|
|
2006
2021
|
def _(self, base: FilterItem) -> BuildFilterItem:
|
|
2007
2022
|
return self._build_filter_item(base)
|
|
@@ -563,6 +563,7 @@ class Environment(BaseModel):
|
|
|
563
563
|
existing = self.validate_concept(concept, meta=meta)
|
|
564
564
|
if existing:
|
|
565
565
|
concept = existing
|
|
566
|
+
|
|
566
567
|
self.concepts[concept.address] = concept
|
|
567
568
|
|
|
568
569
|
from trilogy.core.environment_helpers import generate_related_concepts
|
|
@@ -306,7 +306,12 @@ def evaluate_loop_conditions(
|
|
|
306
306
|
|
|
307
307
|
|
|
308
308
|
def check_for_early_exit(
|
|
309
|
-
complete
|
|
309
|
+
complete: ValidationResult,
|
|
310
|
+
found: set[str],
|
|
311
|
+
partial: set[str],
|
|
312
|
+
missing: set[str],
|
|
313
|
+
context: LoopContext,
|
|
314
|
+
priority_concept: BuildConcept,
|
|
310
315
|
) -> bool:
|
|
311
316
|
if complete == ValidationResult.INCOMPLETE_CONDITION:
|
|
312
317
|
cond_dict = {str(node): node.preexisting_conditions for node in context.stack}
|
|
@@ -331,8 +336,18 @@ def check_for_early_exit(
|
|
|
331
336
|
f"{depth_to_prefix(context.depth)}{LOGGER_PREFIX} Breaking as we have attempted all nodes"
|
|
332
337
|
)
|
|
333
338
|
return True
|
|
339
|
+
elif all(
|
|
340
|
+
[
|
|
341
|
+
x.address in found and x.address not in partial
|
|
342
|
+
for x in context.mandatory_list
|
|
343
|
+
]
|
|
344
|
+
):
|
|
345
|
+
logger.info(
|
|
346
|
+
f"{depth_to_prefix(context.depth)}{LOGGER_PREFIX} Breaking as we have found all mandatory nodes without partials"
|
|
347
|
+
)
|
|
348
|
+
return True
|
|
334
349
|
logger.info(
|
|
335
|
-
f"{depth_to_prefix(context.depth)}{LOGGER_PREFIX} Found complete stack with partials {partial}, continuing search, attempted {context.attempted}
|
|
350
|
+
f"{depth_to_prefix(context.depth)}{LOGGER_PREFIX} Found complete stack with partials {partial}, continuing search, attempted {context.attempted} of total {len(context.mandatory_list)}."
|
|
336
351
|
)
|
|
337
352
|
else:
|
|
338
353
|
logger.info(
|
|
@@ -436,6 +451,7 @@ def generate_loop_completion(context: LoopContext, virtual: set[str]) -> Strateg
|
|
|
436
451
|
context.original_mandatory,
|
|
437
452
|
context.environment,
|
|
438
453
|
non_virtual_difference_values,
|
|
454
|
+
depth=context.depth,
|
|
439
455
|
)
|
|
440
456
|
|
|
441
457
|
return group_if_required_v2(
|
|
@@ -443,6 +459,7 @@ def generate_loop_completion(context: LoopContext, virtual: set[str]) -> Strateg
|
|
|
443
459
|
context.original_mandatory,
|
|
444
460
|
context.environment,
|
|
445
461
|
non_virtual_difference_values,
|
|
462
|
+
depth=context.depth,
|
|
446
463
|
)
|
|
447
464
|
|
|
448
465
|
|
|
@@ -466,6 +483,7 @@ def _search_concepts(
|
|
|
466
483
|
conditions=conditions,
|
|
467
484
|
)
|
|
468
485
|
|
|
486
|
+
# if we get a can
|
|
469
487
|
if candidate:
|
|
470
488
|
return candidate
|
|
471
489
|
context = initialize_loop_context(
|
|
@@ -477,13 +495,14 @@ def _search_concepts(
|
|
|
477
495
|
accept_partial=accept_partial,
|
|
478
496
|
conditions=conditions,
|
|
479
497
|
)
|
|
480
|
-
|
|
498
|
+
partial: set[str] = set()
|
|
481
499
|
while context.incomplete:
|
|
482
500
|
|
|
483
501
|
priority_concept = get_priority_concept(
|
|
484
502
|
context.mandatory_list,
|
|
485
503
|
context.attempted,
|
|
486
504
|
found_concepts=context.found,
|
|
505
|
+
partial_concepts=partial,
|
|
487
506
|
depth=depth,
|
|
488
507
|
)
|
|
489
508
|
|
|
@@ -538,7 +557,7 @@ def _search_concepts(
|
|
|
538
557
|
# assign
|
|
539
558
|
context.found = found_c
|
|
540
559
|
early_exit = check_for_early_exit(
|
|
541
|
-
complete, partial, missing_c, context, priority_concept
|
|
560
|
+
complete, found_c, partial, missing_c, context, priority_concept
|
|
542
561
|
)
|
|
543
562
|
if early_exit:
|
|
544
563
|
break
|
|
@@ -608,4 +627,4 @@ def source_query_concepts(
|
|
|
608
627
|
logger.info(
|
|
609
628
|
f"{depth_to_prefix(0)}{LOGGER_PREFIX} final concepts are {[x.address for x in final]}"
|
|
610
629
|
)
|
|
611
|
-
return group_if_required_v2(root, output_concepts, environment)
|
|
630
|
+
return group_if_required_v2(root, output_concepts, environment, depth=0)
|
|
@@ -187,7 +187,7 @@ def _generate_aggregate_node(ctx: NodeGenerationContext) -> StrategyNode | None:
|
|
|
187
187
|
|
|
188
188
|
logger.info(
|
|
189
189
|
f"{depth_to_prefix(ctx.depth)}{LOGGER_PREFIX} "
|
|
190
|
-
f"for {ctx.concept.address}, generating aggregate node with {agg_optional}"
|
|
190
|
+
f"for {ctx.concept.address}, generating aggregate node with optional {agg_optional}"
|
|
191
191
|
)
|
|
192
192
|
|
|
193
193
|
return gen_group_node(
|
|
@@ -441,7 +441,7 @@ def generate_node(
|
|
|
441
441
|
depth: int,
|
|
442
442
|
source_concepts: SearchConceptsType,
|
|
443
443
|
history: History,
|
|
444
|
-
accept_partial: bool
|
|
444
|
+
accept_partial: bool,
|
|
445
445
|
conditions: BuildWhereClause | None = None,
|
|
446
446
|
) -> StrategyNode | None:
|
|
447
447
|
|
|
@@ -184,10 +184,14 @@ def group_if_required_v2(
|
|
|
184
184
|
final: List[BuildConcept],
|
|
185
185
|
environment: BuildEnvironment,
|
|
186
186
|
where_injected: set[str] | None = None,
|
|
187
|
+
depth: int = 0,
|
|
187
188
|
):
|
|
188
189
|
where_injected = where_injected or set()
|
|
189
190
|
required = check_if_group_required(
|
|
190
|
-
downstream_concepts=final,
|
|
191
|
+
downstream_concepts=final,
|
|
192
|
+
parents=[root.resolve()],
|
|
193
|
+
environment=environment,
|
|
194
|
+
depth=depth,
|
|
191
195
|
)
|
|
192
196
|
targets = [
|
|
193
197
|
x
|
|
@@ -258,6 +262,7 @@ def get_priority_concept(
|
|
|
258
262
|
all_concepts: List[BuildConcept],
|
|
259
263
|
attempted_addresses: set[str],
|
|
260
264
|
found_concepts: set[str],
|
|
265
|
+
partial_concepts: set[str],
|
|
261
266
|
depth: int,
|
|
262
267
|
) -> BuildConcept:
|
|
263
268
|
# optimized search for missing concepts
|
|
@@ -265,13 +270,15 @@ def get_priority_concept(
|
|
|
265
270
|
[
|
|
266
271
|
c
|
|
267
272
|
for c in all_concepts
|
|
268
|
-
if c.address not in attempted_addresses
|
|
273
|
+
if c.address not in attempted_addresses
|
|
274
|
+
and (c.address not in found_concepts or c.address in partial_concepts)
|
|
269
275
|
],
|
|
270
276
|
key=lambda x: x.address,
|
|
271
277
|
)
|
|
272
278
|
# sometimes we need to scan intermediate concepts to get merge keys or filter keys,
|
|
273
279
|
# so do an exhaustive search
|
|
274
|
-
# pass_two = [c for c in all_concepts
|
|
280
|
+
# pass_two = [c for c in all_concepts if c.address not in attempted_addresses]
|
|
281
|
+
|
|
275
282
|
for remaining_concept in (pass_one,):
|
|
276
283
|
priority = (
|
|
277
284
|
# then multiselects to remove them from scope
|
|
@@ -333,5 +340,5 @@ def get_priority_concept(
|
|
|
333
340
|
if final:
|
|
334
341
|
return final[0]
|
|
335
342
|
raise ValueError(
|
|
336
|
-
f"Cannot resolve query. No remaining priority concepts, have attempted {attempted_addresses}"
|
|
343
|
+
f"Cannot resolve query. No remaining priority concepts, have attempted {attempted_addresses} out of {all_concepts} with found {found_concepts}"
|
|
337
344
|
)
|