pytrilogy 0.0.1.117__tar.gz → 0.0.2.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.

Potentially problematic release.


This version of pytrilogy might be problematic. Click here for more details.

Files changed (111) hide show
  1. {pytrilogy-0.0.1.117/pytrilogy.egg-info → pytrilogy-0.0.2.1}/PKG-INFO +1 -1
  2. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1/pytrilogy.egg-info}/PKG-INFO +1 -1
  3. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/pytrilogy.egg-info/SOURCES.txt +0 -1
  4. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/tests/test_discovery_nodes.py +10 -2
  5. pytrilogy-0.0.2.1/tests/test_environment.py +64 -0
  6. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/tests/test_models.py +1 -1
  7. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/tests/test_parsing.py +10 -0
  8. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/tests/test_query_processing.py +1 -1
  9. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/__init__.py +1 -1
  10. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/constants.py +6 -0
  11. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/enums.py +7 -2
  12. pytrilogy-0.0.2.1/trilogy/core/env_processor.py +63 -0
  13. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/functions.py +11 -0
  14. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/models.py +737 -146
  15. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/optimization.py +31 -28
  16. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/optimizations/inline_constant.py +4 -1
  17. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/optimizations/inline_datasource.py +25 -4
  18. pytrilogy-0.0.2.1/trilogy/core/optimizations/predicate_pushdown.py +145 -0
  19. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/concept_strategies_v3.py +69 -39
  20. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/graph_utils.py +3 -3
  21. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/node_generators/__init__.py +0 -2
  22. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/node_generators/basic_node.py +30 -17
  23. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/node_generators/filter_node.py +3 -1
  24. pytrilogy-0.0.2.1/trilogy/core/processing/node_generators/node_merge_node.py +418 -0
  25. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/node_generators/rowset_node.py +18 -16
  26. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/node_generators/select_node.py +44 -83
  27. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/nodes/__init__.py +2 -0
  28. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/nodes/base_node.py +22 -5
  29. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/nodes/filter_node.py +3 -0
  30. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/nodes/group_node.py +20 -2
  31. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/nodes/merge_node.py +32 -18
  32. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/nodes/select_node_v2.py +17 -3
  33. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/utility.py +100 -8
  34. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/query_processor.py +77 -24
  35. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/dialect/base.py +11 -46
  36. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/dialect/bigquery.py +1 -1
  37. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/dialect/common.py +11 -0
  38. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/dialect/duckdb.py +1 -1
  39. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/dialect/presto.py +1 -0
  40. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/executor.py +29 -0
  41. pytrilogy-0.0.2.1/trilogy/hooks/graph_hook.py +69 -0
  42. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/hooks/query_debugger.py +1 -0
  43. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/parsing/common.py +8 -5
  44. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/parsing/parse_engine.py +48 -27
  45. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/parsing/render.py +13 -6
  46. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/parsing/trilogy.lark +12 -7
  47. pytrilogy-0.0.1.117/tests/test_environment.py +0 -22
  48. pytrilogy-0.0.1.117/trilogy/core/env_processor.py +0 -39
  49. pytrilogy-0.0.1.117/trilogy/core/optimizations/predicate_pushdown.py +0 -105
  50. pytrilogy-0.0.1.117/trilogy/core/processing/node_generators/concept_merge_node.py +0 -214
  51. pytrilogy-0.0.1.117/trilogy/core/processing/node_generators/node_merge_node.py +0 -169
  52. pytrilogy-0.0.1.117/trilogy/hooks/graph_hook.py +0 -24
  53. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/LICENSE.md +0 -0
  54. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/README.md +0 -0
  55. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/pyproject.toml +0 -0
  56. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/pytrilogy.egg-info/dependency_links.txt +0 -0
  57. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/pytrilogy.egg-info/entry_points.txt +0 -0
  58. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/pytrilogy.egg-info/requires.txt +0 -0
  59. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/pytrilogy.egg-info/top_level.txt +0 -0
  60. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/setup.cfg +0 -0
  61. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/setup.py +0 -0
  62. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/tests/test_datatypes.py +0 -0
  63. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/tests/test_declarations.py +0 -0
  64. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/tests/test_derived_concepts.py +0 -0
  65. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/tests/test_functions.py +0 -0
  66. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/tests/test_imports.py +0 -0
  67. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/tests/test_metadata.py +0 -0
  68. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/tests/test_multi_join_assignments.py +0 -0
  69. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/tests/test_partial_handling.py +0 -0
  70. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/tests/test_select.py +0 -0
  71. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/tests/test_statements.py +0 -0
  72. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/tests/test_undefined_concept.py +0 -0
  73. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/tests/test_where_clause.py +0 -0
  74. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/compiler.py +0 -0
  75. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/__init__.py +0 -0
  76. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/constants.py +0 -0
  77. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/environment_helpers.py +0 -0
  78. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/ergonomics.py +0 -0
  79. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/exceptions.py +0 -0
  80. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/graph_models.py +0 -0
  81. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/internal.py +0 -0
  82. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/optimizations/__init__.py +0 -0
  83. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/optimizations/base_optimization.py +0 -0
  84. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/__init__.py +0 -0
  85. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/node_generators/common.py +0 -0
  86. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/node_generators/group_node.py +0 -0
  87. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
  88. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
  89. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
  90. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/node_generators/window_node.py +0 -0
  91. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/nodes/unnest_node.py +0 -0
  92. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/core/processing/nodes/window_node.py +0 -0
  93. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/dialect/__init__.py +0 -0
  94. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/dialect/config.py +0 -0
  95. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/dialect/enums.py +0 -0
  96. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/dialect/postgres.py +0 -0
  97. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/dialect/snowflake.py +0 -0
  98. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/dialect/sql_server.py +0 -0
  99. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/engine.py +0 -0
  100. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/hooks/__init__.py +0 -0
  101. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/hooks/base_hook.py +0 -0
  102. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/metadata/__init__.py +0 -0
  103. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/parser.py +0 -0
  104. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/parsing/__init__.py +0 -0
  105. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/parsing/config.py +0 -0
  106. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/parsing/exceptions.py +0 -0
  107. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/parsing/helpers.py +0 -0
  108. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/py.typed +0 -0
  109. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/scripts/__init__.py +0 -0
  110. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/scripts/trilogy.py +0 -0
  111. {pytrilogy-0.0.1.117 → pytrilogy-0.0.2.1}/trilogy/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytrilogy
3
- Version: 0.0.1.117
3
+ Version: 0.0.2.1
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: pytrilogy
3
- Version: 0.0.1.117
3
+ Version: 0.0.2.1
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -58,7 +58,6 @@ trilogy/core/processing/utility.py
58
58
  trilogy/core/processing/node_generators/__init__.py
59
59
  trilogy/core/processing/node_generators/basic_node.py
60
60
  trilogy/core/processing/node_generators/common.py
61
- trilogy/core/processing/node_generators/concept_merge_node.py
62
61
  trilogy/core/processing/node_generators/filter_node.py
63
62
  trilogy/core/processing/node_generators/group_node.py
64
63
  trilogy/core/processing/node_generators/group_to_node.py
@@ -39,7 +39,11 @@ def test_group_node_property(test_environment: Environment, test_environment_gra
39
39
  source_concepts=search_concepts,
40
40
  depth=0,
41
41
  )
42
- input_concept_names = {x.name for x in group_node.parents[0].output_concepts}
42
+ input_concept_names = {
43
+ x.name
44
+ for x in group_node.parents[0].output_concepts
45
+ if x not in group_node.parents[0].hidden_concepts
46
+ }
43
47
  assert input_concept_names == {"category_name_length", "category_id"}
44
48
  # assert len(input_concept.grain.components) == 1
45
49
  # assert input_concept.grain.components[0].name == "category_id"
@@ -60,7 +64,11 @@ def test_group_node_property_all(test_environment: Environment, test_environment
60
64
  source_concepts=search_concepts,
61
65
  depth=0,
62
66
  )
63
- input_concept_names = {x.name for x in group_node.parents[0].output_concepts}
67
+ input_concept_names = {
68
+ x.name
69
+ for x in group_node.parents[0].output_concepts
70
+ if x not in group_node.parents[0].hidden_concepts
71
+ }
64
72
  assert input_concept_names == {"category_name_length", "category_id"}
65
73
  final = group_node.resolve()
66
74
  assert len(final.datasources) == 1
@@ -0,0 +1,64 @@
1
+ from trilogy.core.models import Environment
2
+ from pathlib import Path
3
+ from trilogy.core.enums import Modifier
4
+
5
+
6
+ def test_environment_serialization(test_environment: Environment):
7
+ str(test_environment)
8
+ path = test_environment.to_cache()
9
+
10
+ test_environment2 = Environment.from_cache(path)
11
+ assert test_environment2
12
+
13
+ assert test_environment.concepts == test_environment2.concepts
14
+ assert test_environment.datasources == test_environment2.datasources
15
+ for k, v in test_environment.concepts.items():
16
+ assert v == test_environment2.concepts[k]
17
+
18
+
19
+ def test_environment_from_path():
20
+
21
+ env = Environment.from_file(Path(__file__).parent / "test_env.preql")
22
+
23
+ assert "id" in env.concepts
24
+
25
+
26
+ def test_environment_merge():
27
+ env1: Environment
28
+ env1, _ = Environment().parse(
29
+ """
30
+ key order_id int;
31
+
32
+ datasource orders
33
+ (order_id:order_id)
34
+ grain (order_id)
35
+ address orders;
36
+ """
37
+ )
38
+
39
+ env2, _ = Environment().parse(
40
+ """
41
+ key order_id int;
42
+
43
+ datasource replacements
44
+ (order_id:order_id)
45
+ grain (order_id)
46
+ address replacements;
47
+
48
+
49
+ """
50
+ )
51
+
52
+ env1.add_import("replacements", env2)
53
+
54
+ _ = env1.merge_concept(
55
+ env1.concepts["replacements.order_id"],
56
+ env1.concepts["order_id"],
57
+ modifiers=[Modifier.PARTIAL],
58
+ )
59
+
60
+ assert env1.concepts["order_id"] == env1.concepts["replacements.order_id"]
61
+
62
+ order_id = env1.datasources["replacements.replacements"].columns[0]
63
+ assert order_id.concept == env1.concepts["order_id"]
64
+ assert order_id.modifiers == [Modifier.PARTIAL]
@@ -132,7 +132,7 @@ def test_undefined(test_environment: Environment):
132
132
  environment=test_environment.concepts,
133
133
  )
134
134
 
135
- y = x.with_select_grain(Grain(components=[test_environment.concepts["order_id"]]))
135
+ y = x.with_select_context(Grain(components=[test_environment.concepts["order_id"]]))
136
136
 
137
137
  assert y.grain == Grain(components=[test_environment.concepts["order_id"]])
138
138
 
@@ -15,6 +15,7 @@ from trilogy.parsing.parse_engine import (
15
15
  )
16
16
  from trilogy.constants import MagicConstants
17
17
  from trilogy.dialect.base import BaseDialect
18
+ from trilogy.core.enums import BooleanOperator
18
19
 
19
20
 
20
21
  def test_in():
@@ -127,6 +128,15 @@ def test_show(test_environment):
127
128
  )
128
129
 
129
130
 
131
+ def test_conditional(test_environment):
132
+ _, parsed = parse_text(
133
+ "const order_id <- 4; SELECT order_id WHERE order_id =4 and order_id = 10;"
134
+ )
135
+ query = parsed[-1]
136
+ assert isinstance(query, SelectStatement)
137
+ assert query.where_clause.conditional.operator == BooleanOperator.AND
138
+
139
+
130
140
  def test_as_transform(test_environment):
131
141
  _, parsed = parse_text("const order_id <- 4; SELECT order_id as new_order_id;")
132
142
  query = parsed[-1]
@@ -134,7 +134,7 @@ def test_query_aggregation(test_environment, test_environment_graph):
134
134
  environment=test_environment, graph=test_environment_graph, statement=select
135
135
  )
136
136
 
137
- assert {datasource.identifier} == {"revenue_at_local_order_id_at_abstract"}
137
+ assert {datasource.identifier} == {"local_revenue_at_local_order_id_at_abstract"}
138
138
  check = datasource
139
139
  assert len(check.input_concepts) == 2
140
140
  assert check.input_concepts[0].name == "revenue"
@@ -4,6 +4,6 @@ from trilogy.executor import Executor
4
4
  from trilogy.parser import parse
5
5
  from trilogy.constants import CONFIG
6
6
 
7
- __version__ = "0.0.1.117"
7
+ __version__ = "0.0.2.1"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -1,6 +1,7 @@
1
1
  from logging import getLogger
2
2
  from dataclasses import dataclass, field
3
3
  from enum import Enum
4
+ import random
4
5
 
5
6
  logger = getLogger("trilogy")
6
7
 
@@ -34,7 +35,12 @@ class Config:
34
35
  validate_missing: bool = True
35
36
  optimizations: Optimizations = field(default_factory=Optimizations)
36
37
 
38
+ def set_random_seed(self, seed: int):
39
+ random.seed(seed)
40
+
37
41
 
38
42
  CONFIG = Config()
39
43
 
44
+ CONFIG.set_random_seed(42)
45
+
40
46
  CONFIG.strict_mode = True
@@ -43,7 +43,6 @@ class PurposeLineage(Enum):
43
43
  ROOT = "root"
44
44
  ROWSET = "rowset"
45
45
  MULTISELECT = "multiselect"
46
- MERGE = "merge"
47
46
 
48
47
 
49
48
  class Granularity(Enum):
@@ -263,7 +262,6 @@ class DatePart(Enum):
263
262
  class SourceType(Enum):
264
263
  FILTER = "filter"
265
264
  SELECT = "select"
266
- MERGE = "merge"
267
265
  ABSTRACT = "abstract"
268
266
  DIRECT_SELECT = "direct_select"
269
267
  GROUP = "group"
@@ -271,8 +269,15 @@ class SourceType(Enum):
271
269
  UNNEST = "unnest"
272
270
  CONSTANT = "constant"
273
271
  ROWSET = "rowset"
272
+ MERGE = "merge"
274
273
 
275
274
 
276
275
  class ShowCategory(Enum):
277
276
  MODELS = "models"
278
277
  CONCEPTS = "concepts"
278
+
279
+
280
+ class SelectFiltering(Enum):
281
+ NONE = "none"
282
+ EXPLICIT = "explicit" # the filtering contains only selected values
283
+ IMPLICIT = "implicit" # the filtering contains unselected values
@@ -0,0 +1,63 @@
1
+ from trilogy.core.graph_models import (
2
+ ReferenceGraph,
3
+ concept_to_node,
4
+ datasource_to_node,
5
+ )
6
+ from trilogy.core.models import Environment, Concept, Datasource
7
+
8
+
9
+ def add_concept(concept: Concept, g: ReferenceGraph):
10
+ g.add_node(concept)
11
+ # if we have sources, recursively add them
12
+ node_name = concept_to_node(concept)
13
+ if concept.sources:
14
+ for source in concept.sources:
15
+ generic = source.with_default_grain()
16
+ g.add_edge(generic, node_name)
17
+ for _, pseudonym in concept.pseudonyms.items():
18
+ pseudonym = pseudonym.with_default_grain()
19
+ pseudonym_node = concept_to_node(pseudonym)
20
+ if pseudonym_node.split("@")[0] == node_name.split("@")[0]:
21
+ continue
22
+ g.add_edge(pseudonym_node, node_name, pseudonym=True)
23
+ g.add_edge(node_name, pseudonym_node, pseudonym=True)
24
+ add_concept(pseudonym, g)
25
+
26
+
27
+ def generate_adhoc_graph(
28
+ concepts: list[Concept],
29
+ datasources: list[Datasource],
30
+ restrict_to_listed: bool = False,
31
+ ) -> ReferenceGraph:
32
+ g = ReferenceGraph()
33
+
34
+ # add all parsed concepts
35
+ for concept in concepts:
36
+ add_concept(concept, g)
37
+
38
+ for dataset in datasources:
39
+ node = datasource_to_node(dataset)
40
+ g.add_node(dataset, type="datasource", datasource=dataset)
41
+ for concept in dataset.concepts:
42
+ if restrict_to_listed:
43
+ if concept_to_node(concept) not in g.nodes:
44
+ continue
45
+ g.add_edge(node, concept)
46
+ g.add_edge(concept, node)
47
+ # if there is a key on a table at a different grain
48
+ # add an FK edge to the canonical source, if it exists
49
+ # for example, order ID on order product table
50
+ default = concept.with_default_grain()
51
+ if concept != default:
52
+ g.add_edge(concept, default)
53
+ g.add_edge(default, concept)
54
+ return g
55
+
56
+
57
+ def generate_graph(
58
+ environment: Environment,
59
+ ) -> ReferenceGraph:
60
+
61
+ return generate_adhoc_graph(
62
+ list(environment.concepts.values()), list(environment.datasources.values())
63
+ )
@@ -52,12 +52,22 @@ def argument_to_purpose(arg) -> Purpose:
52
52
  if isinstance(arg, Function):
53
53
  return arg.output_purpose
54
54
  elif isinstance(arg, AggregateWrapper):
55
+ base = arg.function.output_purpose
56
+ if arg.by and base == Purpose.METRIC:
57
+ return Purpose.PROPERTY
55
58
  return arg.function.output_purpose
56
59
  elif isinstance(arg, Parenthetical):
57
60
  return argument_to_purpose(arg.content)
58
61
  elif isinstance(arg, WindowItem):
59
62
  return Purpose.PROPERTY
60
63
  elif isinstance(arg, Concept):
64
+ base = arg.purpose
65
+ if (
66
+ isinstance(arg.lineage, AggregateWrapper)
67
+ and arg.lineage.by
68
+ and base == Purpose.METRIC
69
+ ):
70
+ return Purpose.PROPERTY
61
71
  return arg.purpose
62
72
  elif isinstance(arg, (int, float, str, bool, list, NumericType, DataType)):
63
73
  return Purpose.CONSTANT
@@ -153,6 +163,7 @@ def Max(args: list[Concept]) -> Function:
153
163
  DataType.DATE,
154
164
  DataType.DATETIME,
155
165
  DataType.TIMESTAMP,
166
+ DataType.BOOL,
156
167
  },
157
168
  arg_count=1,
158
169
  # output_grain=Grain(components=arguments),