pytrilogy 0.0.3.95__py3-none-any.whl → 0.0.3.97__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.95.dist-info → pytrilogy-0.0.3.97.dist-info}/METADATA +44 -7
- {pytrilogy-0.0.3.95.dist-info → pytrilogy-0.0.3.97.dist-info}/RECORD +24 -23
- trilogy/__init__.py +1 -1
- trilogy/authoring/__init__.py +59 -45
- trilogy/constants.py +1 -0
- trilogy/core/enums.py +9 -0
- trilogy/core/exceptions.py +56 -2
- trilogy/core/graph_models.py +4 -4
- trilogy/core/statements/execute.py +2 -0
- trilogy/core/validation/common.py +55 -3
- trilogy/core/validation/concept.py +40 -25
- trilogy/core/validation/datasource.py +38 -34
- trilogy/core/validation/environment.py +4 -3
- trilogy/core/validation/fix.py +106 -0
- trilogy/dialect/base.py +10 -1
- trilogy/dialect/metadata.py +233 -0
- trilogy/executor.py +33 -163
- trilogy/parsing/parse_engine.py +8 -6
- trilogy/parsing/render.py +30 -3
- trilogy/parsing/trilogy.lark +7 -4
- trilogy/compiler.py +0 -0
- {pytrilogy-0.0.3.95.dist-info → pytrilogy-0.0.3.97.dist-info}/WHEEL +0 -0
- {pytrilogy-0.0.3.95.dist-info → pytrilogy-0.0.3.97.dist-info}/entry_points.txt +0 -0
- {pytrilogy-0.0.3.95.dist-info → pytrilogy-0.0.3.97.dist-info}/licenses/LICENSE.md +0 -0
- {pytrilogy-0.0.3.95.dist-info → pytrilogy-0.0.3.97.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.97
|
|
4
4
|
Summary: Declarative, typed query language that compiles to SQL.
|
|
5
5
|
Home-page:
|
|
6
6
|
Author:
|
|
@@ -136,11 +136,11 @@ Versus SQL, Trilogy aims to:
|
|
|
136
136
|
|
|
137
137
|
| Backend | Status | Notes |
|
|
138
138
|
|---------|--------|-------|
|
|
139
|
-
| **BigQuery** |
|
|
140
|
-
| **DuckDB** |
|
|
141
|
-
| **Snowflake** |
|
|
142
|
-
| **SQL Server** |
|
|
143
|
-
| **Presto** |
|
|
139
|
+
| **BigQuery** | Core | Full support |
|
|
140
|
+
| **DuckDB** | Core | Full support |
|
|
141
|
+
| **Snowflake** | Core | Full support |
|
|
142
|
+
| **SQL Server** | Experimental | Limited testing |
|
|
143
|
+
| **Presto** | Experimental | Limited testing |
|
|
144
144
|
|
|
145
145
|
## Examples
|
|
146
146
|
|
|
@@ -311,7 +311,44 @@ trilogy fmt <path to trilogy file>
|
|
|
311
311
|
- [Public model repository](https://github.com/trilogydata/trilogy-public-models) - Great place for modeling examples
|
|
312
312
|
- [Full documentation](https://trilogydata.dev/)
|
|
313
313
|
|
|
314
|
-
##
|
|
314
|
+
## Python API Integration
|
|
315
|
+
|
|
316
|
+
### Root Imports
|
|
317
|
+
|
|
318
|
+
Are stable and should be sufficient for executing code from Trilogy as text.
|
|
319
|
+
|
|
320
|
+
```python
|
|
321
|
+
from pytrilogy import Executor, Dialect
|
|
322
|
+
```
|
|
323
|
+
|
|
324
|
+
### Authoring Imports
|
|
325
|
+
|
|
326
|
+
Are also stable, and should be used for cases which programatically generate Trilogy statements without a base text format
|
|
327
|
+
or need to process/transform parsed code in more complicated ways.
|
|
328
|
+
|
|
329
|
+
```python
|
|
330
|
+
from pytrilogy.authoring import Concept, Function, ...
|
|
331
|
+
```
|
|
332
|
+
|
|
333
|
+
### Other Imports
|
|
334
|
+
|
|
335
|
+
Are likely to be unstable. Open an issue if you need to take dependencies on other modules outside those two paths.
|
|
336
|
+
|
|
337
|
+
## MCP/Server
|
|
338
|
+
|
|
339
|
+
Trilogy is straightforward to run as a server/MCP server; the former to generate SQL on demand and integrate into other tools, and MCP
|
|
340
|
+
for full interactive query loops.
|
|
341
|
+
|
|
342
|
+
This makes it easy to integrate Trilogy into existing tools or workflows.
|
|
343
|
+
|
|
344
|
+
You can see examples of both use cases in the trilogy-studio codebase [here](https://github.com/trilogy-data/trilogy-studio-core)
|
|
345
|
+
and install and run an MCP server directly with that codebase.
|
|
346
|
+
|
|
347
|
+
If you're interested in a more fleshed out standalone server or MCP server, please open an issue and we'll prioritize it!
|
|
348
|
+
|
|
349
|
+
## Trilogy Syntax Reference
|
|
350
|
+
|
|
351
|
+
Not exhaustive - see [documentation](https://trilogydata.dev/) for more details.
|
|
315
352
|
|
|
316
353
|
### Import
|
|
317
354
|
```sql
|
|
@@ -1,23 +1,22 @@
|
|
|
1
|
-
pytrilogy-0.0.3.
|
|
2
|
-
trilogy/__init__.py,sha256=
|
|
3
|
-
trilogy/
|
|
4
|
-
trilogy/constants.py,sha256=eKb_EJvSqjN9tGbdVEViwdtwwh8fZ3-jpOEDqL71y70,1691
|
|
1
|
+
pytrilogy-0.0.3.97.dist-info/licenses/LICENSE.md,sha256=5ZRvtTyCCFwz1THxDTjAu3Lidds9WjPvvzgVwPSYNDo,1042
|
|
2
|
+
trilogy/__init__.py,sha256=cYPR5Qx9Z_vJCc8nN83bLxuX2AWSJ8GNmHHEIFoyexA,303
|
|
3
|
+
trilogy/constants.py,sha256=SSsRMg9HTou259nMKAw-rJNBgzkWjQ3QIQXcrq9i5Kk,1717
|
|
5
4
|
trilogy/engine.py,sha256=3MiADf5MKcmxqiHBuRqiYdsXiLj7oitDfVvXvHrfjkA,2178
|
|
6
|
-
trilogy/executor.py,sha256=
|
|
5
|
+
trilogy/executor.py,sha256=YfSjuJ0FVm2gHnNgmUlXijWDTUFjqq9FNakWpeEYO48,15769
|
|
7
6
|
trilogy/parser.py,sha256=o4cfk3j3yhUFoiDKq9ZX_GjBF3dKhDjXEwb63rcBkBM,293
|
|
8
7
|
trilogy/py.typed,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
9
8
|
trilogy/render.py,sha256=qQWwduymauOlB517UtM-VGbVe8Cswa4UJub5aGbSO6c,1512
|
|
10
9
|
trilogy/utility.py,sha256=euQccZLKoYBz0LNg5tzLlvv2YHvXh9HArnYp1V3uXsM,763
|
|
11
|
-
trilogy/authoring/__init__.py,sha256=
|
|
10
|
+
trilogy/authoring/__init__.py,sha256=TABMOETSMERrWuyDLR0nK4ISlqR0yaqeXrmuOdrSvAY,3060
|
|
12
11
|
trilogy/core/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
13
12
|
trilogy/core/constants.py,sha256=nizWYDCJQ1bigQMtkNIEMNTcN0NoEAXiIHLzpelxQ24,201
|
|
14
|
-
trilogy/core/enums.py,sha256=
|
|
13
|
+
trilogy/core/enums.py,sha256=H8I2Dz4POHZ4ixYCGzNs4c3KDqxLQklGLVfmje1DSMo,8877
|
|
15
14
|
trilogy/core/env_processor.py,sha256=H-rr2ALj31l5oh3FqeI47Qju6OOfiXBacXNJGNZ92zQ,4521
|
|
16
15
|
trilogy/core/environment_helpers.py,sha256=TRlqVctqIRBxzfjRBmpQsAVoiCcsEKBhG1B6PUE0l1M,12743
|
|
17
16
|
trilogy/core/ergonomics.py,sha256=e-7gE29vPLFdg0_A1smQ7eOrUwKl5VYdxRSTddHweRA,1631
|
|
18
|
-
trilogy/core/exceptions.py,sha256=
|
|
17
|
+
trilogy/core/exceptions.py,sha256=fI16oTNCVMMAJFSn2AFzZVapzsF5M9WbdN5e5UixwXc,2807
|
|
19
18
|
trilogy/core/functions.py,sha256=ESUWMRmwtavwCLl6z1NP9EFzWTJoXn3orTaaOSsj33Q,33093
|
|
20
|
-
trilogy/core/graph_models.py,sha256=
|
|
19
|
+
trilogy/core/graph_models.py,sha256=4EWFTHGfYd72zvS2HYoV6hm7nMC_VEd7vWr6txY-ig0,3400
|
|
21
20
|
trilogy/core/internal.py,sha256=r9QagDB2GvpqlyD_I7VrsfbVfIk5mnok2znEbv72Aa4,2681
|
|
22
21
|
trilogy/core/optimization.py,sha256=ojpn-p79lr03SSVQbbw74iPCyoYpDYBmj1dbZ3oXCjI,8860
|
|
23
22
|
trilogy/core/query_processor.py,sha256=uqygDJqkjIH4vLP-lbGRgTN7rRcYEkr3KGqNimNw_80,20345
|
|
@@ -75,20 +74,22 @@ trilogy/core/statements/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG
|
|
|
75
74
|
trilogy/core/statements/author.py,sha256=VFzylve72fw0tqMSP5Yiwp8--_r92b9zzX1XAdxuTYQ,15963
|
|
76
75
|
trilogy/core/statements/build.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
77
76
|
trilogy/core/statements/common.py,sha256=VnVLULQg1TJLNUFzJaROT1tsf2ewk3IpuhvZaP36R6A,535
|
|
78
|
-
trilogy/core/statements/execute.py,sha256=
|
|
77
|
+
trilogy/core/statements/execute.py,sha256=kiwJcVeMa4wZR-xLfM2oYOJ9DeyJkP8An38WFyJxktM,2413
|
|
79
78
|
trilogy/core/validation/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
80
|
-
trilogy/core/validation/common.py,sha256=
|
|
81
|
-
trilogy/core/validation/concept.py,sha256=
|
|
82
|
-
trilogy/core/validation/datasource.py,sha256=
|
|
83
|
-
trilogy/core/validation/environment.py,sha256=
|
|
79
|
+
trilogy/core/validation/common.py,sha256=Sd-towAX1uSDe3dK51FcVtIwVrMhayEwdHqhzeJHro0,4776
|
|
80
|
+
trilogy/core/validation/concept.py,sha256=Jr8H9wn0-OMJeSFD5GydiB-LvCSj4CUe72evHjRq85E,4991
|
|
81
|
+
trilogy/core/validation/datasource.py,sha256=d9AQNcukIRgN2spItPsXFiNtlZva-lDnfei3i06yQCE,6489
|
|
82
|
+
trilogy/core/validation/environment.py,sha256=waBPMB6rxa-9SKUAdhfT8UUDqh4a76n27pKtRDre560,2834
|
|
83
|
+
trilogy/core/validation/fix.py,sha256=Z818UFNLxndMTLiyhB3doLxIfnOZ-16QGvVFWuD7UsA,3750
|
|
84
84
|
trilogy/dialect/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
85
|
-
trilogy/dialect/base.py,sha256=
|
|
85
|
+
trilogy/dialect/base.py,sha256=0QVHv4F0t3_gRQrZ0woFoUNKu7vaXGo-BG1l47CZUKc,49698
|
|
86
86
|
trilogy/dialect/bigquery.py,sha256=XS3hpybeowgfrOrkycAigAF3NX2YUzTzfgE6f__2fT4,4316
|
|
87
87
|
trilogy/dialect/common.py,sha256=tSthIZOXXRPQ4KeMKnDDsH7KlTmf2EVqigVtLyoc4zc,6071
|
|
88
88
|
trilogy/dialect/config.py,sha256=olnyeVU5W5T6b9-dMeNAnvxuPlyc2uefb7FRME094Ec,3834
|
|
89
89
|
trilogy/dialect/dataframe.py,sha256=RUbNgReEa9g3pL6H7fP9lPTrAij5pkqedpZ99D8_5AE,1522
|
|
90
90
|
trilogy/dialect/duckdb.py,sha256=JoUvQ19WvgxoaJkGLM7DPXOd1H0394k3vBiblksQzOI,5631
|
|
91
91
|
trilogy/dialect/enums.py,sha256=FRNYQ5-w-B6-X0yXKNU5g9GowsMlERFogTC5u2nxL_s,4740
|
|
92
|
+
trilogy/dialect/metadata.py,sha256=Vt4-p82bD1ijqeoI2dagUVUbC-KgNNJ2MvDwQIa5mG8,7034
|
|
92
93
|
trilogy/dialect/postgres.py,sha256=el2PKwfyvWGk5EZtLudqAH5ewLitY1sFHJiocBSyxyM,3393
|
|
93
94
|
trilogy/dialect/presto.py,sha256=k1IaeilR3nzPC9Hp7jlAdzJ7TsuxB3LQTBQ28MYE7O8,3715
|
|
94
95
|
trilogy/dialect/snowflake.py,sha256=T6_mKfhpDazB1xQxqFLS2AJwzwzBcPYY6_qxRnAtFBs,3326
|
|
@@ -103,9 +104,9 @@ trilogy/parsing/common.py,sha256=550-L0444GUuBFdiDWkOg_DxnMXtcJFUMES2R5zlwik,310
|
|
|
103
104
|
trilogy/parsing/config.py,sha256=Z-DaefdKhPDmSXLgg5V4pebhSB0h590vI0_VtHnlukI,111
|
|
104
105
|
trilogy/parsing/exceptions.py,sha256=Xwwsv2C9kSNv2q-HrrKC1f60JNHShXcCMzstTSEbiCw,154
|
|
105
106
|
trilogy/parsing/helpers.py,sha256=AbpHGcgLb-kRsJGnwFEktk7uzpZOCcBY74-YBdrKVGs,1
|
|
106
|
-
trilogy/parsing/parse_engine.py,sha256=
|
|
107
|
-
trilogy/parsing/render.py,sha256=
|
|
108
|
-
trilogy/parsing/trilogy.lark,sha256=
|
|
107
|
+
trilogy/parsing/parse_engine.py,sha256=Zd4Zpj8k_Q95onXlThV_AWSwjUAGDsD57uTyKKmXxNI,81784
|
|
108
|
+
trilogy/parsing/render.py,sha256=OirN76I8z9xDNGvydO4DhsTsN-NS_1SC5_77_V23CkI,20515
|
|
109
|
+
trilogy/parsing/trilogy.lark,sha256=rM4WleeyGhoRgU-FOGcaeHOzZcYVxN4f13e_3B4OeLQ,16389
|
|
109
110
|
trilogy/scripts/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
110
111
|
trilogy/scripts/trilogy.py,sha256=1L0XrH4mVHRt1C9T1HnaDv2_kYEfbWTb5_-cBBke79w,3774
|
|
111
112
|
trilogy/std/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -116,8 +117,8 @@ trilogy/std/money.preql,sha256=XWwvAV3WxBsHX9zfptoYRnBigcfYwrYtBHXTME0xJuQ,2082
|
|
|
116
117
|
trilogy/std/net.preql,sha256=WZCuvH87_rZntZiuGJMmBDMVKkdhTtxeHOkrXNwJ1EE,416
|
|
117
118
|
trilogy/std/ranking.preql,sha256=LDoZrYyz4g3xsII9XwXfmstZD-_92i1Eox1UqkBIfi8,83
|
|
118
119
|
trilogy/std/report.preql,sha256=LbV-XlHdfw0jgnQ8pV7acG95xrd1-p65fVpiIc-S7W4,202
|
|
119
|
-
pytrilogy-0.0.3.
|
|
120
|
-
pytrilogy-0.0.3.
|
|
121
|
-
pytrilogy-0.0.3.
|
|
122
|
-
pytrilogy-0.0.3.
|
|
123
|
-
pytrilogy-0.0.3.
|
|
120
|
+
pytrilogy-0.0.3.97.dist-info/METADATA,sha256=t-pwxx9XVd6oSuAMdPfTDHFaY7AevCESieu_mK6YrcU,11683
|
|
121
|
+
pytrilogy-0.0.3.97.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
|
|
122
|
+
pytrilogy-0.0.3.97.dist-info/entry_points.txt,sha256=ewBPU2vLnVexZVnB-NrVj-p3E-4vukg83Zk8A55Wp2w,56
|
|
123
|
+
pytrilogy-0.0.3.97.dist-info/top_level.txt,sha256=cAy__NW_eMAa_yT9UnUNlZLFfxcg6eimUAZ184cdNiE,8
|
|
124
|
+
pytrilogy-0.0.3.97.dist-info/RECORD,,
|
trilogy/__init__.py
CHANGED
trilogy/authoring/__init__.py
CHANGED
|
@@ -60,68 +60,82 @@ from trilogy.core.statements.author import (
|
|
|
60
60
|
RowsetDerivationStatement,
|
|
61
61
|
SelectItem,
|
|
62
62
|
SelectStatement,
|
|
63
|
+
ShowCategory,
|
|
64
|
+
ShowStatement,
|
|
65
|
+
ValidateStatement,
|
|
63
66
|
)
|
|
64
67
|
from trilogy.parsing.common import arbitrary_to_concept, arg_to_datatype
|
|
65
68
|
|
|
66
69
|
__all__ = [
|
|
67
|
-
|
|
68
|
-
"
|
|
69
|
-
|
|
70
|
-
"
|
|
71
|
-
"
|
|
72
|
-
"
|
|
73
|
-
"CaseElse",
|
|
74
|
-
"AggregateWrapper",
|
|
75
|
-
"WindowItem",
|
|
76
|
-
"WindowOrder",
|
|
77
|
-
"WindowType",
|
|
78
|
-
"WindowItemOrder",
|
|
79
|
-
"WindowItemOver",
|
|
80
|
-
"DataType",
|
|
81
|
-
"StructType",
|
|
82
|
-
"ArrayType",
|
|
83
|
-
"NumericType",
|
|
84
|
-
"Grain",
|
|
85
|
-
"RowsetDerivationStatement",
|
|
86
|
-
"MapType",
|
|
87
|
-
"ListWrapper",
|
|
70
|
+
# trilogy.constants
|
|
71
|
+
"DEFAULT_NAMESPACE",
|
|
72
|
+
# trilogy.core.enums
|
|
73
|
+
"BooleanOperator",
|
|
74
|
+
"ComparisonOperator",
|
|
75
|
+
"FunctionClass",
|
|
88
76
|
"FunctionType",
|
|
77
|
+
"InfiniteFunctionArgs",
|
|
78
|
+
"Ordering",
|
|
79
|
+
"Purpose",
|
|
80
|
+
# trilogy.core.functions
|
|
89
81
|
"FunctionFactory",
|
|
90
|
-
|
|
91
|
-
"
|
|
92
|
-
"
|
|
93
|
-
"
|
|
94
|
-
"
|
|
82
|
+
# trilogy.core.models.author
|
|
83
|
+
"AggregateWrapper",
|
|
84
|
+
"CaseElse",
|
|
85
|
+
"CaseWhen",
|
|
86
|
+
"Comparison",
|
|
87
|
+
"Concept",
|
|
95
88
|
"ConceptRef",
|
|
89
|
+
"Conditional",
|
|
90
|
+
"FilterItem",
|
|
91
|
+
"Function",
|
|
92
|
+
"FunctionCallWrapper",
|
|
96
93
|
"HavingClause",
|
|
97
94
|
"MagicConstants",
|
|
98
95
|
"Metadata",
|
|
96
|
+
"MultiSelectLineage",
|
|
99
97
|
"OrderBy",
|
|
100
98
|
"OrderItem",
|
|
101
99
|
"Parenthetical",
|
|
100
|
+
"RowsetItem",
|
|
102
101
|
"SubselectComparison",
|
|
103
|
-
"
|
|
104
|
-
"
|
|
105
|
-
"
|
|
106
|
-
"
|
|
107
|
-
"
|
|
108
|
-
"
|
|
109
|
-
|
|
110
|
-
"
|
|
111
|
-
"
|
|
112
|
-
"
|
|
113
|
-
"
|
|
114
|
-
"
|
|
115
|
-
"
|
|
116
|
-
"
|
|
102
|
+
"WhereClause",
|
|
103
|
+
"WindowItem",
|
|
104
|
+
"WindowItemOrder",
|
|
105
|
+
"WindowItemOver",
|
|
106
|
+
"WindowOrder",
|
|
107
|
+
"WindowType",
|
|
108
|
+
# trilogy.core.models.core
|
|
109
|
+
"ArrayType",
|
|
110
|
+
"DataType",
|
|
111
|
+
"ListWrapper",
|
|
112
|
+
"MapType",
|
|
113
|
+
"NumericType",
|
|
114
|
+
"StructType",
|
|
115
|
+
"TraitDataType",
|
|
116
|
+
# trilogy.core.models.datasource
|
|
117
|
+
"Address",
|
|
117
118
|
"Datasource",
|
|
118
119
|
"DatasourceMetadata",
|
|
119
|
-
|
|
120
|
-
"
|
|
121
|
-
|
|
120
|
+
# trilogy.core.models.environment
|
|
121
|
+
"Environment",
|
|
122
|
+
# trilogy.core.statements.author
|
|
123
|
+
"ConceptDeclarationStatement",
|
|
124
|
+
"ConceptTransform",
|
|
122
125
|
"CopyStatement",
|
|
126
|
+
"Grain",
|
|
123
127
|
"HasUUID",
|
|
124
128
|
"ImportStatement",
|
|
125
|
-
"
|
|
126
|
-
"
|
|
129
|
+
"MultiSelectStatement",
|
|
130
|
+
"PersistStatement",
|
|
131
|
+
"RawSQLStatement",
|
|
132
|
+
"RowsetDerivationStatement",
|
|
133
|
+
"SelectItem",
|
|
134
|
+
"SelectStatement",
|
|
135
|
+
"ShowCategory",
|
|
136
|
+
"ShowStatement",
|
|
137
|
+
"ValidateStatement",
|
|
138
|
+
# trilogy.parsing.common
|
|
139
|
+
"arbitrary_to_concept",
|
|
140
|
+
"arg_to_datatype",
|
|
127
141
|
]
|
trilogy/constants.py
CHANGED
trilogy/core/enums.py
CHANGED
|
@@ -82,6 +82,15 @@ class Modifier(Enum):
|
|
|
82
82
|
return Modifier.NULLABLE
|
|
83
83
|
return super()._missing_(value=strval.capitalize())
|
|
84
84
|
|
|
85
|
+
def __lt__(self, other):
|
|
86
|
+
order = [
|
|
87
|
+
Modifier.HIDDEN,
|
|
88
|
+
Modifier.PARTIAL,
|
|
89
|
+
Modifier.NULLABLE,
|
|
90
|
+
Modifier.OPTIONAL,
|
|
91
|
+
]
|
|
92
|
+
return order.index(self) < order.index(other)
|
|
93
|
+
|
|
85
94
|
|
|
86
95
|
class JoinType(Enum):
|
|
87
96
|
INNER = "inner"
|
trilogy/core/exceptions.py
CHANGED
|
@@ -1,4 +1,15 @@
|
|
|
1
|
-
from
|
|
1
|
+
from dataclasses import dataclass
|
|
2
|
+
from typing import Any, List, Sequence
|
|
3
|
+
|
|
4
|
+
from trilogy.core.enums import Modifier
|
|
5
|
+
from trilogy.core.models.core import (
|
|
6
|
+
ArrayType,
|
|
7
|
+
DataType,
|
|
8
|
+
MapType,
|
|
9
|
+
NumericType,
|
|
10
|
+
StructType,
|
|
11
|
+
TraitDataType,
|
|
12
|
+
)
|
|
2
13
|
|
|
3
14
|
|
|
4
15
|
class UndefinedConceptException(Exception):
|
|
@@ -29,7 +40,7 @@ class ModelValidationError(Exception):
|
|
|
29
40
|
self,
|
|
30
41
|
message,
|
|
31
42
|
children: Sequence["ModelValidationError"] | None = None,
|
|
32
|
-
**kwargs
|
|
43
|
+
**kwargs,
|
|
33
44
|
):
|
|
34
45
|
super().__init__(self, message, **kwargs)
|
|
35
46
|
self.message = message
|
|
@@ -40,6 +51,49 @@ class DatasourceModelValidationError(ModelValidationError):
|
|
|
40
51
|
pass
|
|
41
52
|
|
|
42
53
|
|
|
54
|
+
class DatasourceGrainValidationError(DatasourceModelValidationError):
|
|
55
|
+
pass
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
@dataclass
|
|
59
|
+
class DatasourceColumnBindingData:
|
|
60
|
+
address: str
|
|
61
|
+
value: Any
|
|
62
|
+
value_type: (
|
|
63
|
+
DataType | ArrayType | StructType | MapType | NumericType | TraitDataType
|
|
64
|
+
)
|
|
65
|
+
value_modifiers: List[Modifier]
|
|
66
|
+
actual_type: (
|
|
67
|
+
DataType | ArrayType | StructType | MapType | NumericType | TraitDataType
|
|
68
|
+
)
|
|
69
|
+
actual_modifiers: List[Modifier]
|
|
70
|
+
|
|
71
|
+
def format_failure(self):
|
|
72
|
+
return f"Concept {self.address} value '{self.value}' with type {self.value_modifiers} does not conform to expected type {str(self.actual_type)} with modifiers {self.actual_modifiers}"
|
|
73
|
+
|
|
74
|
+
def is_modifier_issue(self) -> bool:
|
|
75
|
+
return len(self.value_modifiers) > 0 and any(
|
|
76
|
+
[x not in self.actual_modifiers for x in self.value_modifiers]
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
def is_type_issue(self) -> bool:
|
|
80
|
+
return self.value_type != self.actual_type
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
class DatasourceColumnBindingError(DatasourceModelValidationError):
|
|
84
|
+
def __init__(
|
|
85
|
+
self,
|
|
86
|
+
address: str,
|
|
87
|
+
errors: list[DatasourceColumnBindingData],
|
|
88
|
+
message: str | None = None,
|
|
89
|
+
):
|
|
90
|
+
if not message:
|
|
91
|
+
message = f"Datasource {address} failed validation. Found rows that do not conform to types: {[failure.format_failure() for failure in errors]}"
|
|
92
|
+
super().__init__(message)
|
|
93
|
+
self.errors = errors
|
|
94
|
+
self.dataset_address = address
|
|
95
|
+
|
|
96
|
+
|
|
43
97
|
class ConceptModelValidationError(ModelValidationError):
|
|
44
98
|
pass
|
|
45
99
|
|
trilogy/core/graph_models.py
CHANGED
|
@@ -64,13 +64,13 @@ def datasource_to_node(input: BuildDatasource) -> str:
|
|
|
64
64
|
|
|
65
65
|
|
|
66
66
|
class ReferenceGraph(nx.DiGraph):
|
|
67
|
-
def __init__(self, *args, **kwargs):
|
|
67
|
+
def __init__(self, *args, **kwargs) -> None:
|
|
68
68
|
super().__init__(*args, **kwargs)
|
|
69
69
|
self.concepts: dict[str, BuildConcept] = {}
|
|
70
70
|
self.datasources: dict[str, BuildDatasource] = {}
|
|
71
71
|
self.pseudonyms: set[tuple[str, str]] = set()
|
|
72
72
|
|
|
73
|
-
def copy(self):
|
|
73
|
+
def copy(self) -> "ReferenceGraph":
|
|
74
74
|
g = ReferenceGraph()
|
|
75
75
|
g.concepts = self.concepts.copy()
|
|
76
76
|
g.datasources = self.datasources.copy()
|
|
@@ -83,7 +83,7 @@ class ReferenceGraph(nx.DiGraph):
|
|
|
83
83
|
# g.add_edges_from(self.edges(data=True))
|
|
84
84
|
return g
|
|
85
85
|
|
|
86
|
-
def remove_node(self, n):
|
|
86
|
+
def remove_node(self, n) -> None:
|
|
87
87
|
if n in self.concepts:
|
|
88
88
|
del self.concepts[n]
|
|
89
89
|
if n in self.datasources:
|
|
@@ -98,7 +98,7 @@ class ReferenceGraph(nx.DiGraph):
|
|
|
98
98
|
self.datasources[node_name] = attr["datasource"]
|
|
99
99
|
super().add_node(node_name, **attr)
|
|
100
100
|
|
|
101
|
-
def add_datasource_node(self, node_name, datasource):
|
|
101
|
+
def add_datasource_node(self, node_name, datasource) -> None:
|
|
102
102
|
self.datasources[node_name] = datasource
|
|
103
103
|
super().add_node(node_name, datasource=datasource)
|
|
104
104
|
|
|
@@ -2,13 +2,25 @@ from dataclasses import dataclass
|
|
|
2
2
|
from enum import Enum
|
|
3
3
|
|
|
4
4
|
from trilogy import Environment
|
|
5
|
-
from trilogy.authoring import
|
|
5
|
+
from trilogy.authoring import (
|
|
6
|
+
ConceptRef,
|
|
7
|
+
DataType,
|
|
8
|
+
Ordering,
|
|
9
|
+
Purpose,
|
|
10
|
+
)
|
|
11
|
+
from trilogy.constants import MagicConstants
|
|
12
|
+
from trilogy.core.enums import ComparisonOperator, FunctionType
|
|
6
13
|
from trilogy.core.exceptions import ModelValidationError
|
|
7
14
|
from trilogy.core.models.build import (
|
|
15
|
+
BuildCaseElse,
|
|
16
|
+
BuildCaseWhen,
|
|
8
17
|
BuildComparison,
|
|
9
18
|
BuildConcept,
|
|
10
19
|
BuildConditional,
|
|
11
20
|
BuildDatasource,
|
|
21
|
+
BuildFunction,
|
|
22
|
+
BuildOrderBy,
|
|
23
|
+
BuildOrderItem,
|
|
12
24
|
)
|
|
13
25
|
from trilogy.core.models.environment import EnvironmentConceptDict
|
|
14
26
|
from trilogy.core.models.execute import (
|
|
@@ -27,7 +39,8 @@ class ExpectationType(Enum):
|
|
|
27
39
|
@dataclass
|
|
28
40
|
class ValidationTest:
|
|
29
41
|
check_type: ExpectationType
|
|
30
|
-
|
|
42
|
+
raw_query: ProcessedQuery | None = None
|
|
43
|
+
generated_query: str | None = None
|
|
31
44
|
expected: str | None = None
|
|
32
45
|
result: ModelValidationError | None = None
|
|
33
46
|
ran: bool = True
|
|
@@ -38,6 +51,32 @@ class ValidationType(Enum):
|
|
|
38
51
|
CONCEPTS = "concepts"
|
|
39
52
|
|
|
40
53
|
|
|
54
|
+
def build_order_args(concepts: list[BuildConcept]) -> list[BuildFunction]:
|
|
55
|
+
order_args = []
|
|
56
|
+
for concept in concepts:
|
|
57
|
+
order_args.append(
|
|
58
|
+
BuildFunction(
|
|
59
|
+
operator=FunctionType.CASE,
|
|
60
|
+
arguments=[
|
|
61
|
+
BuildCaseWhen(
|
|
62
|
+
comparison=BuildComparison(
|
|
63
|
+
left=concept,
|
|
64
|
+
operator=ComparisonOperator.IS,
|
|
65
|
+
right=MagicConstants.NULL,
|
|
66
|
+
),
|
|
67
|
+
expr=1,
|
|
68
|
+
),
|
|
69
|
+
BuildCaseElse(expr=0),
|
|
70
|
+
],
|
|
71
|
+
output_data_type=DataType.INTEGER,
|
|
72
|
+
output_purpose=Purpose.PROPERTY,
|
|
73
|
+
arg_count=2,
|
|
74
|
+
)
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
return order_args
|
|
78
|
+
|
|
79
|
+
|
|
41
80
|
def easy_query(
|
|
42
81
|
concepts: list[BuildConcept],
|
|
43
82
|
datasource: BuildDatasource,
|
|
@@ -80,7 +119,6 @@ def easy_query(
|
|
|
80
119
|
group_to_grain=True,
|
|
81
120
|
base_alias_override=datasource.safe_identifier,
|
|
82
121
|
)
|
|
83
|
-
|
|
84
122
|
filter_cte = CTE(
|
|
85
123
|
name=f"datasource_{datasource.name}_filter",
|
|
86
124
|
source=QueryDatasource(
|
|
@@ -99,6 +137,20 @@ def easy_query(
|
|
|
99
137
|
grain=cte.grain,
|
|
100
138
|
condition=condition,
|
|
101
139
|
limit=limit,
|
|
140
|
+
order_by=BuildOrderBy(
|
|
141
|
+
items=[
|
|
142
|
+
BuildOrderItem(
|
|
143
|
+
expr=BuildFunction(
|
|
144
|
+
operator=FunctionType.SUM,
|
|
145
|
+
arguments=build_order_args(concepts),
|
|
146
|
+
output_data_type=DataType.INTEGER,
|
|
147
|
+
output_purpose=Purpose.PROPERTY,
|
|
148
|
+
arg_count=len(concepts),
|
|
149
|
+
),
|
|
150
|
+
order=Ordering.DESCENDING,
|
|
151
|
+
)
|
|
152
|
+
]
|
|
153
|
+
),
|
|
102
154
|
)
|
|
103
155
|
|
|
104
156
|
return ProcessedQuery(
|