pytrilogy 0.0.3.92__tar.gz → 0.0.3.94__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 (152) hide show
  1. {pytrilogy-0.0.3.92/pytrilogy.egg-info → pytrilogy-0.0.3.94}/PKG-INFO +1 -1
  2. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94/pytrilogy.egg-info}/PKG-INFO +1 -1
  3. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_discovery_nodes.py +1 -1
  4. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_parsing.py +2 -3
  5. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_query_processing.py +1 -1
  6. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/__init__.py +1 -1
  7. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/env_processor.py +4 -2
  8. pytrilogy-0.0.3.94/trilogy/core/graph_models.py +132 -0
  9. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/models/author.py +17 -26
  10. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/models/build.py +141 -151
  11. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/models/build_environment.py +2 -6
  12. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/models/execute.py +3 -3
  13. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/node_generators/group_node.py +3 -7
  14. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/node_generators/node_merge_node.py +30 -28
  15. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/node_generators/select_helpers/datasource_injection.py +25 -11
  16. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/node_generators/select_merge_node.py +66 -80
  17. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/parsing/parse_engine.py +1 -1
  18. pytrilogy-0.0.3.92/trilogy/core/graph_models.py +0 -113
  19. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/LICENSE.md +0 -0
  20. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/README.md +0 -0
  21. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/pyproject.toml +0 -0
  22. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/pytrilogy.egg-info/SOURCES.txt +0 -0
  23. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/pytrilogy.egg-info/dependency_links.txt +0 -0
  24. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/pytrilogy.egg-info/entry_points.txt +0 -0
  25. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/pytrilogy.egg-info/requires.txt +0 -0
  26. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/pytrilogy.egg-info/top_level.txt +0 -0
  27. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/setup.cfg +0 -0
  28. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/setup.py +0 -0
  29. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_datatypes.py +0 -0
  30. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_declarations.py +0 -0
  31. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_derived_concepts.py +0 -0
  32. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_enums.py +0 -0
  33. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_environment.py +0 -0
  34. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_execute_models.py +0 -0
  35. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_executor.py +0 -0
  36. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_failure.py +0 -0
  37. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_functions.py +0 -0
  38. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_imports.py +0 -0
  39. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_metadata.py +0 -0
  40. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_models.py +0 -0
  41. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_multi_join_assignments.py +0 -0
  42. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_parse_engine.py +0 -0
  43. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_parsing_failures.py +0 -0
  44. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_partial_handling.py +0 -0
  45. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_query_render.py +0 -0
  46. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_select.py +0 -0
  47. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_show.py +0 -0
  48. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_statements.py +0 -0
  49. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_typing.py +0 -0
  50. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_undefined_concept.py +0 -0
  51. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_user_functions.py +0 -0
  52. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/tests/test_where_clause.py +0 -0
  53. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/authoring/__init__.py +0 -0
  54. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/compiler.py +0 -0
  55. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/constants.py +0 -0
  56. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/__init__.py +0 -0
  57. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/constants.py +0 -0
  58. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/enums.py +0 -0
  59. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/environment_helpers.py +0 -0
  60. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/ergonomics.py +0 -0
  61. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/exceptions.py +0 -0
  62. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/functions.py +0 -0
  63. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/internal.py +0 -0
  64. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/models/__init__.py +0 -0
  65. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/models/core.py +0 -0
  66. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/models/datasource.py +0 -0
  67. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/models/environment.py +0 -0
  68. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/optimization.py +0 -0
  69. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/optimizations/__init__.py +0 -0
  70. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/optimizations/base_optimization.py +0 -0
  71. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/optimizations/inline_datasource.py +0 -0
  72. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/optimizations/predicate_pushdown.py +0 -0
  73. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/__init__.py +0 -0
  74. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/concept_strategies_v3.py +0 -0
  75. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/discovery_loop.py +0 -0
  76. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/discovery_node_factory.py +0 -0
  77. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/discovery_utility.py +0 -0
  78. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/discovery_validation.py +0 -0
  79. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/graph_utils.py +0 -0
  80. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/node_generators/__init__.py +0 -0
  81. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/node_generators/basic_node.py +0 -0
  82. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/node_generators/common.py +0 -0
  83. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/node_generators/constant_node.py +0 -0
  84. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/node_generators/filter_node.py +0 -0
  85. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/node_generators/group_to_node.py +0 -0
  86. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/node_generators/multiselect_node.py +0 -0
  87. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/node_generators/recursive_node.py +0 -0
  88. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/node_generators/rowset_node.py +0 -0
  89. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/node_generators/select_helpers/__init__.py +0 -0
  90. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/node_generators/select_node.py +0 -0
  91. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/node_generators/synonym_node.py +0 -0
  92. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/node_generators/union_node.py +0 -0
  93. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/node_generators/unnest_node.py +0 -0
  94. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/node_generators/window_node.py +0 -0
  95. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/nodes/__init__.py +0 -0
  96. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/nodes/base_node.py +0 -0
  97. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/nodes/filter_node.py +0 -0
  98. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/nodes/group_node.py +0 -0
  99. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/nodes/merge_node.py +0 -0
  100. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/nodes/recursive_node.py +0 -0
  101. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/nodes/select_node_v2.py +0 -0
  102. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/nodes/union_node.py +0 -0
  103. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/nodes/unnest_node.py +0 -0
  104. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/nodes/window_node.py +0 -0
  105. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/processing/utility.py +0 -0
  106. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/query_processor.py +0 -0
  107. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/statements/__init__.py +0 -0
  108. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/statements/author.py +0 -0
  109. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/statements/build.py +0 -0
  110. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/statements/common.py +0 -0
  111. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/statements/execute.py +0 -0
  112. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/core/utility.py +0 -0
  113. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/dialect/__init__.py +0 -0
  114. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/dialect/base.py +0 -0
  115. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/dialect/bigquery.py +0 -0
  116. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/dialect/common.py +0 -0
  117. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/dialect/config.py +0 -0
  118. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/dialect/dataframe.py +0 -0
  119. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/dialect/duckdb.py +0 -0
  120. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/dialect/enums.py +0 -0
  121. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/dialect/postgres.py +0 -0
  122. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/dialect/presto.py +0 -0
  123. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/dialect/snowflake.py +0 -0
  124. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/dialect/sql_server.py +0 -0
  125. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/engine.py +0 -0
  126. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/executor.py +0 -0
  127. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/hooks/__init__.py +0 -0
  128. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/hooks/base_hook.py +0 -0
  129. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/hooks/graph_hook.py +0 -0
  130. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/hooks/query_debugger.py +0 -0
  131. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/metadata/__init__.py +0 -0
  132. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/parser.py +0 -0
  133. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/parsing/__init__.py +0 -0
  134. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/parsing/common.py +0 -0
  135. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/parsing/config.py +0 -0
  136. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/parsing/exceptions.py +0 -0
  137. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/parsing/helpers.py +0 -0
  138. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/parsing/render.py +0 -0
  139. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/parsing/trilogy.lark +0 -0
  140. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/py.typed +0 -0
  141. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/render.py +0 -0
  142. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/scripts/__init__.py +0 -0
  143. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/scripts/trilogy.py +0 -0
  144. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/std/__init__.py +0 -0
  145. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/std/date.preql +0 -0
  146. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/std/display.preql +0 -0
  147. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/std/geography.preql +0 -0
  148. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/std/money.preql +0 -0
  149. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/std/net.preql +0 -0
  150. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/std/ranking.preql +0 -0
  151. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/std/report.preql +0 -0
  152. {pytrilogy-0.0.3.92 → pytrilogy-0.0.3.94}/trilogy/utility.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: pytrilogy
3
- Version: 0.0.3.92
3
+ Version: 0.0.3.94
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.4
2
2
  Name: pytrilogy
3
- Version: 0.0.3.92
3
+ Version: 0.0.3.94
4
4
  Summary: Declarative, typed query language that compiles to SQL.
5
5
  Home-page:
6
6
  Author:
@@ -67,7 +67,7 @@ def test_group_node_property_all(test_environment: Environment, test_environment
67
67
  sum_name_length = test_environment.concepts["category_name_length_sum"]
68
68
  all_rows = test_environment.concepts[f"{INTERNAL_NAMESPACE}.{ALL_ROWS_CONCEPT}"]
69
69
  sum_name_length_all_rows = sum_name_length.with_grain(
70
- BuildGrain(components=[all_rows])
70
+ BuildGrain(components={all_rows.address})
71
71
  )
72
72
  group_node = gen_group_node(
73
73
  sum_name_length_all_rows,
@@ -5,7 +5,6 @@ from trilogy.core.functions import argument_to_purpose, function_args_to_output_
5
5
  from trilogy.core.models.author import (
6
6
  Comparison,
7
7
  Conditional,
8
- ListWrapper,
9
8
  SubselectComparison,
10
9
  )
11
10
  from trilogy.core.models.build import BuildComparison
@@ -61,7 +60,7 @@ def test_not_in():
61
60
  _, parsed = parse_text(
62
61
  "const order_id <- 4; SELECT order_id WHERE order_id NOT IN (1,2,3);"
63
62
  )
64
- query: ProcessedQuery = parsed[-1]
63
+ query: SelectStatement = parsed[-1]
65
64
  right = query.where_clause.conditional.right
66
65
  assert isinstance(right, TupleWrapper), type(right)
67
66
  assert right[0] == 1
@@ -75,7 +74,7 @@ def test_datetime_lit_rendering():
75
74
  from datetime import datetime
76
75
 
77
76
  now = datetime.now()
78
- wrapper = ListWrapper(
77
+ wrapper = TupleWrapper(
79
78
  (now,),
80
79
  type=DataType.DATETIME,
81
80
  )
@@ -135,7 +135,7 @@ def test_basic_aggregate(test_environment: Environment, test_environment_graph):
135
135
  )
136
136
  datasource = datasource.resolve()
137
137
  assert isinstance(datasource, QueryDatasource)
138
- assert datasource.grain == BuildGrain(components=[product])
138
+ assert datasource.grain == BuildGrain(components={product.address})
139
139
 
140
140
 
141
141
  def test_join_aggregate(test_environment: Environment, test_environment_graph):
@@ -4,6 +4,6 @@ from trilogy.dialect.enums import Dialects
4
4
  from trilogy.executor import Executor
5
5
  from trilogy.parser import parse
6
6
 
7
- __version__ = "0.0.3.92"
7
+ __version__ = "0.0.3.94"
8
8
 
9
9
  __all__ = ["parse", "Executor", "Dialects", "Environment", "CONFIG"]
@@ -44,8 +44,10 @@ def add_concept(
44
44
  continue
45
45
  if pseudonym_node.split("@")[0] == node_name.split("@")[0]:
46
46
  continue
47
- g.add_edge(pseudonym_node, node_name, pseudonym=True)
48
- g.add_edge(node_name, pseudonym_node, pseudonym=True)
47
+ g.add_edge(pseudonym_node, node_name)
48
+ g.add_edge(node_name, pseudonym_node)
49
+ g.pseudonyms.add((pseudonym_node, node_name))
50
+ g.pseudonyms.add((node_name, pseudonym_node))
49
51
  add_concept(pseudonym, g, concept_mapping, default_concept_graph, seen)
50
52
 
51
53
 
@@ -0,0 +1,132 @@
1
+ from typing import Union
2
+
3
+ import networkx as nx
4
+
5
+ from trilogy.core.models.build import BuildConcept, BuildDatasource, BuildWhereClause
6
+
7
+
8
+ def get_graph_exact_match(
9
+ g: Union[nx.DiGraph, "ReferenceGraph"],
10
+ accept_partial: bool,
11
+ conditions: BuildWhereClause | None,
12
+ ) -> set[str]:
13
+ exact: set[str] = set()
14
+ for node, ds in g.datasources.items():
15
+ if isinstance(ds, list):
16
+ exact.add(node)
17
+ continue
18
+
19
+ if not conditions and not ds.non_partial_for:
20
+ exact.add(node)
21
+ continue
22
+ elif not conditions and accept_partial and ds.non_partial_for:
23
+ exact.add(node)
24
+ continue
25
+ elif conditions:
26
+ if not ds.non_partial_for:
27
+ continue
28
+ if ds.non_partial_for and conditions == ds.non_partial_for:
29
+ exact.add(node)
30
+ continue
31
+ else:
32
+ continue
33
+
34
+ return exact
35
+
36
+
37
+ def prune_sources_for_conditions(
38
+ g: "ReferenceGraph",
39
+ accept_partial: bool,
40
+ conditions: BuildWhereClause | None,
41
+ ):
42
+ complete = get_graph_exact_match(g, accept_partial, conditions)
43
+ to_remove = []
44
+ for node in g.datasources:
45
+ if node not in complete:
46
+ to_remove.append(node)
47
+
48
+ for node in to_remove:
49
+ g.remove_node(node)
50
+
51
+
52
+ def concept_to_node(input: BuildConcept) -> str:
53
+ # if input.purpose == Purpose.METRIC:
54
+ # return f"c~{input.namespace}.{input.name}@{input.grain}"
55
+ return f"c~{input.address}@{input.grain.str_no_condition}"
56
+
57
+
58
+ def datasource_to_node(input: BuildDatasource) -> str:
59
+ # if isinstance(input, JoinedDataSource):
60
+ # return "ds~join~" + ",".join(
61
+ # [datasource_to_node(sub) for sub in input.datasources]
62
+ # )
63
+ return f"ds~{input.identifier}"
64
+
65
+
66
+ class ReferenceGraph(nx.DiGraph):
67
+ def __init__(self, *args, **kwargs):
68
+ super().__init__(*args, **kwargs)
69
+ self.concepts: dict[str, BuildConcept] = {}
70
+ self.datasources: dict[str, BuildDatasource] = {}
71
+ self.pseudonyms: set[tuple[str, str]] = set()
72
+
73
+ def copy(self):
74
+ g = ReferenceGraph()
75
+ g.concepts = self.concepts.copy()
76
+ g.datasources = self.datasources.copy()
77
+ g.pseudonyms = {*self.pseudonyms}
78
+ # g.add_nodes_from(self.nodes(data=True))
79
+ for node in self.nodes:
80
+ g.add_node(node, fast=True)
81
+ for edge in self.edges:
82
+ g.add_edge(edge[0], edge[1], fast=True)
83
+ # g.add_edges_from(self.edges(data=True))
84
+ return g
85
+
86
+ def remove_node(self, n):
87
+ if n in self.concepts:
88
+ del self.concepts[n]
89
+ if n in self.datasources:
90
+ del self.datasources[n]
91
+ super().remove_node(n)
92
+
93
+ def add_node(self, node_for_adding, fast: bool = False, **attr):
94
+ if fast:
95
+ return super().add_node(node_for_adding, **attr)
96
+ if isinstance(node_for_adding, BuildConcept):
97
+ node_name = concept_to_node(node_for_adding)
98
+ self.concepts[node_name] = node_for_adding
99
+ elif isinstance(node_for_adding, BuildDatasource):
100
+ node_name = datasource_to_node(node_for_adding)
101
+ self.datasources[node_name] = node_for_adding
102
+ else:
103
+ node_name = node_for_adding
104
+ if attr.get("datasource"):
105
+ self.datasources[node_name] = attr["datasource"]
106
+ super().add_node(node_name, **attr)
107
+
108
+ def add_edge(self, u_of_edge, v_of_edge, fast: bool = False, **attr):
109
+ if fast:
110
+ return super().add_edge(u_of_edge, v_of_edge, **attr)
111
+ if isinstance(u_of_edge, BuildConcept):
112
+ orig = u_of_edge
113
+ u_of_edge = concept_to_node(u_of_edge)
114
+ if u_of_edge not in self.nodes:
115
+ self.add_node(orig)
116
+ elif isinstance(u_of_edge, BuildDatasource):
117
+ origd = u_of_edge
118
+ u_of_edge = datasource_to_node(u_of_edge)
119
+ if u_of_edge not in self.nodes:
120
+ self.add_node(origd)
121
+
122
+ if isinstance(v_of_edge, BuildConcept):
123
+ orig = v_of_edge
124
+ v_of_edge = concept_to_node(v_of_edge)
125
+ if v_of_edge not in self.nodes:
126
+ self.add_node(orig)
127
+ elif isinstance(v_of_edge, BuildDatasource):
128
+ origd = v_of_edge
129
+ v_of_edge = datasource_to_node(v_of_edge)
130
+ if v_of_edge not in self.nodes:
131
+ self.add_node(origd)
132
+ super().add_edge(u_of_edge, v_of_edge)
@@ -902,7 +902,11 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
902
902
 
903
903
  @property
904
904
  def is_aggregate(self):
905
- return self.calculate_is_aggregate(self.lineage)
905
+ base = getattr(self, "_is_aggregate", None)
906
+ if base:
907
+ return base
908
+ setattr(self, "_is_aggregate", self.calculate_is_aggregate(self.lineage))
909
+ return self._is_aggregate
906
910
 
907
911
  def with_merge(self, source: Self, target: Self, modifiers: List[Modifier]) -> Self:
908
912
  if self.address == source.address:
@@ -1069,18 +1073,25 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
1069
1073
  final_grain = grain if not self.grain.components else self.grain
1070
1074
  keys = self.keys
1071
1075
 
1072
- if self.is_aggregate and isinstance(new_lineage, Function) and grain.components:
1076
+ if self.is_aggregate and grain.components and isinstance(new_lineage, Function):
1073
1077
  grain_components: list[ConceptRef | Concept] = [
1074
1078
  environment.concepts[c].reference for c in grain.components
1075
1079
  ]
1076
- new_lineage = AggregateWrapper(function=new_lineage, by=grain_components)
1080
+ new_lineage = AggregateWrapper.model_construct(
1081
+ function=new_lineage, by=grain_components
1082
+ )
1077
1083
  final_grain = grain
1078
1084
  keys = set(grain.components)
1079
- elif isinstance(new_lineage, AggregateWrapper) and not new_lineage.by and grain:
1085
+ elif (
1086
+ grain
1087
+ and new_lineage
1088
+ and isinstance(new_lineage, AggregateWrapper)
1089
+ and not new_lineage.by
1090
+ ):
1080
1091
  grain_components = [
1081
1092
  environment.concepts[c].reference for c in grain.components
1082
1093
  ]
1083
- new_lineage = AggregateWrapper(
1094
+ new_lineage = AggregateWrapper.model_construct(
1084
1095
  function=new_lineage.function, by=grain_components
1085
1096
  )
1086
1097
  final_grain = grain
@@ -1133,7 +1144,7 @@ class Concept(Addressable, DataTyped, ConceptArgs, Mergeable, Namespaced, BaseMo
1133
1144
  granularity=self.granularity,
1134
1145
  derivation=self.derivation,
1135
1146
  lineage=self.lineage,
1136
- grain=grain if grain else Grain(components=set()),
1147
+ grain=grain if grain else Grain.model_construct(components=set()),
1137
1148
  namespace=self.namespace,
1138
1149
  keys=self.keys,
1139
1150
  modifiers=self.modifiers,
@@ -1670,15 +1681,6 @@ class Function(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
1670
1681
  def datatype(self):
1671
1682
  return self.output_datatype
1672
1683
 
1673
- @field_validator("output_datatype")
1674
- @classmethod
1675
- def parse_output_datatype(cls, v, info: ValidationInfo):
1676
- values = info.data
1677
- if values.get("operator") == FunctionType.ATTR_ACCESS:
1678
- if isinstance(v, StructType):
1679
- raise SyntaxError
1680
- return v
1681
-
1682
1684
  @field_validator("arguments", mode="before")
1683
1685
  @classmethod
1684
1686
  def parse_arguments(cls, v, info: ValidationInfo):
@@ -1845,17 +1847,6 @@ class Function(DataTyped, ConceptArgs, Mergeable, Namespaced, BaseModel):
1845
1847
  base += get_concept_arguments(arg)
1846
1848
  return base
1847
1849
 
1848
- @property
1849
- def output_grain(self):
1850
- # aggregates have an abstract grain
1851
- base_grain = Grain(components=[])
1852
- if self.operator in FunctionClass.AGGREGATE_FUNCTIONS.value:
1853
- return base_grain
1854
- # scalars have implicit grain of all arguments
1855
- for input in self.concept_arguments:
1856
- base_grain += input.grain
1857
- return base_grain
1858
-
1859
1850
 
1860
1851
  class FunctionCallWrapper(
1861
1852
  DataTyped,