Skip to content

Commit

Permalink
[Backport to 0.4.latest] Add support for conversion metrics (#224)
Browse files Browse the repository at this point in the history
### Description
Backporting conversion metric support PRs to `0.4.latest`
- #221
- #209
<!---
Describe the Pull Request here. Add any references and info to help
reviewers
  understand your changes. Include any tradeoffs you considered.
-->

### Checklist

- [x] I have read [the contributing
guide](https://github.com/dbt-labs/dbt-semantic-interfaces/blob/main/CONTRIBUTING.md)
and understand what's expected of me
- [x] I have signed the
[CLA](https://docs.getdbt.com/docs/contributor-license-agreements)
- [x] This PR includes tests, or tests are not required/relevant for
this PR
- [x] I have run `changie new` to [create a changelog
entry](https://github.com/dbt-labs/dbt-semantic-interfaces/blob/main/CONTRIBUTING.md#adding-a-changelog-entry)
  • Loading branch information
WilliamDee authored Dec 7, 2023
1 parent 0f90d5e commit 0d896f9
Show file tree
Hide file tree
Showing 16 changed files with 668 additions and 9 deletions.
6 changes: 6 additions & 0 deletions .changes/unreleased/Features-20231127-124601.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Features
body: Added spec changes for conversion metrics.
time: 2023-11-27T12:46:01.036548-05:00
custom:
Author: WilliamDee
Issue: "210"
6 changes: 6 additions & 0 deletions .changes/unreleased/Features-20231127-150021.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
kind: Features
body: Added validation for conversion metric configurations.
time: 2023-11-27T15:00:21.734245-05:00
custom:
Author: WilliamDee
Issue: "211"
27 changes: 25 additions & 2 deletions dbt_semantic_interfaces/implementations/metric.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,11 @@
)
from dbt_semantic_interfaces.implementations.metadata import PydanticMetadata
from dbt_semantic_interfaces.references import MeasureReference, MetricReference
from dbt_semantic_interfaces.type_enums import MetricType, TimeGranularity
from dbt_semantic_interfaces.type_enums import (
ConversionCalculationType,
MetricType,
TimeGranularity,
)


class PydanticMetricInputMeasure(PydanticCustomInputParser, HashableBaseModel):
Expand Down Expand Up @@ -114,6 +118,13 @@ def parse(window: str) -> PydanticMetricTimeWindow:
)


class PydanticConstantPropertyInput(HashableBaseModel):
"""Input of a constant property used in conversion metrics."""

base_property: str
conversion_property: str


class PydanticMetricInput(HashableBaseModel):
"""Provides a pointer to a metric along with the additional properties used on that metric."""

Expand All @@ -134,6 +145,17 @@ def post_aggregation_reference(self) -> MetricReference:
return MetricReference(element_name=self.alias or self.name)


class PydanticConversionTypeParams(HashableBaseModel):
"""Type params to provide context for conversion metrics properties."""

base_measure: PydanticMetricInputMeasure
conversion_measure: PydanticMetricInputMeasure
entity: str
calculation: ConversionCalculationType = ConversionCalculationType.CONVERSION_RATE
window: Optional[PydanticMetricTimeWindow]
constant_properties: Optional[List[PydanticConstantPropertyInput]]


class PydanticMetricTypeParams(HashableBaseModel):
"""Type params add additional context to certain metric types (the context depends on the metric type)."""

Expand All @@ -144,6 +166,7 @@ class PydanticMetricTypeParams(HashableBaseModel):
window: Optional[PydanticMetricTimeWindow]
grain_to_date: Optional[TimeGranularity]
metrics: Optional[List[PydanticMetricInput]]
conversion_type_params: Optional[PydanticConversionTypeParams]

input_measures: List[PydanticMetricInputMeasure] = Field(default_factory=list)

Expand Down Expand Up @@ -172,7 +195,7 @@ def measure_references(self) -> List[MeasureReference]:
@property
def input_metrics(self) -> Sequence[PydanticMetricInput]:
"""Return the associated input metrics for this metric."""
if self.type is MetricType.SIMPLE or self.type is MetricType.CUMULATIVE:
if self.type is MetricType.SIMPLE or self.type is MetricType.CUMULATIVE or self.type is MetricType.CONVERSION:
return ()
elif self.type is MetricType.DERIVED:
assert self.type_params.metrics is not None, f"{MetricType.DERIVED} should have type_params.metrics set"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,61 @@
},
"type": "object"
},
"constant_property_input_schema": {
"$id": "constant_property_input_schema",
"additionalProperties": false,
"properties": {
"base_property": {
"type": "string"
},
"conversion_property": {
"type": "string"
}
},
"required": [
"base_property",
"conversion_property"
],
"type": "object"
},
"conversion_type_params_schema": {
"$id": "conversion_type_params_schema",
"additionalProperties": false,
"properties": {
"base_measure": {
"$ref": "#/definitions/metric_input_measure_schema"
},
"calculation": {
"enum": [
"CONVERSIONS",
"CONVERSION_RATE",
"conversions",
"conversion_rate"
]
},
"constant_properties": {
"items": {
"$ref": "#/definitions/constant_property_input_schema"
},
"type": "array"
},
"conversion_measure": {
"$ref": "#/definitions/metric_input_measure_schema"
},
"entity": {
"type": "string"
},
"window": {
"type": "string"
}
},
"required": [
"base_measure",
"conversion_measure",
"entity"
],
"type": "object"
},
"dimension_schema": {
"$id": "dimension_schema",
"additionalProperties": false,
Expand Down Expand Up @@ -347,10 +402,12 @@
"RATIO",
"CUMULATIVE",
"DERIVED",
"CONVERSION",
"simple",
"ratio",
"cumulative",
"derived"
"derived",
"conversion"
]
},
"type_params": {
Expand All @@ -368,6 +425,9 @@
"$id": "metric_type_params",
"additionalProperties": false,
"properties": {
"conversion_type_params": {
"$ref": "#/definitions/conversion_type_params_schema"
},
"denominator": {
"$ref": "#/definitions/metric_input_measure_schema"
},
Expand Down
34 changes: 33 additions & 1 deletion dbt_semantic_interfaces/parsing/schemas.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,12 @@


# Enums
metric_types_enum_values = ["SIMPLE", "RATIO", "CUMULATIVE", "DERIVED"]
metric_types_enum_values = ["SIMPLE", "RATIO", "CUMULATIVE", "DERIVED", "CONVERSION"]
metric_types_enum_values += [x.lower() for x in metric_types_enum_values]

calculation_types_enum_values = ["CONVERSIONS", "CONVERSION_RATE"]
calculation_types_enum_values += [x.lower() for x in calculation_types_enum_values]

entity_type_enum_values = ["PRIMARY", "UNIQUE", "FOREIGN", "NATURAL"]
entity_type_enum_values += [x.lower() for x in entity_type_enum_values]

Expand Down Expand Up @@ -85,6 +88,32 @@
"additionalProperties": False,
}

conversion_type_params_schema = {
"$id": "conversion_type_params_schema",
"type": "object",
"properties": {
"base_measure": {"$ref": "metric_input_measure_schema"},
"conversion_measure": {"$ref": "metric_input_measure_schema"},
"calculation": {"enum": calculation_types_enum_values},
"entity": {"type": "string"},
"window": {"type": "string"},
"constant_properties": {"type": "array", "items": {"$ref": "constant_property_input_schema"}},
},
"additionalProperties": False,
"required": ["base_measure", "conversion_measure", "entity"],
}

constant_property_input_schema = {
"$id": "constant_property_input_schema",
"type": "object",
"properties": {
"base_property": {"type": "string"},
"conversion_property": {"type": "string"},
},
"additionalProperties": False,
"required": ["base_property", "conversion_property"],
}

metric_type_params_schema = {
"$id": "metric_type_params",
"type": "object",
Expand All @@ -99,6 +128,7 @@
"type": "array",
"items": {"$ref": "metric_input_schema"},
},
"conversion_type_params": {"$ref": "conversion_type_params_schema"},
},
"additionalProperties": False,
}
Expand Down Expand Up @@ -382,6 +412,8 @@
filter_schema["$id"]: filter_schema,
metric_input_measure_schema["$id"]: metric_input_measure_schema,
metric_type_params_schema["$id"]: metric_type_params_schema,
conversion_type_params_schema["$id"]: conversion_type_params_schema,
constant_property_input_schema["$id"]: constant_property_input_schema,
entity_schema["$id"]: entity_schema,
measure_schema["$id"]: measure_schema,
dimension_schema["$id"]: dimension_schema,
Expand Down
2 changes: 2 additions & 0 deletions dbt_semantic_interfaces/protocols/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,8 @@
)
from dbt_semantic_interfaces.protocols.metadata import FileSlice, Metadata # noqa:F401
from dbt_semantic_interfaces.protocols.metric import ( # noqa:F401
ConstantPropertyInput,
ConversionTypeParams,
Metric,
MetricInput,
MetricInputMeasure,
Expand Down
72 changes: 71 additions & 1 deletion dbt_semantic_interfaces/protocols/metric.py
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,11 @@
from dbt_semantic_interfaces.protocols.metadata import Metadata
from dbt_semantic_interfaces.protocols.where_filter import WhereFilterIntersection
from dbt_semantic_interfaces.references import MeasureReference, MetricReference
from dbt_semantic_interfaces.type_enums import MetricType, TimeGranularity
from dbt_semantic_interfaces.type_enums import (
ConversionCalculationType,
MetricType,
TimeGranularity,
)


class MetricInputMeasure(Protocol):
Expand Down Expand Up @@ -113,6 +117,67 @@ def post_aggregation_reference(self) -> MetricReference:
pass


class ConstantPropertyInput(Protocol):
"""Provides the constant property set for conversion metrics.
Constant properties are additional elements linking a base event to a conversion event.
The specified properties will typically be a reference to a dimension or entity, and will be used
to join the base event to the final conversion event. Typical constant properties are things like
session keys (for services where conversions are measured within a user session), or secondary entities
(like a user/application pair for an app platform or a user/shop pair for a retail/online storefront platform).
"""

@property
@abstractmethod
def base_property(self) -> str: # noqa: D
pass

@property
@abstractmethod
def conversion_property(self) -> str: # noqa: D
pass


class ConversionTypeParams(Protocol):
"""Type params to provide context for conversion metrics properties."""

@property
@abstractmethod
def base_measure(self) -> MetricInputMeasure:
"""Measure used to calculate the base event."""
pass

@property
@abstractmethod
def conversion_measure(self) -> MetricInputMeasure:
"""Measure used to calculate the conversion event."""
pass

@property
@abstractmethod
def entity(self) -> str:
"""Specified join entity."""
pass

@property
@abstractmethod
def calculation(self) -> ConversionCalculationType:
"""Type of conversion metric calculation."""
pass

@property
@abstractmethod
def window(self) -> Optional[MetricTimeWindow]:
"""Maximum time range for finding successive conversion events."""
pass

@property
@abstractmethod
def constant_properties(self) -> Optional[Sequence[ConstantPropertyInput]]:
"""Return the list of defined constant properties."""
pass


class MetricTypeParams(Protocol):
"""Type params add additional context to certain metric types (the context depends on the metric type)."""

Expand Down Expand Up @@ -157,6 +222,11 @@ def grain_to_date(self) -> Optional[TimeGranularity]: # noqa: D
def metrics(self) -> Optional[Sequence[MetricInput]]: # noqa: D
pass

@property
@abstractmethod
def conversion_type_params(self) -> Optional[ConversionTypeParams]: # noqa: D
pass


class Metric(Protocol):
"""Describes a metric."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,11 @@ def _get_measures_for_metric(
measures.update(
AddInputMetricMeasuresRule._get_measures_for_metric(semantic_manifest, input_metric.name)
)
elif matched_metric.type is MetricType.CONVERSION:
conversion_type_params = matched_metric.type_params.conversion_type_params
assert conversion_type_params, "Conversion metric should have conversion_type_params."
measures.add(conversion_type_params.base_measure)
measures.add(conversion_type_params.conversion_measure)
else:
assert_values_exhausted(matched_metric.type)
else:
Expand Down
3 changes: 3 additions & 0 deletions dbt_semantic_interfaces/type_enums/__init__.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from dbt_semantic_interfaces.type_enums.aggregation_type import ( # noqa:F401
AggregationType,
)
from dbt_semantic_interfaces.type_enums.conversion_calculation_type import ( # noqa:F401
ConversionCalculationType,
)
from dbt_semantic_interfaces.type_enums.dimension_type import DimensionType # noqa:F401
from dbt_semantic_interfaces.type_enums.entity_type import EntityType # noqa:F401
from dbt_semantic_interfaces.type_enums.metric_type import MetricType # noqa:F401
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
from dbt_semantic_interfaces.enum_extension import ExtendedEnum


class ConversionCalculationType(ExtendedEnum):
"""Types of calculations for a conversion metric."""

CONVERSIONS = "conversions"
CONVERSION_RATE = "conversion_rate"
1 change: 1 addition & 0 deletions dbt_semantic_interfaces/type_enums/metric_type.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,3 +8,4 @@ class MetricType(ExtendedEnum):
RATIO = "ratio"
CUMULATIVE = "cumulative"
DERIVED = "derived"
CONVERSION = "conversion"
Loading

0 comments on commit 0d896f9

Please sign in to comment.