Skip to content

Commit

Permalink
Rename default_grain -> default_granularity
Browse files Browse the repository at this point in the history
After discussing with core team members, we've decided this name is more consistent with other fields
  • Loading branch information
courtneyholcomb committed Jun 27, 2024
1 parent 0e55d39 commit 7c2ea37
Show file tree
Hide file tree
Showing 10 changed files with 53 additions and 45 deletions.
2 changes: 1 addition & 1 deletion .changes/unreleased/Features-20240614-140515.yaml
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
kind: Features
body: Add default_grain to metric spec.
body: Add default_granularity to metric spec.
time: 2024-06-14T14:05:15.355931-07:00
custom:
Author: courtneyholcomb
Expand Down
2 changes: 1 addition & 1 deletion dbt_semantic_interfaces/implementations/metric.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,7 +211,7 @@ def _implements_protocol(self) -> Metric: # noqa: D
metadata: Optional[PydanticMetadata]
label: Optional[str] = None
config: Optional[PydanticMetricConfig]
default_grain: Optional[TimeGranularity] = None
default_granularity: Optional[TimeGranularity] = None

@property
def input_measures(self) -> Sequence[PydanticMetricInputMeasure]:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -458,7 +458,7 @@
"config": {
"$ref": "#/definitions/metric_config_schema"
},
"default_grain": {
"default_granularity": {
"enum": [
"NANOSECOND",
"MICROSECOND",
Expand Down
2 changes: 1 addition & 1 deletion dbt_semantic_interfaces/parsing/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -304,7 +304,7 @@
"description": {"type": "string"},
"label": {"type": "string"},
"config": {"$ref": "metric_config_schema"},
"default_grain": {"enum": time_granularity_values},
"default_granularity": {"enum": time_granularity_values},
},
"additionalProperties": False,
"required": ["name", "type", "type_params"],
Expand Down
2 changes: 1 addition & 1 deletion dbt_semantic_interfaces/protocols/metric.py
Original file line number Diff line number Diff line change
Expand Up @@ -328,7 +328,7 @@ def label(self) -> Optional[str]:

@property
@abstractmethod
def default_grain(self) -> Optional[TimeGranularity]:
def default_granularity(self) -> Optional[TimeGranularity]:
"""Default grain used for the metric.
This will be used in a couple of circumstances:
Expand Down
4 changes: 2 additions & 2 deletions dbt_semantic_interfaces/test_utils.py
Original file line number Diff line number Diff line change
Expand Up @@ -123,7 +123,7 @@ def metric_with_guaranteed_meta(
type_params: PydanticMetricTypeParams,
metadata: PydanticMetadata = default_meta(),
description: str = "adhoc metric",
default_grain: Optional[TimeGranularity] = None,
default_granularity: Optional[TimeGranularity] = None,
) -> PydanticMetric:
"""Creates a metric with the given input.
Expand All @@ -136,7 +136,7 @@ def metric_with_guaranteed_meta(
type_params=type_params,
filter=None,
metadata=metadata,
default_grain=default_grain,
default_granularity=default_granularity,
)


Expand Down
12 changes: 6 additions & 6 deletions dbt_semantic_interfaces/transformations/default_grain.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,20 +17,20 @@


class SetDefaultGrainRule(ProtocolHint[SemanticManifestTransformRule[PydanticSemanticManifest]]):
"""If default_grain is not set for a metric, set it to DAY if available, else the smallest available grain."""
"""If default_granularity is not set for a metric, set it to DAY if available, else the smallest available grain."""

@override
def _implements_protocol(self) -> SemanticManifestTransformRule[PydanticSemanticManifest]: # noqa: D
return self

@staticmethod
def transform_model(semantic_manifest: PydanticSemanticManifest) -> PydanticSemanticManifest:
"""For each metric, set default_grain to DAY or the smallest granularity supported by all agg_time_dims."""
"""For each metric, set default_granularity to DAY or smallest granularity supported by all agg_time_dims."""
for metric in semantic_manifest.metrics:
if metric.default_grain:
if metric.default_granularity:
continue

default_grain = TimeGranularity.DAY
default_granularity = TimeGranularity.DAY
seen_agg_time_dimensions: Set[TimeDimensionReference] = set()
for semantic_model in semantic_manifest.semantic_models:
for measure_ref in set(metric.measure_references).intersection(semantic_model.measure_references):
Expand All @@ -41,8 +41,8 @@ def transform_model(semantic_manifest: PydanticSemanticManifest) -> PydanticSema
dimension = semantic_model.get_dimension(DimensionReference(agg_time_dimension_ref.element_name))
if (
dimension.type_params
and dimension.type_params.time_granularity.to_int() > default_grain.to_int()
and dimension.type_params.time_granularity.to_int() > default_granularity.to_int()
):
default_grain = dimension.type_params.time_granularity
default_granularity = dimension.type_params.time_granularity

return semantic_manifest
20 changes: 10 additions & 10 deletions dbt_semantic_interfaces/validations/metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -578,7 +578,7 @@ def validate_manifest(semantic_manifest: SemanticManifestT) -> Sequence[Validati


class DefaultGrainRule(SemanticManifestValidationRule[SemanticManifestT], Generic[SemanticManifestT]):
"""Checks that default_grain set for metric is queryable for that metric."""
"""Checks that default_granularity set for metric is queryable for that metric."""

@staticmethod
def _min_queryable_granularity_for_metric(
Expand All @@ -605,7 +605,7 @@ def _min_queryable_granularity_for_metric(

@staticmethod
@validate_safely(
whats_being_done="running model validation ensuring a metric's default_grain is valid for the metric"
whats_being_done="running model validation ensuring a metric's default_granularity is valid for the metric"
)
def _validate_metric(
metric: Metric,
Expand All @@ -618,7 +618,7 @@ def _validate_metric(
metric=MetricModelReference(metric_name=metric.name),
)

if metric.default_grain:
if metric.default_granularity:
min_queryable_granularity = DefaultGrainRule._min_queryable_granularity_for_metric(
metric=metric, metric_index=metric_index, measure_to_agg_time_dimension=measure_to_agg_time_dimension
)
Expand All @@ -627,25 +627,25 @@ def _validate_metric(
for granularity in TimeGranularity
if granularity.to_int() >= min_queryable_granularity.to_int()
]
if metric.default_grain.name not in valid_granularities:
if metric.default_granularity.name not in valid_granularities:
issues.append(
ValidationError(
context=context,
message=(
f"`default_grain` for metric '{metric.name}' must be >= {min_queryable_granularity.name}. "
"Valid options are those that are >= the largest granularity defined for the metric's "
f"measures' agg_time_dimensions. Got: {metric.default_grain.name}. "
f"Valid options: {valid_granularities}"
f"`default_granularity` for metric '{metric.name}' must be >= "
f"{min_queryable_granularity.name}. Valid options are those that are >= the largest "
f"granularity defined for the metric's measures' agg_time_dimensions. Got: "
f"{metric.default_granularity.name}. Valid options: {valid_granularities}"
),
)
)

return issues

@staticmethod
@validate_safely(whats_being_done="running manifest validation ensuring metric default_grains are valid")
@validate_safely(whats_being_done="running manifest validation ensuring metric default_granularitys are valid")
def validate_manifest(semantic_manifest: SemanticManifestT) -> Sequence[ValidationIssue]:
"""Validate that the default_grain for each metric is queryable for that metric.
"""Validate that the default_granularity for each metric is queryable for that metric.
TODO: figure out a more efficient way to reference other aspects of the model. This validation essentially
requires parsing the entire model, which could be slow and likely is repeated work. The blocker is that the
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,7 @@ metric:
name: "trailing_2_months_revenue"
description: "trailing_2_months_revenue"
type: cumulative
default_grain: month
default_granularity: month
type_params:
measure:
name: txn_revenue
Expand Down
50 changes: 29 additions & 21 deletions tests/validations/test_metrics.py
Original file line number Diff line number Diff line change
Expand Up @@ -716,7 +716,7 @@ def test_cumulative_metrics() -> None: # noqa: D
f"{missing_error_strings} in {set([x.as_readable_str() for x in build_issues])}"


def test_default_grain() -> None:
def test_default_granularity() -> None:
"""Test that default grain is validated appropriately."""
week_measure_name = "foo"
month_measure_name = "boo"
Expand Down Expand Up @@ -753,62 +753,66 @@ def test_default_grain() -> None:
metrics=[
# Simple metrics
metric_with_guaranteed_meta(
name="month_metric_with_no_default_grain_set",
name="month_metric_with_no_default_granularity_set",
type=MetricType.SIMPLE,
type_params=PydanticMetricTypeParams(
measure=PydanticMetricInputMeasure(name=month_measure_name),
),
),
metric_with_guaranteed_meta(
name="week_metric_with_valid_default_grain",
name="week_metric_with_valid_default_granularity",
type=MetricType.SIMPLE,
type_params=PydanticMetricTypeParams(
measure=PydanticMetricInputMeasure(name=week_measure_name),
),
default_grain=TimeGranularity.MONTH,
default_granularity=TimeGranularity.MONTH,
),
metric_with_guaranteed_meta(
name="month_metric_with_invalid_default_grain",
name="month_metric_with_invalid_default_granularity",
type=MetricType.SIMPLE,
type_params=PydanticMetricTypeParams(
measure=PydanticMetricInputMeasure(name=month_measure_name),
),
default_grain=TimeGranularity.WEEK,
default_granularity=TimeGranularity.WEEK,
),
# Derived metrics
metric_with_guaranteed_meta(
name="derived_metric_with_no_default_grain_set",
name="derived_metric_with_no_default_granularity_set",
type=MetricType.DERIVED,
type_params=PydanticMetricTypeParams(
metrics=[
PydanticMetricInput(name="week_metric_with_valid_default_grain"),
PydanticMetricInput(name="week_metric_with_valid_default_granularity"),
],
expr="week_metric_with_valid_default_grain + 1",
expr="week_metric_with_valid_default_granularity + 1",
),
),
metric_with_guaranteed_meta(
name="derived_metric_with_valid_default_grain",
name="derived_metric_with_valid_default_granularity",
type=MetricType.DERIVED,
type_params=PydanticMetricTypeParams(
metrics=[
PydanticMetricInput(name="week_metric_with_valid_default_grain"),
PydanticMetricInput(name="month_metric_with_no_default_grain_set"),
PydanticMetricInput(name="week_metric_with_valid_default_granularity"),
PydanticMetricInput(name="month_metric_with_no_default_granularity_set"),
],
expr="week_metric_with_valid_default_grain + month_metric_with_no_default_grain_set",
expr=(
"week_metric_with_valid_default_granularity + month_metric_with_no_default_granularity_set"
),
),
default_grain=TimeGranularity.YEAR,
default_granularity=TimeGranularity.YEAR,
),
metric_with_guaranteed_meta(
name="derived_metric_with_invalid_default_grain",
name="derived_metric_with_invalid_default_granularity",
type=MetricType.DERIVED,
type_params=PydanticMetricTypeParams(
metrics=[
PydanticMetricInput(name="week_metric_with_valid_default_grain"),
PydanticMetricInput(name="month_metric_with_no_default_grain_set"),
PydanticMetricInput(name="week_metric_with_valid_default_granularity"),
PydanticMetricInput(name="month_metric_with_no_default_granularity_set"),
],
expr="week_metric_with_valid_default_grain + month_metric_with_no_default_grain_set",
expr=(
"week_metric_with_valid_default_granularity + month_metric_with_no_default_granularity_set"
),
),
default_grain=TimeGranularity.DAY,
default_granularity=TimeGranularity.DAY,
),
],
project_configuration=EXAMPLE_PROJECT_CONFIGURATION,
Expand All @@ -817,8 +821,12 @@ def test_default_grain() -> None:

build_issues = validation_results.all_issues
assert len(build_issues) == 2
expected_substr1 = "`default_grain` for metric 'month_metric_with_invalid_default_grain' must be >= MONTH."
expected_substr2 = "`default_grain` for metric 'derived_metric_with_invalid_default_grain' must be >= MONTH."
expected_substr1 = (
"`default_granularity` for metric 'month_metric_with_invalid_default_granularity' must be >= MONTH."
)
expected_substr2 = (
"`default_granularity` for metric 'derived_metric_with_invalid_default_granularity' must be >= MONTH."
)
missing_error_strings = set()
for expected_str in [expected_substr1, expected_substr2]:
if not any(actual_str.as_readable_str().find(expected_str) != -1 for actual_str in build_issues):
Expand Down

0 comments on commit 7c2ea37

Please sign in to comment.