Skip to content
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

Add Conversion Metrics Properties #209

Merged
merged 9 commits into from
Nov 29, 2023
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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"
43 changes: 41 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,27 @@ 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]]

@property
def base_measure_reference(self) -> MeasureReference:
"""Return the measure reference associated with the base measure."""
return self.base_measure.measure_reference

@property
def conversion_measure_reference(self) -> MeasureReference:
"""Return the measure reference associated with the conversion measure."""
return self.conversion_measure.measure_reference


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

Expand All @@ -144,9 +176,16 @@ 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)

@property
def conversion_type_params_or_error(self) -> PydanticConversionTypeParams: # noqa: D
if self.conversion_type_params is None:
raise ValueError("Expected conversion_type_params to be not None.")
return self.conversion_type_params


class PydanticMetric(HashableBaseModel, ModelWithMetadataParsing):
"""Describes a metric."""
Expand All @@ -172,7 +211,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"]
WilliamDee marked this conversation as resolved.
Show resolved Hide resolved
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
85 changes: 84 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,75 @@ def post_aggregation_reference(self) -> MetricReference:
pass


class ConstantPropertyInput(Protocol):
"""Provides the constant property values for conversion metrics.

The specified property will be a reference of a dimension/entity.
WilliamDee marked this conversation as resolved.
Show resolved Hide resolved
"""

@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 successing conversion events."""
WilliamDee marked this conversation as resolved.
Show resolved Hide resolved
pass

@property
@abstractmethod
def base_measure_reference(self) -> MeasureReference:
"""Return the measure reference associated with the base measure."""
pass

@property
@abstractmethod
def conversion_measure_reference(self) -> MeasureReference:
"""Return the measure reference associated with the conversion measure."""
pass
WilliamDee marked this conversation as resolved.
Show resolved Hide resolved

@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 +230,16 @@ 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

@property
@abstractmethod
def conversion_type_params_or_error(self) -> ConversionTypeParams: # noqa: D
WilliamDee marked this conversation as resolved.
Show resolved Hide resolved
pass


class Metric(Protocol):
"""Describes a metric."""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,9 @@ def _get_measures_for_metric(
measures.update(
AddInputMetricMeasuresRule._get_measures_for_metric(semantic_manifest, input_metric.name)
)
elif matched_metric.type is MetricType.CONVERSION:
measures.add(matched_metric.type_params.conversion_type_params_or_error.base_measure)
measures.add(matched_metric.type_params.conversion_type_params_or_error.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
Loading