dvt-core 1.11.0b4__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 dvt-core might be problematic. Click here for more details.

Files changed (261) hide show
  1. dvt/__init__.py +7 -0
  2. dvt/_pydantic_shim.py +26 -0
  3. dvt/adapters/__init__.py +16 -0
  4. dvt/adapters/multi_adapter_manager.py +268 -0
  5. dvt/artifacts/__init__.py +0 -0
  6. dvt/artifacts/exceptions/__init__.py +1 -0
  7. dvt/artifacts/exceptions/schemas.py +31 -0
  8. dvt/artifacts/resources/__init__.py +116 -0
  9. dvt/artifacts/resources/base.py +68 -0
  10. dvt/artifacts/resources/types.py +93 -0
  11. dvt/artifacts/resources/v1/analysis.py +10 -0
  12. dvt/artifacts/resources/v1/catalog.py +23 -0
  13. dvt/artifacts/resources/v1/components.py +275 -0
  14. dvt/artifacts/resources/v1/config.py +282 -0
  15. dvt/artifacts/resources/v1/documentation.py +11 -0
  16. dvt/artifacts/resources/v1/exposure.py +52 -0
  17. dvt/artifacts/resources/v1/function.py +53 -0
  18. dvt/artifacts/resources/v1/generic_test.py +32 -0
  19. dvt/artifacts/resources/v1/group.py +22 -0
  20. dvt/artifacts/resources/v1/hook.py +11 -0
  21. dvt/artifacts/resources/v1/macro.py +30 -0
  22. dvt/artifacts/resources/v1/metric.py +173 -0
  23. dvt/artifacts/resources/v1/model.py +146 -0
  24. dvt/artifacts/resources/v1/owner.py +10 -0
  25. dvt/artifacts/resources/v1/saved_query.py +112 -0
  26. dvt/artifacts/resources/v1/seed.py +42 -0
  27. dvt/artifacts/resources/v1/semantic_layer_components.py +72 -0
  28. dvt/artifacts/resources/v1/semantic_model.py +315 -0
  29. dvt/artifacts/resources/v1/singular_test.py +14 -0
  30. dvt/artifacts/resources/v1/snapshot.py +92 -0
  31. dvt/artifacts/resources/v1/source_definition.py +85 -0
  32. dvt/artifacts/resources/v1/sql_operation.py +10 -0
  33. dvt/artifacts/resources/v1/unit_test_definition.py +78 -0
  34. dvt/artifacts/schemas/__init__.py +0 -0
  35. dvt/artifacts/schemas/base.py +191 -0
  36. dvt/artifacts/schemas/batch_results.py +24 -0
  37. dvt/artifacts/schemas/catalog/__init__.py +12 -0
  38. dvt/artifacts/schemas/catalog/v1/__init__.py +0 -0
  39. dvt/artifacts/schemas/catalog/v1/catalog.py +60 -0
  40. dvt/artifacts/schemas/freshness/__init__.py +1 -0
  41. dvt/artifacts/schemas/freshness/v3/__init__.py +0 -0
  42. dvt/artifacts/schemas/freshness/v3/freshness.py +159 -0
  43. dvt/artifacts/schemas/manifest/__init__.py +2 -0
  44. dvt/artifacts/schemas/manifest/v12/__init__.py +0 -0
  45. dvt/artifacts/schemas/manifest/v12/manifest.py +212 -0
  46. dvt/artifacts/schemas/results.py +148 -0
  47. dvt/artifacts/schemas/run/__init__.py +2 -0
  48. dvt/artifacts/schemas/run/v5/__init__.py +0 -0
  49. dvt/artifacts/schemas/run/v5/run.py +184 -0
  50. dvt/artifacts/schemas/upgrades/__init__.py +4 -0
  51. dvt/artifacts/schemas/upgrades/upgrade_manifest.py +174 -0
  52. dvt/artifacts/schemas/upgrades/upgrade_manifest_dbt_version.py +2 -0
  53. dvt/artifacts/utils/validation.py +153 -0
  54. dvt/cli/__init__.py +1 -0
  55. dvt/cli/context.py +16 -0
  56. dvt/cli/exceptions.py +56 -0
  57. dvt/cli/flags.py +558 -0
  58. dvt/cli/main.py +971 -0
  59. dvt/cli/option_types.py +121 -0
  60. dvt/cli/options.py +79 -0
  61. dvt/cli/params.py +803 -0
  62. dvt/cli/requires.py +478 -0
  63. dvt/cli/resolvers.py +32 -0
  64. dvt/cli/types.py +40 -0
  65. dvt/clients/__init__.py +0 -0
  66. dvt/clients/checked_load.py +82 -0
  67. dvt/clients/git.py +164 -0
  68. dvt/clients/jinja.py +206 -0
  69. dvt/clients/jinja_static.py +245 -0
  70. dvt/clients/registry.py +192 -0
  71. dvt/clients/yaml_helper.py +68 -0
  72. dvt/compilation.py +833 -0
  73. dvt/compute/__init__.py +26 -0
  74. dvt/compute/base.py +288 -0
  75. dvt/compute/engines/__init__.py +13 -0
  76. dvt/compute/engines/duckdb_engine.py +368 -0
  77. dvt/compute/engines/spark_engine.py +273 -0
  78. dvt/compute/query_analyzer.py +212 -0
  79. dvt/compute/router.py +483 -0
  80. dvt/config/__init__.py +4 -0
  81. dvt/config/catalogs.py +95 -0
  82. dvt/config/compute_config.py +406 -0
  83. dvt/config/profile.py +411 -0
  84. dvt/config/profiles_v2.py +464 -0
  85. dvt/config/project.py +893 -0
  86. dvt/config/renderer.py +232 -0
  87. dvt/config/runtime.py +491 -0
  88. dvt/config/selectors.py +209 -0
  89. dvt/config/utils.py +78 -0
  90. dvt/connectors/.gitignore +6 -0
  91. dvt/connectors/README.md +306 -0
  92. dvt/connectors/catalog.yml +217 -0
  93. dvt/connectors/download_connectors.py +300 -0
  94. dvt/constants.py +29 -0
  95. dvt/context/__init__.py +0 -0
  96. dvt/context/base.py +746 -0
  97. dvt/context/configured.py +136 -0
  98. dvt/context/context_config.py +350 -0
  99. dvt/context/docs.py +82 -0
  100. dvt/context/exceptions_jinja.py +179 -0
  101. dvt/context/macro_resolver.py +195 -0
  102. dvt/context/macros.py +171 -0
  103. dvt/context/manifest.py +73 -0
  104. dvt/context/providers.py +2198 -0
  105. dvt/context/query_header.py +14 -0
  106. dvt/context/secret.py +59 -0
  107. dvt/context/target.py +74 -0
  108. dvt/contracts/__init__.py +0 -0
  109. dvt/contracts/files.py +413 -0
  110. dvt/contracts/graph/__init__.py +0 -0
  111. dvt/contracts/graph/manifest.py +1904 -0
  112. dvt/contracts/graph/metrics.py +98 -0
  113. dvt/contracts/graph/model_config.py +71 -0
  114. dvt/contracts/graph/node_args.py +42 -0
  115. dvt/contracts/graph/nodes.py +1806 -0
  116. dvt/contracts/graph/semantic_manifest.py +233 -0
  117. dvt/contracts/graph/unparsed.py +812 -0
  118. dvt/contracts/project.py +417 -0
  119. dvt/contracts/results.py +53 -0
  120. dvt/contracts/selection.py +23 -0
  121. dvt/contracts/sql.py +86 -0
  122. dvt/contracts/state.py +69 -0
  123. dvt/contracts/util.py +46 -0
  124. dvt/deprecations.py +347 -0
  125. dvt/deps/__init__.py +0 -0
  126. dvt/deps/base.py +153 -0
  127. dvt/deps/git.py +196 -0
  128. dvt/deps/local.py +80 -0
  129. dvt/deps/registry.py +131 -0
  130. dvt/deps/resolver.py +149 -0
  131. dvt/deps/tarball.py +121 -0
  132. dvt/docs/source/_ext/dbt_click.py +118 -0
  133. dvt/docs/source/conf.py +32 -0
  134. dvt/env_vars.py +64 -0
  135. dvt/event_time/event_time.py +40 -0
  136. dvt/event_time/sample_window.py +60 -0
  137. dvt/events/__init__.py +16 -0
  138. dvt/events/base_types.py +37 -0
  139. dvt/events/core_types_pb2.py +2 -0
  140. dvt/events/logging.py +109 -0
  141. dvt/events/types.py +2534 -0
  142. dvt/exceptions.py +1487 -0
  143. dvt/flags.py +89 -0
  144. dvt/graph/__init__.py +11 -0
  145. dvt/graph/cli.py +248 -0
  146. dvt/graph/graph.py +172 -0
  147. dvt/graph/queue.py +213 -0
  148. dvt/graph/selector.py +375 -0
  149. dvt/graph/selector_methods.py +976 -0
  150. dvt/graph/selector_spec.py +223 -0
  151. dvt/graph/thread_pool.py +18 -0
  152. dvt/hooks.py +21 -0
  153. dvt/include/README.md +49 -0
  154. dvt/include/__init__.py +3 -0
  155. dvt/include/global_project.py +4 -0
  156. dvt/include/starter_project/.gitignore +4 -0
  157. dvt/include/starter_project/README.md +15 -0
  158. dvt/include/starter_project/__init__.py +3 -0
  159. dvt/include/starter_project/analyses/.gitkeep +0 -0
  160. dvt/include/starter_project/dvt_project.yml +36 -0
  161. dvt/include/starter_project/macros/.gitkeep +0 -0
  162. dvt/include/starter_project/models/example/my_first_dbt_model.sql +27 -0
  163. dvt/include/starter_project/models/example/my_second_dbt_model.sql +6 -0
  164. dvt/include/starter_project/models/example/schema.yml +21 -0
  165. dvt/include/starter_project/seeds/.gitkeep +0 -0
  166. dvt/include/starter_project/snapshots/.gitkeep +0 -0
  167. dvt/include/starter_project/tests/.gitkeep +0 -0
  168. dvt/internal_deprecations.py +27 -0
  169. dvt/jsonschemas/__init__.py +3 -0
  170. dvt/jsonschemas/jsonschemas.py +309 -0
  171. dvt/jsonschemas/project/0.0.110.json +4717 -0
  172. dvt/jsonschemas/project/0.0.85.json +2015 -0
  173. dvt/jsonschemas/resources/0.0.110.json +2636 -0
  174. dvt/jsonschemas/resources/0.0.85.json +2536 -0
  175. dvt/jsonschemas/resources/latest.json +6773 -0
  176. dvt/links.py +4 -0
  177. dvt/materializations/__init__.py +0 -0
  178. dvt/materializations/incremental/__init__.py +0 -0
  179. dvt/materializations/incremental/microbatch.py +235 -0
  180. dvt/mp_context.py +8 -0
  181. dvt/node_types.py +37 -0
  182. dvt/parser/__init__.py +23 -0
  183. dvt/parser/analysis.py +21 -0
  184. dvt/parser/base.py +549 -0
  185. dvt/parser/common.py +267 -0
  186. dvt/parser/docs.py +52 -0
  187. dvt/parser/fixtures.py +51 -0
  188. dvt/parser/functions.py +30 -0
  189. dvt/parser/generic_test.py +100 -0
  190. dvt/parser/generic_test_builders.py +334 -0
  191. dvt/parser/hooks.py +119 -0
  192. dvt/parser/macros.py +137 -0
  193. dvt/parser/manifest.py +2204 -0
  194. dvt/parser/models.py +574 -0
  195. dvt/parser/partial.py +1179 -0
  196. dvt/parser/read_files.py +445 -0
  197. dvt/parser/schema_generic_tests.py +423 -0
  198. dvt/parser/schema_renderer.py +111 -0
  199. dvt/parser/schema_yaml_readers.py +936 -0
  200. dvt/parser/schemas.py +1467 -0
  201. dvt/parser/search.py +149 -0
  202. dvt/parser/seeds.py +28 -0
  203. dvt/parser/singular_test.py +20 -0
  204. dvt/parser/snapshots.py +44 -0
  205. dvt/parser/sources.py +557 -0
  206. dvt/parser/sql.py +63 -0
  207. dvt/parser/unit_tests.py +622 -0
  208. dvt/plugins/__init__.py +20 -0
  209. dvt/plugins/contracts.py +10 -0
  210. dvt/plugins/exceptions.py +2 -0
  211. dvt/plugins/manager.py +164 -0
  212. dvt/plugins/manifest.py +21 -0
  213. dvt/profiler.py +20 -0
  214. dvt/py.typed +1 -0
  215. dvt/runners/__init__.py +2 -0
  216. dvt/runners/exposure_runner.py +7 -0
  217. dvt/runners/no_op_runner.py +46 -0
  218. dvt/runners/saved_query_runner.py +7 -0
  219. dvt/selected_resources.py +8 -0
  220. dvt/task/__init__.py +0 -0
  221. dvt/task/base.py +504 -0
  222. dvt/task/build.py +197 -0
  223. dvt/task/clean.py +57 -0
  224. dvt/task/clone.py +162 -0
  225. dvt/task/compile.py +151 -0
  226. dvt/task/compute.py +366 -0
  227. dvt/task/debug.py +650 -0
  228. dvt/task/deps.py +280 -0
  229. dvt/task/docs/__init__.py +3 -0
  230. dvt/task/docs/generate.py +408 -0
  231. dvt/task/docs/index.html +250 -0
  232. dvt/task/docs/serve.py +28 -0
  233. dvt/task/freshness.py +323 -0
  234. dvt/task/function.py +122 -0
  235. dvt/task/group_lookup.py +46 -0
  236. dvt/task/init.py +374 -0
  237. dvt/task/list.py +237 -0
  238. dvt/task/printer.py +176 -0
  239. dvt/task/profiles.py +256 -0
  240. dvt/task/retry.py +175 -0
  241. dvt/task/run.py +1146 -0
  242. dvt/task/run_operation.py +142 -0
  243. dvt/task/runnable.py +802 -0
  244. dvt/task/seed.py +104 -0
  245. dvt/task/show.py +150 -0
  246. dvt/task/snapshot.py +57 -0
  247. dvt/task/sql.py +111 -0
  248. dvt/task/test.py +464 -0
  249. dvt/tests/fixtures/__init__.py +1 -0
  250. dvt/tests/fixtures/project.py +620 -0
  251. dvt/tests/util.py +651 -0
  252. dvt/tracking.py +529 -0
  253. dvt/utils/__init__.py +3 -0
  254. dvt/utils/artifact_upload.py +151 -0
  255. dvt/utils/utils.py +408 -0
  256. dvt/version.py +249 -0
  257. dvt_core-1.11.0b4.dist-info/METADATA +252 -0
  258. dvt_core-1.11.0b4.dist-info/RECORD +261 -0
  259. dvt_core-1.11.0b4.dist-info/WHEEL +5 -0
  260. dvt_core-1.11.0b4.dist-info/entry_points.txt +2 -0
  261. dvt_core-1.11.0b4.dist-info/top_level.txt +1 -0
@@ -0,0 +1,233 @@
1
+ from typing import List, Optional, Set
2
+
3
+ from dvt import deprecations
4
+ from dvt.constants import (
5
+ LEGACY_TIME_SPINE_GRANULARITY,
6
+ LEGACY_TIME_SPINE_MODEL_NAME,
7
+ MINIMUM_REQUIRED_TIME_SPINE_GRANULARITY,
8
+ )
9
+ from dvt.contracts.graph.manifest import Manifest
10
+ from dvt.contracts.graph.nodes import ModelNode
11
+ from dvt.events.types import ArtifactWritten, SemanticValidationFailure
12
+ from dvt.exceptions import ParsingError
13
+ from dvt.flags import get_flags
14
+
15
+ from dbt_common.clients.system import write_file
16
+ from dbt_common.events.base_types import EventLevel
17
+ from dbt_common.events.functions import fire_event
18
+ from dbt_semantic_interfaces.implementations.metric import PydanticMetric
19
+ from dbt_semantic_interfaces.implementations.node_relation import PydanticNodeRelation
20
+ from dbt_semantic_interfaces.implementations.project_configuration import (
21
+ PydanticProjectConfiguration,
22
+ )
23
+ from dbt_semantic_interfaces.implementations.saved_query import PydanticSavedQuery
24
+ from dbt_semantic_interfaces.implementations.semantic_manifest import (
25
+ PydanticSemanticManifest,
26
+ )
27
+ from dbt_semantic_interfaces.implementations.semantic_model import PydanticSemanticModel
28
+ from dbt_semantic_interfaces.implementations.time_spine import (
29
+ PydanticTimeSpine,
30
+ PydanticTimeSpineCustomGranularityColumn,
31
+ PydanticTimeSpinePrimaryColumn,
32
+ )
33
+ from dbt_semantic_interfaces.implementations.time_spine_table_configuration import (
34
+ PydanticTimeSpineTableConfiguration as LegacyTimeSpine,
35
+ )
36
+ from dbt_semantic_interfaces.type_enums import TimeGranularity
37
+ from dbt_semantic_interfaces.validations.semantic_manifest_validator import (
38
+ SemanticManifestValidator,
39
+ )
40
+ from dbt_semantic_interfaces.validations.validator_helpers import (
41
+ FileContext,
42
+ ValidationError,
43
+ ValidationIssueContext,
44
+ )
45
+
46
+
47
+ class SemanticManifest:
48
+ def __init__(self, manifest: Manifest) -> None:
49
+ self.manifest = manifest
50
+
51
+ def validate(self) -> bool:
52
+
53
+ # TODO: Enforce this check.
54
+ # if self.manifest.metrics and not self.manifest.semantic_models:
55
+ # fire_event(
56
+ # SemanticValidationFailure(
57
+ # msg="Metrics require semantic models, but none were found."
58
+ # ),
59
+ # EventLevel.ERROR,
60
+ # )
61
+ # return False
62
+
63
+ if not self.manifest.metrics or not self.manifest.semantic_models:
64
+ return True
65
+
66
+ semantic_manifest = self._get_pydantic_semantic_manifest()
67
+ validator = SemanticManifestValidator[PydanticSemanticManifest]()
68
+ validation_results = validator.validate_semantic_manifest(semantic_manifest)
69
+ validation_result_errors = list(validation_results.errors)
70
+
71
+ metrics_using_old_params: Set[str] = set()
72
+ for metric in semantic_manifest.metrics or []:
73
+ for field in ("window", "grain_to_date"):
74
+ type_params_field_value = getattr(metric.type_params, field)
75
+ # Warn that the old type_params structure has been deprecated.
76
+ if type_params_field_value:
77
+ metrics_using_old_params.add(metric.name)
78
+ if metrics_using_old_params:
79
+ if get_flags().require_nested_cumulative_type_params is False:
80
+ deprecations.warn(
81
+ "mf-cumulative-type-params-deprecation",
82
+ )
83
+ else:
84
+ names = ", ".join(metrics_using_old_params)
85
+ validation_result_errors.append(
86
+ ValidationError(
87
+ context=ValidationIssueContext(
88
+ # We don't have the file context at this point.
89
+ file_context=FileContext(),
90
+ object_name=names,
91
+ object_type="metric",
92
+ ),
93
+ message=f"Cumulative fields `type_params.window` and `type_params.grain_to_date` should be nested under `type_params.cumulative_type_params.window` and `type_params.cumulative_type_params.grain_to_date`. Invalid metrics: {names}. See documentation on behavior changes: https://docs.getdbt.com/reference/global-configs/behavior-changes.",
94
+ )
95
+ )
96
+
97
+ time_spines = semantic_manifest.project_configuration.time_spines
98
+ legacy_time_spines = (
99
+ semantic_manifest.project_configuration.time_spine_table_configurations
100
+ )
101
+ # If the time spine contains a day grain then it is functionally equivalent to the legacy time spine.
102
+ time_spines_contain_day = any(
103
+ c for c in time_spines if c.primary_column.time_granularity == TimeGranularity.DAY
104
+ )
105
+ if (
106
+ get_flags().require_yaml_configuration_for_mf_time_spines is False
107
+ and legacy_time_spines
108
+ and not time_spines_contain_day
109
+ ):
110
+ deprecations.warn(
111
+ "mf-timespine-without-yaml-configuration",
112
+ )
113
+
114
+ for warning in validation_results.warnings:
115
+ fire_event(SemanticValidationFailure(msg=warning.message))
116
+
117
+ for error in validation_result_errors:
118
+ fire_event(SemanticValidationFailure(msg=error.message), EventLevel.ERROR)
119
+
120
+ return not validation_result_errors
121
+
122
+ def write_json_to_file(self, file_path: str):
123
+ semantic_manifest = self._get_pydantic_semantic_manifest()
124
+ json = semantic_manifest.json()
125
+ write_file(file_path, json)
126
+ fire_event(ArtifactWritten(artifact_type=self.__class__.__name__, artifact_path=file_path))
127
+
128
+ def _get_pydantic_semantic_manifest(self) -> PydanticSemanticManifest:
129
+ pydantic_time_spines: List[PydanticTimeSpine] = []
130
+ minimum_time_spine_granularity: Optional[TimeGranularity] = None
131
+ for node in self.manifest.nodes.values():
132
+ if not (isinstance(node, ModelNode) and node.time_spine):
133
+ continue
134
+ time_spine = node.time_spine
135
+ standard_granularity_column = None
136
+ for column in node.columns.values():
137
+ if column.name == time_spine.standard_granularity_column:
138
+ standard_granularity_column = column
139
+ break
140
+ # Assertions needed for type checking
141
+ if not standard_granularity_column:
142
+ raise ParsingError(
143
+ "Expected to find time spine standard granularity column in model columns, but did not. "
144
+ "This should have been caught in YAML parsing."
145
+ )
146
+ if not standard_granularity_column.granularity:
147
+ raise ParsingError(
148
+ "Expected to find granularity set for time spine standard granularity column, but did not. "
149
+ "This should have been caught in YAML parsing."
150
+ )
151
+ pydantic_time_spine = PydanticTimeSpine(
152
+ node_relation=PydanticNodeRelation(
153
+ alias=node.alias,
154
+ schema_name=node.schema,
155
+ database=node.database,
156
+ relation_name=node.relation_name,
157
+ ),
158
+ primary_column=PydanticTimeSpinePrimaryColumn(
159
+ name=time_spine.standard_granularity_column,
160
+ time_granularity=standard_granularity_column.granularity,
161
+ ),
162
+ custom_granularities=[
163
+ PydanticTimeSpineCustomGranularityColumn(
164
+ name=custom_granularity.name, column_name=custom_granularity.column_name
165
+ )
166
+ for custom_granularity in time_spine.custom_granularities
167
+ ],
168
+ )
169
+ pydantic_time_spines.append(pydantic_time_spine)
170
+ if (
171
+ not minimum_time_spine_granularity
172
+ or standard_granularity_column.granularity.to_int()
173
+ < minimum_time_spine_granularity.to_int()
174
+ ):
175
+ minimum_time_spine_granularity = standard_granularity_column.granularity
176
+
177
+ project_config = PydanticProjectConfiguration(
178
+ time_spine_table_configurations=[], time_spines=pydantic_time_spines
179
+ )
180
+ pydantic_semantic_manifest = PydanticSemanticManifest(
181
+ metrics=[], semantic_models=[], project_configuration=project_config
182
+ )
183
+
184
+ for semantic_model in self.manifest.semantic_models.values():
185
+ pydantic_semantic_manifest.semantic_models.append(
186
+ PydanticSemanticModel.parse_obj(semantic_model.to_dict())
187
+ )
188
+
189
+ for metric in self.manifest.metrics.values():
190
+ pydantic_semantic_manifest.metrics.append(PydanticMetric.parse_obj(metric.to_dict()))
191
+
192
+ for saved_query in self.manifest.saved_queries.values():
193
+ pydantic_semantic_manifest.saved_queries.append(
194
+ PydanticSavedQuery.parse_obj(saved_query.to_dict())
195
+ )
196
+
197
+ legacy_time_spine_model: Optional[ModelNode] = None
198
+ if self.manifest.semantic_models:
199
+ legacy_time_spine_model = self.manifest.ref_lookup.find(
200
+ LEGACY_TIME_SPINE_MODEL_NAME, None, None, self.manifest
201
+ )
202
+ if legacy_time_spine_model:
203
+ if (
204
+ not minimum_time_spine_granularity
205
+ or LEGACY_TIME_SPINE_GRANULARITY.to_int()
206
+ < minimum_time_spine_granularity.to_int()
207
+ ):
208
+ minimum_time_spine_granularity = LEGACY_TIME_SPINE_GRANULARITY
209
+
210
+ # If no time spines have been configured at DAY or smaller AND legacy time spine model does not exist, error.
211
+ if (
212
+ not minimum_time_spine_granularity
213
+ or minimum_time_spine_granularity.to_int()
214
+ > MINIMUM_REQUIRED_TIME_SPINE_GRANULARITY.to_int()
215
+ ):
216
+ raise ParsingError(
217
+ "The semantic layer requires a time spine model with granularity DAY or smaller in the project, "
218
+ "but none was found. Guidance on creating this model can be found on our docs site "
219
+ "(https://docs.getdbt.com/docs/build/metricflow-time-spine)."
220
+ )
221
+
222
+ # For backward compatibility: if legacy time spine exists without config, include it in the manifest.
223
+ if legacy_time_spine_model and legacy_time_spine_model.time_spine is None:
224
+ legacy_time_spine = LegacyTimeSpine(
225
+ location=legacy_time_spine_model.relation_name,
226
+ column_name="date_day",
227
+ grain=LEGACY_TIME_SPINE_GRANULARITY,
228
+ )
229
+ pydantic_semantic_manifest.project_configuration.time_spine_table_configurations = [
230
+ legacy_time_spine
231
+ ]
232
+
233
+ return pydantic_semantic_manifest