-
Notifications
You must be signed in to change notification settings - Fork 23
Add measures to metrics (temporary) & fix metric deduplication #436
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 5 commits
0b78216
a33e80a
c35aa8f
e35ed6e
217703e
933d8fb
157424b
bf33f74
74670d2
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -48,6 +48,7 @@ def test_cumulative_metric_with_custom_grain_to_date() -> None: # noqa: D | |
| type_params: | ||
| measure: | ||
| name: bookings | ||
| fill_nulls_with: 15 | ||
|
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. de-duplicating cut the number of metrics down by 1, but I wanted to make sure we had variety in our tests so I made this one require a new metric.
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. what a silly metric! |
||
| cumulative_type_params: | ||
| grain_to_date: martian_week | ||
| """ | ||
|
|
@@ -121,9 +122,9 @@ def test_cumulative_metric_with_custom_window() -> None: # noqa: D | |
| ) | ||
| assert not model.issues.has_blocking_issues | ||
| semantic_manifest = model.semantic_manifest | ||
| # 2 explicit ones and one that is created for the measure input for the | ||
| # cumulative metric's params | ||
| assert len(semantic_manifest.metrics) == 3 | ||
| # 2 explicit metrics. The cumulative metric's input metric should be deduplicated | ||
| # so it will match. | ||
| assert len(semantic_manifest.metrics) == 2 | ||
|
|
||
| metric = next((m for m in semantic_manifest.metrics if m.name == "test_cumulative_metric_with_custom_window"), None) | ||
| assert metric is not None, "Can't find metric" | ||
|
|
@@ -185,9 +186,9 @@ def test_conversion_metric_with_custom_grain_window() -> None: # noqa: D | |
| ) | ||
| assert not model.issues.has_blocking_issues | ||
| semantic_manifest = model.semantic_manifest | ||
| # 2 explicit ones and one that is created for the measure input for the | ||
| # cumulative metric's params | ||
| assert len(semantic_manifest.metrics) == 3 | ||
| # 2 explicitly created metrics. The conversion measure -> metric transformation | ||
| # should not need to create a new metric since the existing one already matches. | ||
| assert len(semantic_manifest.metrics) == 2 | ||
|
|
||
| metric = next( | ||
| (m for m in semantic_manifest.metrics if m.name == "test_conversion_metric_with_custom_grain_window"), None | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,226 @@ | ||
| from dbt_semantic_interfaces.implementations.elements.dimension import ( | ||
| PydanticDimension, | ||
| PydanticDimensionTypeParams, | ||
| ) | ||
| from dbt_semantic_interfaces.implementations.elements.entity import PydanticEntity | ||
| from dbt_semantic_interfaces.implementations.elements.measure import PydanticMeasure | ||
| from dbt_semantic_interfaces.implementations.metric import ( | ||
| PydanticCumulativeTypeParams, | ||
| PydanticMetric, | ||
| PydanticMetricInput, | ||
| PydanticMetricInputMeasure, | ||
| PydanticMetricTimeWindow, | ||
| PydanticMetricTypeParams, | ||
| ) | ||
| from dbt_semantic_interfaces.implementations.node_relation import PydanticNodeRelation | ||
| from dbt_semantic_interfaces.implementations.project_configuration import ( | ||
| PydanticProjectConfiguration, | ||
| ) | ||
| from dbt_semantic_interfaces.implementations.semantic_manifest import ( | ||
| PydanticSemanticManifest, | ||
| ) | ||
| from dbt_semantic_interfaces.implementations.semantic_model import ( | ||
| PydanticSemanticModel, | ||
| PydanticSemanticModelDefaults, | ||
| ) | ||
| from dbt_semantic_interfaces.transformations.semantic_manifest_transformer import ( | ||
| PydanticSemanticManifestTransformer, | ||
| ) | ||
| from dbt_semantic_interfaces.type_enums import ( | ||
| AggregationType, | ||
| DimensionType, | ||
| EntityType, | ||
| MetricType, | ||
| TimeGranularity, | ||
| ) | ||
| from dbt_semantic_interfaces.validations.semantic_manifest_validator import ( | ||
| SemanticManifestValidator, | ||
| ) | ||
|
|
||
|
|
||
| def _project_config() -> PydanticProjectConfiguration: | ||
| return PydanticProjectConfiguration() | ||
|
|
||
|
|
||
| def _build_semantic_model_with_creatable_measure( | ||
| sm_name: str, | ||
| time_dim_name: str = "ds", | ||
| ) -> PydanticSemanticModel: | ||
| return PydanticSemanticModel( | ||
| name=sm_name, | ||
| defaults=PydanticSemanticModelDefaults(agg_time_dimension=time_dim_name), | ||
| node_relation=PydanticNodeRelation(alias=sm_name, schema_name="schema"), | ||
| entities=[ | ||
| PydanticEntity( | ||
| name="user", | ||
| type=EntityType.PRIMARY, | ||
| expr="user_id", | ||
| ), | ||
| ], | ||
| dimensions=[ | ||
| PydanticDimension( | ||
| name="ds", | ||
| type=DimensionType.TIME, | ||
| type_params=PydanticDimensionTypeParams(time_granularity=TimeGranularity.DAY), | ||
| ), | ||
| PydanticDimension( | ||
| name="created_at", | ||
| type=DimensionType.TIME, | ||
| type_params=PydanticDimensionTypeParams(time_granularity=TimeGranularity.DAY), | ||
| ), | ||
| PydanticDimension( | ||
| name="ds_partitioned", | ||
| type=DimensionType.TIME, | ||
| is_partition=True, | ||
| type_params=PydanticDimensionTypeParams(time_granularity=TimeGranularity.DAY), | ||
| ), | ||
| PydanticDimension( | ||
| name="home_state", | ||
| type=DimensionType.CATEGORICAL, | ||
| ), | ||
| PydanticDimension( | ||
| name="last_profile_edit_ts", | ||
| type=DimensionType.TIME, | ||
| type_params=PydanticDimensionTypeParams(time_granularity=TimeGranularity.MILLISECOND), | ||
| ), | ||
| PydanticDimension( | ||
| name="bio_added_ts", | ||
| type=DimensionType.TIME, | ||
| type_params=PydanticDimensionTypeParams(time_granularity=TimeGranularity.SECOND), | ||
| ), | ||
| PydanticDimension( | ||
| name="last_login_ts", | ||
| type=DimensionType.TIME, | ||
| type_params=PydanticDimensionTypeParams(time_granularity=TimeGranularity.MINUTE), | ||
| ), | ||
| PydanticDimension( | ||
| name="archived_at", | ||
| type=DimensionType.TIME, | ||
| type_params=PydanticDimensionTypeParams(time_granularity=TimeGranularity.HOUR), | ||
| ), | ||
| ], | ||
| measures=[ | ||
| PydanticMeasure( | ||
| name="archived_users", | ||
| agg=AggregationType.SUM, | ||
| expr="1", | ||
| create_metric=True, | ||
| ) | ||
| ], | ||
| ) | ||
|
|
||
|
|
||
| def test_e2e_measure_create_metric_then_cumulative_uses_metric_input() -> None: | ||
| """End-to-end: measure create_metric=True, cumulative references created metric by name.""" | ||
| sm = _build_semantic_model_with_creatable_measure("sm", time_dim_name="ds") | ||
|
|
||
| metrics = [ | ||
| PydanticMetric( | ||
| name="subdaily_cumulative_window_metric", | ||
| type=MetricType.CUMULATIVE, | ||
| description="m1_cumulative_1 description", | ||
| type_params=PydanticMetricTypeParams( | ||
| measure=PydanticMetricInputMeasure(name="archived_users"), | ||
| cumulative_type_params=PydanticCumulativeTypeParams( | ||
| window=PydanticMetricTimeWindow(count=3, granularity="hour"), | ||
| ), | ||
| ), | ||
| ), | ||
| PydanticMetric( | ||
| name="subdaily_cumulative_grain_to_date_metric", | ||
| type=MetricType.CUMULATIVE, | ||
| description="m1_cumulative_2 description", | ||
| type_params=PydanticMetricTypeParams( | ||
| measure=PydanticMetricInputMeasure(name="archived_users"), | ||
| cumulative_type_params=PydanticCumulativeTypeParams( | ||
| grain_to_date="hour", | ||
| ), | ||
| ), | ||
| ), | ||
| PydanticMetric( | ||
| name="subdaily_offset_window_metric", | ||
| type=MetricType.DERIVED, | ||
| description="archived_users_offset_window description", | ||
| type_params=PydanticMetricTypeParams( | ||
| expr="archived_users", | ||
| metrics=[ | ||
| PydanticMetricInput( | ||
| name="archived_users", | ||
| offset_window=PydanticMetricTimeWindow(count=1, granularity="hour"), | ||
| ) | ||
| ], | ||
| ), | ||
| ), | ||
| PydanticMetric( | ||
| name="subdaily_offset_grain_to_date_metric", | ||
| type=MetricType.DERIVED, | ||
| description="offset grain to date metric with a sub-daily agg time dim", | ||
| type_params=PydanticMetricTypeParams( | ||
| expr="archived_users", | ||
| metrics=[ | ||
| PydanticMetricInput( | ||
| name="archived_users", | ||
| offset_to_grain="hour", | ||
| ) | ||
| ], | ||
| ), | ||
| ), | ||
| PydanticMetric( | ||
| name="subdaily_join_to_time_spine_metric", | ||
| type=MetricType.SIMPLE, | ||
| description="simple metric with sub-daily agg time dim that joins to time spine", | ||
| type_params=PydanticMetricTypeParams( | ||
| measure=PydanticMetricInputMeasure( | ||
| name="archived_users", | ||
| join_to_timespine=True, | ||
| ), | ||
| ), | ||
| ), | ||
| PydanticMetric( | ||
| name="simple_subdaily_metric_default_day", | ||
| type=MetricType.SIMPLE, | ||
| description="simple metric with sub-daily agg time dim that doesn't specify default granularity", | ||
| type_params=PydanticMetricTypeParams( | ||
| measure=PydanticMetricInputMeasure( | ||
| name="archived_users", | ||
| ), | ||
| ), | ||
| ), | ||
| PydanticMetric( | ||
| name="simple_subdaily_metric_default_hour", | ||
| type=MetricType.SIMPLE, | ||
| description="simple metric with sub-daily agg time dim that has an explicit default granularity", | ||
| type_params=PydanticMetricTypeParams( | ||
| measure=PydanticMetricInputMeasure( | ||
| name="archived_users", | ||
| ), | ||
| ), | ||
| time_granularity="hour", | ||
| ), | ||
| PydanticMetric( | ||
| name="archived_users_join_to_time_spine", | ||
| type=MetricType.SIMPLE, | ||
| description="subdaily metric joining to time spine", | ||
| type_params=PydanticMetricTypeParams( | ||
| measure=PydanticMetricInputMeasure( | ||
| name="archived_users", | ||
| join_to_timespine=True, | ||
| ), | ||
| ), | ||
| ), | ||
| ] | ||
|
|
||
| manifest = PydanticSemanticManifest( | ||
| semantic_models=[sm], | ||
| metrics=metrics, | ||
| project_configuration=_project_config(), | ||
| ) | ||
|
|
||
| transformed = PydanticSemanticManifestTransformer.transform(model=manifest) | ||
|
|
||
| model_validator = SemanticManifestValidator[PydanticSemanticManifest]() | ||
| model_validator.checked_validations(transformed) | ||
|
|
||
| # Expect exactly 1 new metric - the proxy simple metric created for the measure | ||
| assert len(transformed.metrics) == len(metrics) + 1 | ||
| assert any(m for m in transformed.metrics if m.type == MetricType.SIMPLE and m.name == "archived_users") |
Uh oh!
There was an error while loading. Please reload this page.