mirror of
https://github.com/dbt-labs/dbt-core
synced 2025-12-17 19:31:34 +00:00
Compare commits
8 Commits
poc/microb
...
will/conve
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
b8ace8ae21 | ||
|
|
028ebf84c1 | ||
|
|
83977dbf31 | ||
|
|
d428daa6a1 | ||
|
|
0f322ed8cb | ||
|
|
67f46f4d2b | ||
|
|
fb81b74835 | ||
|
|
c7559cdb4c |
7
.changes/unreleased/Features-20231206-181458.yaml
Normal file
7
.changes/unreleased/Features-20231206-181458.yaml
Normal file
@@ -0,0 +1,7 @@
|
||||
kind: Features
|
||||
body: Adds support for parsing conversion metric related properties for the semantic
|
||||
layer.
|
||||
time: 2023-12-06T18:14:58.688221-05:00
|
||||
custom:
|
||||
Author: WilliamDee
|
||||
Issue: "9203"
|
||||
@@ -5,7 +5,7 @@ from typing import Any, Dict, Iterator, List
|
||||
|
||||
|
||||
DERIVED_METRICS = [MetricType.DERIVED, MetricType.RATIO]
|
||||
BASE_METRICS = [MetricType.SIMPLE, MetricType.CUMULATIVE]
|
||||
BASE_METRICS = [MetricType.SIMPLE, MetricType.CUMULATIVE, MetricType.CONVERSION]
|
||||
|
||||
|
||||
class MetricReference(object):
|
||||
|
||||
@@ -21,6 +21,7 @@ from dbt.contracts.graph.semantic_models import (
|
||||
SourceFileMetadata,
|
||||
)
|
||||
from dbt.contracts.graph.unparsed import (
|
||||
ConstantPropertyInput,
|
||||
Docs,
|
||||
ExposureType,
|
||||
ExternalTable,
|
||||
@@ -59,7 +60,11 @@ from dbt_semantic_interfaces.references import (
|
||||
TimeDimensionReference,
|
||||
)
|
||||
from dbt_semantic_interfaces.references import MetricReference as DSIMetricReference
|
||||
from dbt_semantic_interfaces.type_enums import MetricType, TimeGranularity
|
||||
from dbt_semantic_interfaces.type_enums import (
|
||||
ConversionCalculationType,
|
||||
MetricType,
|
||||
TimeGranularity,
|
||||
)
|
||||
|
||||
from .model_config import (
|
||||
NodeConfig,
|
||||
@@ -1435,6 +1440,16 @@ class MetricInput(dbtClassMixin):
|
||||
return DSIMetricReference(element_name=self.alias or self.name)
|
||||
|
||||
|
||||
@dataclass
|
||||
class ConversionTypeParams(dbtClassMixin):
|
||||
base_measure: MetricInputMeasure
|
||||
conversion_measure: MetricInputMeasure
|
||||
entity: str
|
||||
calculation: ConversionCalculationType = ConversionCalculationType.CONVERSION_RATE
|
||||
window: Optional[MetricTimeWindow] = None
|
||||
constant_properties: Optional[List[ConstantPropertyInput]] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class MetricTypeParams(dbtClassMixin):
|
||||
measure: Optional[MetricInputMeasure] = None
|
||||
@@ -1445,6 +1460,7 @@ class MetricTypeParams(dbtClassMixin):
|
||||
window: Optional[MetricTimeWindow] = None
|
||||
grain_to_date: Optional[TimeGranularity] = None
|
||||
metrics: Optional[List[MetricInput]] = None
|
||||
conversion_type_params: Optional[ConversionTypeParams] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
@@ -19,6 +19,7 @@ import dbt.helper_types # noqa:F401
|
||||
from dbt.exceptions import CompilationError, ParsingError, DbtInternalError
|
||||
|
||||
from dbt.dataclass_schema import dbtClassMixin, StrEnum, ExtensibleDbtClassMixin, ValidationError
|
||||
from dbt_semantic_interfaces.type_enums import ConversionCalculationType
|
||||
|
||||
from dataclasses import dataclass, field
|
||||
from datetime import timedelta
|
||||
@@ -597,6 +598,24 @@ class UnparsedMetricInput(dbtClassMixin):
|
||||
offset_to_grain: Optional[str] = None # str is really a TimeGranularity Enum
|
||||
|
||||
|
||||
@dataclass
|
||||
class ConstantPropertyInput(dbtClassMixin):
|
||||
base_property: str
|
||||
conversion_property: str
|
||||
|
||||
|
||||
@dataclass
|
||||
class UnparsedConversionTypeParams(dbtClassMixin):
|
||||
base_measure: Union[UnparsedMetricInputMeasure, str]
|
||||
conversion_measure: Union[UnparsedMetricInputMeasure, str]
|
||||
entity: str
|
||||
calculation: str = (
|
||||
ConversionCalculationType.CONVERSION_RATE.value
|
||||
) # ConversionCalculationType Enum
|
||||
window: Optional[str] = None
|
||||
constant_properties: Optional[List[ConstantPropertyInput]] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
class UnparsedMetricTypeParams(dbtClassMixin):
|
||||
measure: Optional[Union[UnparsedMetricInputMeasure, str]] = None
|
||||
@@ -606,6 +625,7 @@ class UnparsedMetricTypeParams(dbtClassMixin):
|
||||
window: Optional[str] = None
|
||||
grain_to_date: Optional[str] = None # str is really a TimeGranularity Enum
|
||||
metrics: Optional[List[Union[UnparsedMetricInput, str]]] = None
|
||||
conversion_type_params: Optional[UnparsedConversionTypeParams] = None
|
||||
|
||||
|
||||
@dataclass
|
||||
|
||||
@@ -1527,6 +1527,34 @@ def _process_refs(
|
||||
node.depends_on.add_node(target_model_id)
|
||||
|
||||
|
||||
def _process_metric_depends_on(
|
||||
manifest: Manifest,
|
||||
current_project: str,
|
||||
metric: Metric,
|
||||
) -> None:
|
||||
"""For a given metric, set the `depends_on` property"""
|
||||
|
||||
assert len(metric.type_params.input_measures) > 0
|
||||
for input_measure in metric.type_params.input_measures:
|
||||
target_semantic_model = manifest.resolve_semantic_model_for_measure(
|
||||
target_measure_name=input_measure.name,
|
||||
current_project=current_project,
|
||||
node_package=metric.package_name,
|
||||
)
|
||||
if target_semantic_model is None:
|
||||
raise dbt.exceptions.ParsingError(
|
||||
f"A semantic model having a measure `{input_measure.name}` does not exist but was referenced.",
|
||||
node=metric,
|
||||
)
|
||||
if target_semantic_model.config.enabled is False:
|
||||
raise dbt.exceptions.ParsingError(
|
||||
f"The measure `{input_measure.name}` is referenced on disabled semantic model `{target_semantic_model.name}`.",
|
||||
node=metric,
|
||||
)
|
||||
|
||||
metric.depends_on.add_node(target_semantic_model.unique_id)
|
||||
|
||||
|
||||
def _process_metric_node(
|
||||
manifest: Manifest,
|
||||
current_project: str,
|
||||
@@ -1546,24 +1574,19 @@ def _process_metric_node(
|
||||
metric.type_params.measure is not None
|
||||
), f"{metric} should have a measure defined, but it does not."
|
||||
metric.type_params.input_measures.append(metric.type_params.measure)
|
||||
target_semantic_model = manifest.resolve_semantic_model_for_measure(
|
||||
target_measure_name=metric.type_params.measure.name,
|
||||
current_project=current_project,
|
||||
node_package=metric.package_name,
|
||||
_process_metric_depends_on(
|
||||
manifest=manifest, current_project=current_project, metric=metric
|
||||
)
|
||||
elif metric.type is MetricType.CONVERSION:
|
||||
conversion_type_params = metric.type_params.conversion_type_params
|
||||
assert (
|
||||
conversion_type_params
|
||||
), f"{metric.name} is a conversion metric and must have conversion_type_params defined."
|
||||
metric.type_params.input_measures.append(conversion_type_params.base_measure)
|
||||
metric.type_params.input_measures.append(conversion_type_params.conversion_measure)
|
||||
_process_metric_depends_on(
|
||||
manifest=manifest, current_project=current_project, metric=metric
|
||||
)
|
||||
if target_semantic_model is None:
|
||||
raise dbt.exceptions.ParsingError(
|
||||
f"A semantic model having a measure `{metric.type_params.measure.name}` does not exist but was referenced.",
|
||||
node=metric,
|
||||
)
|
||||
if target_semantic_model.config.enabled is False:
|
||||
raise dbt.exceptions.ParsingError(
|
||||
f"The measure `{metric.type_params.measure.name}` is referenced on disabled semantic model `{target_semantic_model.name}`.",
|
||||
node=metric,
|
||||
)
|
||||
|
||||
metric.depends_on.add_node(target_semantic_model.unique_id)
|
||||
|
||||
elif metric.type is MetricType.DERIVED or metric.type is MetricType.RATIO:
|
||||
input_metrics = metric.input_metrics
|
||||
if metric.type is MetricType.RATIO:
|
||||
|
||||
@@ -17,6 +17,7 @@ from dbt.contracts.graph.unparsed import (
|
||||
UnparsedQueryParams,
|
||||
UnparsedSavedQuery,
|
||||
UnparsedSemanticModel,
|
||||
UnparsedConversionTypeParams,
|
||||
)
|
||||
from dbt.contracts.graph.model_config import SavedQueryConfig
|
||||
from dbt.contracts.graph.nodes import (
|
||||
@@ -29,6 +30,7 @@ from dbt.contracts.graph.nodes import (
|
||||
MetricTypeParams,
|
||||
SemanticModel,
|
||||
SavedQuery,
|
||||
ConversionTypeParams,
|
||||
)
|
||||
from dbt.contracts.graph.saved_queries import Export, ExportConfig, QueryParams
|
||||
from dbt.contracts.graph.semantic_layer_common import WhereFilter, WhereFilterIntersection
|
||||
@@ -52,6 +54,7 @@ from dbt.clients.jinja import get_rendered
|
||||
from dbt.dataclass_schema import ValidationError
|
||||
from dbt_semantic_interfaces.type_enums import (
|
||||
AggregationType,
|
||||
ConversionCalculationType,
|
||||
DimensionType,
|
||||
EntityType,
|
||||
MetricType,
|
||||
@@ -226,7 +229,7 @@ class MetricParser(YamlReader):
|
||||
self.yaml.path,
|
||||
"window",
|
||||
{"window": unparsed_window},
|
||||
f"Invalid window ({unparsed_window}) in cumulative metric. Should be of the form `<count> <granularity>`, "
|
||||
f"Invalid window ({unparsed_window}) in cumulative/conversion metric. Should be of the form `<count> <granularity>`, "
|
||||
"e.g., `28 days`",
|
||||
)
|
||||
|
||||
@@ -240,7 +243,7 @@ class MetricParser(YamlReader):
|
||||
self.yaml.path,
|
||||
"window",
|
||||
{"window": unparsed_window},
|
||||
f"Invalid time granularity {granularity} in cumulative metric window string: ({unparsed_window})",
|
||||
f"Invalid time granularity {granularity} in cumulative/conversion metric window string: ({unparsed_window})",
|
||||
)
|
||||
|
||||
count = parts[0]
|
||||
@@ -249,7 +252,7 @@ class MetricParser(YamlReader):
|
||||
self.yaml.path,
|
||||
"window",
|
||||
{"window": unparsed_window},
|
||||
f"Invalid count ({count}) in cumulative metric window string: ({unparsed_window})",
|
||||
f"Invalid count ({count}) in cumulative/conversion metric window string: ({unparsed_window})",
|
||||
)
|
||||
|
||||
return MetricTimeWindow(
|
||||
@@ -295,6 +298,20 @@ class MetricParser(YamlReader):
|
||||
|
||||
return metric_inputs
|
||||
|
||||
def _get_optional_conversion_type_params(
|
||||
self, unparsed: Optional[UnparsedConversionTypeParams]
|
||||
) -> Optional[ConversionTypeParams]:
|
||||
if unparsed is None:
|
||||
return None
|
||||
return ConversionTypeParams(
|
||||
base_measure=self._get_input_measure(unparsed.base_measure),
|
||||
conversion_measure=self._get_input_measure(unparsed.conversion_measure),
|
||||
entity=unparsed.entity,
|
||||
calculation=ConversionCalculationType(unparsed.calculation),
|
||||
window=self._get_time_window(unparsed.window),
|
||||
constant_properties=unparsed.constant_properties,
|
||||
)
|
||||
|
||||
def _get_metric_type_params(self, type_params: UnparsedMetricTypeParams) -> MetricTypeParams:
|
||||
grain_to_date: Optional[TimeGranularity] = None
|
||||
if type_params.grain_to_date is not None:
|
||||
@@ -308,6 +325,9 @@ class MetricParser(YamlReader):
|
||||
window=self._get_time_window(type_params.window),
|
||||
grain_to_date=grain_to_date,
|
||||
metrics=self._get_metric_inputs(type_params.metrics),
|
||||
conversion_type_params=self._get_optional_conversion_type_params(
|
||||
type_params.conversion_type_params
|
||||
)
|
||||
# input measures are calculated via metric processing post parsing
|
||||
# input_measures=?,
|
||||
)
|
||||
|
||||
@@ -73,7 +73,7 @@ setup(
|
||||
# These are major-version-0 packages also maintained by dbt-labs. Accept patches.
|
||||
"dbt-extractor~=0.5.0",
|
||||
"minimal-snowplow-tracker~=0.0.2",
|
||||
"dbt-semantic-interfaces~=0.4.0",
|
||||
"dbt-semantic-interfaces~=0.5.0a2",
|
||||
# ----
|
||||
# Expect compatibility with all new versions of these packages, so lower bounds only.
|
||||
"jsonschema>=3.0",
|
||||
|
||||
@@ -7843,7 +7843,8 @@
|
||||
"simple",
|
||||
"ratio",
|
||||
"cumulative",
|
||||
"derived"
|
||||
"derived",
|
||||
"conversion"
|
||||
]
|
||||
},
|
||||
"type_params": {
|
||||
@@ -8410,6 +8411,241 @@
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
},
|
||||
"conversion_type_params": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"title": "ConversionTypeParams",
|
||||
"properties": {
|
||||
"base_measure": {
|
||||
"type": "object",
|
||||
"title": "MetricInputMeasure",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"filter": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"title": "WhereFilterIntersection",
|
||||
"properties": {
|
||||
"where_filters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"title": "WhereFilter",
|
||||
"properties": {
|
||||
"where_sql_template": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"where_sql_template"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"where_filters"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
},
|
||||
"alias": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
},
|
||||
"join_to_timespine": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"fill_nulls_with": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"conversion_measure": {
|
||||
"type": "object",
|
||||
"title": "MetricInputMeasure",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"filter": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"title": "WhereFilterIntersection",
|
||||
"properties": {
|
||||
"where_filters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"title": "WhereFilter",
|
||||
"properties": {
|
||||
"where_sql_template": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"where_sql_template"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"where_filters"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
},
|
||||
"alias": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
},
|
||||
"join_to_timespine": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"fill_nulls_with": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"entity": {
|
||||
"type": "string"
|
||||
},
|
||||
"calculation": {
|
||||
"enum": [
|
||||
"conversions",
|
||||
"conversion_rate"
|
||||
],
|
||||
"default": "conversion_rate"
|
||||
},
|
||||
"window": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"title": "MetricTimeWindow",
|
||||
"properties": {
|
||||
"count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"granularity": {
|
||||
"enum": [
|
||||
"day",
|
||||
"week",
|
||||
"month",
|
||||
"quarter",
|
||||
"year"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"count",
|
||||
"granularity"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
},
|
||||
"constant_properties": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"title": "ConstantPropertyInput",
|
||||
"properties": {
|
||||
"base_property": {
|
||||
"type": "string"
|
||||
},
|
||||
"conversion_property": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"base_property",
|
||||
"conversion_property"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"base_measure",
|
||||
"conversion_measure",
|
||||
"entity"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
@@ -16264,7 +16500,8 @@
|
||||
"simple",
|
||||
"ratio",
|
||||
"cumulative",
|
||||
"derived"
|
||||
"derived",
|
||||
"conversion"
|
||||
]
|
||||
},
|
||||
"type_params": {
|
||||
@@ -16831,6 +17068,241 @@
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
},
|
||||
"conversion_type_params": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"title": "ConversionTypeParams",
|
||||
"properties": {
|
||||
"base_measure": {
|
||||
"type": "object",
|
||||
"title": "MetricInputMeasure",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"filter": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"title": "WhereFilterIntersection",
|
||||
"properties": {
|
||||
"where_filters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"title": "WhereFilter",
|
||||
"properties": {
|
||||
"where_sql_template": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"where_sql_template"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"where_filters"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
},
|
||||
"alias": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
},
|
||||
"join_to_timespine": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"fill_nulls_with": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"conversion_measure": {
|
||||
"type": "object",
|
||||
"title": "MetricInputMeasure",
|
||||
"properties": {
|
||||
"name": {
|
||||
"type": "string"
|
||||
},
|
||||
"filter": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"title": "WhereFilterIntersection",
|
||||
"properties": {
|
||||
"where_filters": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"title": "WhereFilter",
|
||||
"properties": {
|
||||
"where_sql_template": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"where_sql_template"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"where_filters"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
},
|
||||
"alias": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "string"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
},
|
||||
"join_to_timespine": {
|
||||
"type": "boolean",
|
||||
"default": false
|
||||
},
|
||||
"fill_nulls_with": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "integer"
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"name"
|
||||
]
|
||||
},
|
||||
"entity": {
|
||||
"type": "string"
|
||||
},
|
||||
"calculation": {
|
||||
"enum": [
|
||||
"conversions",
|
||||
"conversion_rate"
|
||||
],
|
||||
"default": "conversion_rate"
|
||||
},
|
||||
"window": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "object",
|
||||
"title": "MetricTimeWindow",
|
||||
"properties": {
|
||||
"count": {
|
||||
"type": "integer"
|
||||
},
|
||||
"granularity": {
|
||||
"enum": [
|
||||
"day",
|
||||
"week",
|
||||
"month",
|
||||
"quarter",
|
||||
"year"
|
||||
]
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"count",
|
||||
"granularity"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
},
|
||||
"constant_properties": {
|
||||
"anyOf": [
|
||||
{
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "object",
|
||||
"title": "ConstantPropertyInput",
|
||||
"properties": {
|
||||
"base_property": {
|
||||
"type": "string"
|
||||
},
|
||||
"conversion_property": {
|
||||
"type": "string"
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"base_property",
|
||||
"conversion_property"
|
||||
]
|
||||
}
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
}
|
||||
},
|
||||
"additionalProperties": false,
|
||||
"required": [
|
||||
"base_measure",
|
||||
"conversion_measure",
|
||||
"entity"
|
||||
]
|
||||
},
|
||||
{
|
||||
"type": "null"
|
||||
}
|
||||
],
|
||||
"default": null
|
||||
}
|
||||
},
|
||||
"additionalProperties": false
|
||||
|
||||
@@ -1 +1 @@
|
||||
{"metadata": {"dbt_schema_version": "https://schemas.getdbt.com/dbt/run-results/v6.json", "dbt_version": "1.8.0a1", "generated_at": "2023-12-01T00:27:31.475276Z", "invocation_id": "6dbe790d-e31a-45d3-bf5c-b67232e7e45c", "env": {}}, "results": [{"status": "success", "timing": [{"name": "compile", "started_at": "2023-12-01T00:27:31.399416Z", "completed_at": "2023-12-01T00:27:31.406369Z"}, {"name": "execute", "started_at": "2023-12-01T00:27:31.407149Z", "completed_at": "2023-12-01T00:27:31.458669Z"}], "thread_id": "Thread-8", "execution_time": 0.0613861083984375, "adapter_response": {"_message": "CREATE VIEW", "code": "CREATE VIEW", "rows_affected": -1}, "message": "CREATE VIEW", "failures": null, "unique_id": "model.test.metricflow_time_spine", "compiled": true, "compiled_code": "SELECT to_date('02/20/2023', 'mm/dd/yyyy') as date_day", "relation_name": "\"dbt\".\"test17013904484151129162_test_previous_version_state\".\"metricflow_time_spine\""}, {"status": "success", "timing": [{"name": "compile", "started_at": "2023-12-01T00:27:31.401527Z", "completed_at": "2023-12-01T00:27:31.402989Z"}, {"name": "execute", "started_at": "2023-12-01T00:27:31.403533Z", "completed_at": "2023-12-01T00:27:31.460610Z"}], "thread_id": "Thread-9", "execution_time": 0.0629112720489502, "adapter_response": {"_message": "CREATE VIEW", "code": "CREATE VIEW", "rows_affected": -1}, "message": "CREATE VIEW", "failures": null, "unique_id": "model.test.my_model", "compiled": true, "compiled_code": "select 1 as id", "relation_name": "\"dbt\".\"test17013904484151129162_test_previous_version_state\".\"my_model\""}], "elapsed_time": 0.5424928665161133, "args": {"log_format_file": "debug", "profiles_dir": "/private/var/folders/79/5290gpvn3lx5jdryk4844rm80000gn/T/pytest-of-quigleymalcolm/pytest-449/profile0", "defer": false, "favor_state": false, "populate_cache": true, "partial_parse": true, "select": [], "show_resource_report": false, "version_check": true, "strict_mode": false, "introspect": true, "project_dir": "/private/var/folders/79/5290gpvn3lx5jdryk4844rm80000gn/T/pytest-of-quigleymalcolm/pytest-449/project0", "indirect_selection": "eager", "vars": {}, "log_level_file": "debug", "log_level": "info", "printer_width": 80, "cache_selected_only": false, "which": "run", "log_path": "/Users/quigleymalcolm/Developer/dbt-labs/dbt-core/logs/test17013904484151129162", "write_json": true, "log_file_max_bytes": 10485760, "macro_debugging": true, "static_parser": true, "enable_legacy_logger": false, "warn_error_options": {"include": [], "exclude": []}, "invocation_command": "dbt tests/functional/artifacts/test_previous_version_state.py", "exclude": [], "use_colors": true, "use_colors_file": true, "quiet": false, "print": true, "send_anonymous_usage_stats": false, "partial_parse_file_diff": true, "log_format": "default"}}
|
||||
{"metadata": {"dbt_schema_version": "https://schemas.getdbt.com/dbt/run-results/v6.json", "dbt_version": "1.8.0a1", "generated_at": "2023-12-06T18:53:19.641690Z", "invocation_id": "ad4ef714-e6c6-425e-b7c8-c1c4369df4ea", "env": {}}, "results": [{"status": "success", "timing": [{"name": "compile", "started_at": "2023-12-06T18:53:19.554953Z", "completed_at": "2023-12-06T18:53:19.559711Z"}, {"name": "execute", "started_at": "2023-12-06T18:53:19.564874Z", "completed_at": "2023-12-06T18:53:19.620151Z"}], "thread_id": "Thread-8", "execution_time": 0.06995701789855957, "adapter_response": {"_message": "CREATE VIEW", "code": "CREATE VIEW", "rows_affected": -1}, "message": "CREATE VIEW", "failures": null, "unique_id": "model.test.metricflow_time_spine", "compiled": true, "compiled_code": "SELECT to_date('02/20/2023', 'mm/dd/yyyy') as date_day", "relation_name": "\"dbt\".\"test17018887966812726006_test_previous_version_state\".\"metricflow_time_spine\""}, {"status": "success", "timing": [{"name": "compile", "started_at": "2023-12-06T18:53:19.557019Z", "completed_at": "2023-12-06T18:53:19.559247Z"}, {"name": "execute", "started_at": "2023-12-06T18:53:19.561000Z", "completed_at": "2023-12-06T18:53:19.622080Z"}], "thread_id": "Thread-9", "execution_time": 0.07100677490234375, "adapter_response": {"_message": "CREATE VIEW", "code": "CREATE VIEW", "rows_affected": -1}, "message": "CREATE VIEW", "failures": null, "unique_id": "model.test.my_model", "compiled": true, "compiled_code": "select 1 as id", "relation_name": "\"dbt\".\"test17018887966812726006_test_previous_version_state\".\"my_model\""}], "elapsed_time": 0.13903093338012695, "args": {"print": true, "log_level_file": "debug", "quiet": false, "warn_error_options": {"include": [], "exclude": []}, "write_json": true, "invocation_command": "dbt --cov=core --cov-append --cov-report=xml tests/functional/artifacts/test_previous_version_state.py", "log_level": "info", "select": [], "project_dir": "/private/var/folders/67/r0f0jlj54h95zl3fhmb217jh0000gp/T/pytest-of-william/pytest-68/project0", "static_parser": true, "log_file_max_bytes": 10485760, "empty": false, "introspect": true, "log_format_file": "debug", "vars": {}, "strict_mode": false, "indirect_selection": "eager", "show_resource_report": false, "favor_state": false, "version_check": true, "cache_selected_only": false, "enable_legacy_logger": false, "partial_parse": true, "profiles_dir": "/private/var/folders/67/r0f0jlj54h95zl3fhmb217jh0000gp/T/pytest-of-william/pytest-68/profile0", "defer": false, "printer_width": 80, "send_anonymous_usage_stats": false, "use_colors": true, "log_path": "/Users/william/git/dbt-core/logs/test17018887966812726006", "partial_parse_file_diff": true, "populate_cache": true, "macro_debugging": false, "use_colors_file": true, "log_format": "default", "which": "run", "exclude": []}}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -443,7 +443,7 @@ class TestPreviousVersionState:
|
||||
current_run_results_schema_version == self.CURRENT_EXPECTED_RUN_RESULTS_VERSION
|
||||
), "Sounds like you've bumped the run_results version and need to update this test!"
|
||||
# If we need a newly generated run_results, uncomment the following line and commit the result
|
||||
self.generate_latest_run_results(project, current_run_results_schema_version)
|
||||
# self.generate_latest_run_results(project, current_run_results_schema_version)
|
||||
self.compare_previous_results(project, current_run_results_schema_version, True, 0)
|
||||
|
||||
def test_backwards_compatible_run_results_versions(self, project):
|
||||
|
||||
@@ -626,3 +626,41 @@ metrics:
|
||||
meta:
|
||||
my_meta: 'testing'
|
||||
"""
|
||||
|
||||
conversion_semantic_model_purchasing_yml = """
|
||||
version: 2
|
||||
|
||||
semantic_models:
|
||||
- name: semantic_purchasing
|
||||
model: ref('purchasing')
|
||||
measures:
|
||||
- name: num_orders
|
||||
agg: COUNT
|
||||
expr: purchased_at
|
||||
- name: num_visits
|
||||
agg: SUM
|
||||
expr: 1
|
||||
dimensions:
|
||||
- name: purchased_at
|
||||
type: TIME
|
||||
entities:
|
||||
- name: purchase
|
||||
type: primary
|
||||
expr: '1'
|
||||
defaults:
|
||||
agg_time_dimension: purchased_at
|
||||
|
||||
"""
|
||||
|
||||
conversion_metric_yml = """
|
||||
version: 2
|
||||
metrics:
|
||||
- name: converted_orders_over_visits
|
||||
label: Number of orders converted from visits
|
||||
type: conversion
|
||||
type_params:
|
||||
conversion_type_params:
|
||||
base_measure: num_visits
|
||||
conversion_measure: num_orders
|
||||
entity: purchase
|
||||
"""
|
||||
|
||||
@@ -7,6 +7,8 @@ from dbt.tests.util import run_dbt, get_manifest
|
||||
|
||||
|
||||
from tests.functional.metrics.fixtures import (
|
||||
conversion_semantic_model_purchasing_yml,
|
||||
conversion_metric_yml,
|
||||
mock_purchase_data_csv,
|
||||
models_people_sql,
|
||||
models_people_metrics_yml,
|
||||
@@ -339,3 +341,61 @@ class TestInvalidTimestampWindowMetrics:
|
||||
# initial run
|
||||
with pytest.raises(ParsingError):
|
||||
run_dbt(["run"])
|
||||
|
||||
|
||||
class TestConversionMetric:
|
||||
@pytest.fixture(scope="class")
|
||||
def models(self):
|
||||
return {
|
||||
"purchasing.sql": purchasing_model_sql,
|
||||
"metricflow_time_spine.sql": metricflow_time_spine_sql,
|
||||
"semantic_models.yml": conversion_semantic_model_purchasing_yml,
|
||||
"conversion_metric.yml": conversion_metric_yml,
|
||||
}
|
||||
|
||||
@pytest.fixture(scope="class")
|
||||
def seeds(self):
|
||||
return {
|
||||
"mock_purchase_data.csv": mock_purchase_data_csv,
|
||||
}
|
||||
|
||||
def test_conversion_metric(
|
||||
self,
|
||||
project,
|
||||
):
|
||||
# initial parse
|
||||
runner = dbtRunner()
|
||||
result = runner.invoke(["parse"])
|
||||
assert result.success
|
||||
assert isinstance(result.result, Manifest)
|
||||
|
||||
# make sure the metric is in the manifest
|
||||
manifest = get_manifest(project.project_root)
|
||||
metric_ids = list(manifest.metrics.keys())
|
||||
expected_metric_ids = [
|
||||
"metric.test.converted_orders_over_visits",
|
||||
]
|
||||
assert metric_ids == expected_metric_ids
|
||||
assert manifest.metrics[
|
||||
"metric.test.converted_orders_over_visits"
|
||||
].type_params.conversion_type_params
|
||||
assert (
|
||||
len(
|
||||
manifest.metrics[
|
||||
"metric.test.converted_orders_over_visits"
|
||||
].type_params.input_measures
|
||||
)
|
||||
== 2
|
||||
)
|
||||
assert (
|
||||
manifest.metrics[
|
||||
"metric.test.converted_orders_over_visits"
|
||||
].type_params.conversion_type_params.window
|
||||
is None
|
||||
)
|
||||
assert (
|
||||
manifest.metrics[
|
||||
"metric.test.converted_orders_over_visits"
|
||||
].type_params.conversion_type_params.entity
|
||||
== "purchase"
|
||||
)
|
||||
|
||||
@@ -2,6 +2,8 @@ import pytest
|
||||
import copy
|
||||
|
||||
from dbt.contracts.graph.nodes import (
|
||||
ConstantPropertyInput,
|
||||
ConversionTypeParams,
|
||||
Metric,
|
||||
MetricInput,
|
||||
MetricInputMeasure,
|
||||
@@ -233,6 +235,21 @@ def complex_metric_input_measure(where_filter) -> MetricInputMeasure:
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def conversion_type_params(
|
||||
simple_metric_input_measure, metric_time_window
|
||||
) -> ConversionTypeParams:
|
||||
return ConversionTypeParams(
|
||||
base_measure=simple_metric_input_measure,
|
||||
conversion_measure=simple_metric_input_measure,
|
||||
entity="entity",
|
||||
window=metric_time_window,
|
||||
constant_properties=[
|
||||
ConstantPropertyInput(base_property="base", conversion_property="conversion")
|
||||
],
|
||||
)
|
||||
|
||||
|
||||
@pytest.fixture(scope="session")
|
||||
def complex_metric_type_params(
|
||||
metric_time_window, simple_metric_input, simple_metric_input_measure
|
||||
@@ -245,6 +262,7 @@ def complex_metric_type_params(
|
||||
window=metric_time_window,
|
||||
grain_to_date=TimeGranularity.DAY,
|
||||
metrics=[simple_metric_input],
|
||||
conversion_type_params=conversion_type_params,
|
||||
)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user