Skip to content

Commit

Permalink
Update logic to require a time spine at DAY or smaller granularity
Browse files Browse the repository at this point in the history
  • Loading branch information
courtneyholcomb committed Jul 26, 2024
1 parent f98ec2d commit 219548b
Show file tree
Hide file tree
Showing 3 changed files with 35 additions and 11 deletions.
4 changes: 4 additions & 0 deletions core/dbt/constants.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
from dbt_semantic_interfaces.type_enums import TimeGranularity

DEFAULT_ENV_PLACEHOLDER = "DBT_DEFAULT_PLACEHOLDER"

SECRET_PLACEHOLDER = "$$$DBT_SECRET_START$$${}$$$DBT_SECRET_END$$$"
Expand All @@ -16,5 +18,7 @@
MANIFEST_FILE_NAME = "manifest.json"
SEMANTIC_MANIFEST_FILE_NAME = "semantic_manifest.json"
LEGACY_TIME_SPINE_MODEL_NAME = "metricflow_time_spine"
LEGACY_TIME_SPINE_GRANULARITY = TimeGranularity.DAY
MINIMUM_REQUIRED_TIME_SPINE_GRANULARITY = TimeGranularity.DAY
PARTIAL_PARSE_FILE_NAME = "partial_parse.msgpack"
PACKAGE_LOCK_HASH_KEY = "sha1_hash"
40 changes: 30 additions & 10 deletions core/dbt/contracts/graph/semantic_manifest.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
from typing import List, Optional

from dbt.constants import LEGACY_TIME_SPINE_MODEL_NAME
from dbt.constants import (
LEGACY_TIME_SPINE_GRANULARITY,
LEGACY_TIME_SPINE_MODEL_NAME,
MINIMUM_REQUIRED_TIME_SPINE_GRANULARITY,
)
from dbt.contracts.graph.manifest import Manifest
from dbt.contracts.graph.nodes import ModelNode
from dbt.events.types import SemanticValidationFailure
Expand Down Expand Up @@ -69,7 +73,7 @@ def write_json_to_file(self, file_path: str):

def _get_pydantic_semantic_manifest(self) -> PydanticSemanticManifest:
pydantic_time_spines: List[PydanticTimeSpine] = []
daily_time_spine: Optional[PydanticTimeSpine] = None
minimum_time_spine_granularity: Optional[TimeGranularity] = None
for node in self.manifest.nodes.values():
if not (isinstance(node, ModelNode) and node.time_spine):
continue
Expand Down Expand Up @@ -103,8 +107,12 @@ def _get_pydantic_semantic_manifest(self) -> PydanticSemanticManifest:
),
)
pydantic_time_spines.append(pydantic_time_spine)
if standard_granularity_column.granularity == TimeGranularity.DAY:
daily_time_spine = pydantic_time_spine
if (

Check warning on line 110 in core/dbt/contracts/graph/semantic_manifest.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/contracts/graph/semantic_manifest.py#L109-L110

Added lines #L109 - L110 were not covered by tests
not minimum_time_spine_granularity
or standard_granularity_column.granularity.to_int()
< minimum_time_spine_granularity.to_int()
):
minimum_time_spine_granularity = standard_granularity_column.granularity

Check warning on line 115 in core/dbt/contracts/graph/semantic_manifest.py

View check run for this annotation

Codecov / codecov/patch

core/dbt/contracts/graph/semantic_manifest.py#L115

Added line #L115 was not covered by tests

project_config = PydanticProjectConfiguration(
time_spine_table_configurations=[], time_spines=pydantic_time_spines
Expand All @@ -127,23 +135,35 @@ def _get_pydantic_semantic_manifest(self) -> PydanticSemanticManifest:
)

if self.manifest.semantic_models:
# If no time spines have been configured AND legacy time spine model does not exist, error.
legacy_time_spine_model = self.manifest.ref_lookup.find(
LEGACY_TIME_SPINE_MODEL_NAME, None, None, self.manifest
)
if not (daily_time_spine or legacy_time_spine_model):
if legacy_time_spine_model:
if (
not minimum_time_spine_granularity
or LEGACY_TIME_SPINE_GRANULARITY.to_int()
< minimum_time_spine_granularity.to_int()
):
minimum_time_spine_granularity = LEGACY_TIME_SPINE_GRANULARITY

# If no time spines have been configured at DAY or smaller AND legacy time spine model does not exist, error.
if (
not minimum_time_spine_granularity
or minimum_time_spine_granularity.to_int()
< MINIMUM_REQUIRED_TIME_SPINE_GRANULARITY.to_int()
):
raise ParsingError(
"The semantic layer requires a time spine model in the project, but none was found. "
"Guidance on creating this model can be found on our docs site ("
"https://docs.getdbt.com/docs/build/metricflow-time-spine) " # TODO: update docs link when available!
"The semantic layer requires a time spine model with granularity DAY or smaller in the project, "
"but none was found. Guidance on creating this model can be found on our docs site "
"(https://docs.getdbt.com/docs/build/metricflow-time-spine)." # TODO: update docs link when available!
)

# For backward compatibility: if legacy time spine exists, include it in the manifest.
if legacy_time_spine_model:
legacy_time_spine = LegacyTimeSpine(
location=legacy_time_spine_model.relation_name,
column_name="date_day",
grain=TimeGranularity.DAY,
grain=LEGACY_TIME_SPINE_GRANULARITY,
)
pydantic_semantic_manifest.project_configuration.time_spine_table_configurations = [
legacy_time_spine
Expand Down
2 changes: 1 addition & 1 deletion tests/functional/time_spines/test_time_spines.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,7 +147,7 @@ def test_time_spines(self, project):
result = runner.invoke(["parse"])
assert isinstance(result.exception, ParsingError)
assert (
"The semantic layer requires a time spine model in the project, but none was found."
"The semantic layer requires a time spine model with granularity DAY or smaller"
in result.exception.msg
)

Expand Down

0 comments on commit 219548b

Please sign in to comment.