Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
21 changes: 21 additions & 0 deletions converters/dbt/src/ossie_dbt/msi_to_ossie.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,13 +15,15 @@
# specific language governing permissions and limitations
# under the License.

import json
import re
from collections import defaultdict
from dataclasses import dataclass
from itertools import combinations
from typing import Dict, List, Optional, Sequence, Tuple

from ossie import (
OssieCustomExtension,
OssieDataset,
OssieDialect,
OssieDialectExpression,
Expand All @@ -32,6 +34,7 @@
OssieMetric,
OssieRelationship,
OssieSemanticModel,
OssieVendor,
)
from ossie_dbt.converter_issues import ConverterIssue, ConverterIssueType, ConverterResult
from ossie_dbt.filter_utils import _collect_filter_sql, _merge_filter_sqls
Expand All @@ -44,6 +47,7 @@
Measure,
MeasureAggregationParameters,
)
from metricflow_semantic_interfaces.protocols.meta import SemanticLayerElementConfig
from metricflow_semantic_interfaces.protocols.metric import Metric
from metricflow_semantic_interfaces.protocols.semantic_model import SemanticModel
from metricflow_semantic_interfaces.transformations.convert_count import ConvertCountMetricToSumRule
Expand Down Expand Up @@ -116,6 +120,7 @@ def convert(
name=metric.name,
expression=self._make_expression(expr),
description=metric.description,
custom_extensions=self._custom_extensions_from_meta(metric.config),
)
)

Expand Down Expand Up @@ -153,6 +158,7 @@ def _convert_semantic_model(self, sm: SemanticModel) -> OssieDataset:
unique_keys=unique_keys if unique_keys else None,
description=sm.description,
fields=fields if fields else None,
custom_extensions=self._custom_extensions_from_meta(sm.config),
)

def _convert_dimension(self, dim: Dimension) -> OssieField:
Expand All @@ -165,6 +171,7 @@ def _convert_dimension(self, dim: Dimension) -> OssieField:
dimension=OssieDimension(is_time=is_time),
label=dim.label,
description=dim.description,
custom_extensions=self._custom_extensions_from_meta(dim.config),
)

def _convert_entity(self, entity: Entity) -> OssieField:
Expand All @@ -175,6 +182,7 @@ def _convert_entity(self, entity: Entity) -> OssieField:
expression=self._make_expression(expr),
label=entity.label,
description=entity.description,
custom_extensions=self._custom_extensions_from_meta(entity.config),
)

def _convert_measure(self, measure: Measure) -> OssieField:
Expand All @@ -185,8 +193,21 @@ def _convert_measure(self, measure: Measure) -> OssieField:
expression=self._make_expression(expr),
label=measure.label,
description=measure.description,
custom_extensions=self._custom_extensions_from_meta(measure.config),
)

@staticmethod
def _custom_extensions_from_meta(
config: Optional[SemanticLayerElementConfig],
) -> Optional[List[OssieCustomExtension]]:
"""Carry a non-empty MSI `config.meta` dict across as one Ossie `custom_extensions` entry.

`config.meta` is otherwise silently dropped at this boundary (ossie#303).
"""
if config is None or not config.meta:
return None
return [OssieCustomExtension(vendor_name=OssieVendor.DBT.value, data=json.dumps(config.meta))]

@staticmethod
def _extract_keys(entities: Sequence[Entity]) -> Tuple[Optional[List[str]], List[List[str]]]:
primary_key: Optional[List[str]] = None
Expand Down
48 changes: 39 additions & 9 deletions converters/dbt/src/ossie_dbt/ossie_to_msi.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,19 @@
# specific language governing permissions and limitations
# under the License.

import json
from dataclasses import dataclass
from typing import List, Optional, Set

from ossie import (
OssieCustomExtension,
OssieDataset,
OssieDialect,
OssieDocument,
OssieExpression,
OssieField,
OssieSemanticModel,
OssieVendor,
)
from ossie_dbt.converter_issues import ConverterResult
from ossie_dbt.expression_utils import (
Expand All @@ -34,6 +37,9 @@
_try_parse_ratio,
)

from metricflow_semantic_interfaces.implementations.element_config import (
PydanticSemanticLayerElementConfig,
)
from metricflow_semantic_interfaces.implementations.elements.dimension import (
PydanticDimension,
PydanticDimensionTypeParams,
Expand Down Expand Up @@ -149,8 +155,23 @@ def _convert_dataset(
entities=entities,
dimensions=dimensions,
measures=[],
config=self._config_from_custom_extensions(dataset.custom_extensions),
)

@staticmethod
def _config_from_custom_extensions(
custom_extensions: Optional[List[OssieCustomExtension]],
) -> Optional[PydanticSemanticLayerElementConfig]:
"""Reconstruct MSI `config.meta` from the `custom_extensions` entry this converter wrote (ossie#303).

Only the DBT-vendor entry round-trips; extensions written by another vendor did not
originate from config.meta and have no meaning as one.
"""
for ext in custom_extensions or []:
if ext.vendor_name == OssieVendor.DBT.value:
return PydanticSemanticLayerElementConfig(meta=json.loads(ext.data))
return None

@staticmethod
def _build_key_sets(dataset: OssieDataset, ossie_sm: OssieSemanticModel) -> _KeySets:
"""Return a _KeySets with primary, unique, and foreign key column sets for a dataset."""
Expand Down Expand Up @@ -197,7 +218,7 @@ def _classify_field(
description=field.description,
label=field.label,
role=None,
config=None,
config=self._config_from_custom_extensions(field.custom_extensions),
)
)
return
Expand All @@ -210,7 +231,7 @@ def _classify_field(
description=field.description,
label=field.label,
role=None,
config=None,
config=self._config_from_custom_extensions(field.custom_extensions),
)
)
return
Expand All @@ -223,7 +244,7 @@ def _classify_field(
description=field.description,
label=field.label,
role=None,
config=None,
config=self._config_from_custom_extensions(field.custom_extensions),
)
)
return
Expand All @@ -237,7 +258,7 @@ def _classify_field(
expr=expr_or_none,
description=field.description,
label=field.label,
config=None,
config=self._config_from_custom_extensions(field.custom_extensions),
)
)
return
Expand All @@ -249,7 +270,7 @@ def _classify_field(
expr=expr_or_none,
description=field.description,
label=field.label,
config=None,
config=self._config_from_custom_extensions(field.custom_extensions),
)
)

Expand All @@ -261,7 +282,15 @@ def _convert_metrics(self, ossie_sm: OssieSemanticModel) -> List[PydanticMetric]
metrics: List[PydanticMetric] = []
for metric in ossie_sm.metrics or []:
expr_str = self._get_expression(metric.expression)
metrics.extend(self._convert_metric(metric.name, expr_str, metric.description, ossie_sm.datasets))
metrics.extend(
self._convert_metric(
metric.name,
expr_str,
metric.description,
ossie_sm.datasets,
config=self._config_from_custom_extensions(metric.custom_extensions),
)
)
return metrics

def _convert_metric(
Expand All @@ -270,6 +299,7 @@ def _convert_metric(
expr_str: str,
description: Optional[str],
datasets: List[OssieDataset],
config: Optional[PydanticSemanticLayerElementConfig] = None,
) -> List[PydanticMetric]:
"""Return one or more PydanticMetric objects for the given Ossie expression.

Expand Down Expand Up @@ -302,7 +332,7 @@ def _convert_metric(
),
filter=None,
metadata=None,
config=None,
config=config,
)
]

Expand All @@ -324,7 +354,7 @@ def _convert_metric(
),
filter=None,
metadata=None,
config=None,
config=config,
)
return [*num_metrics, *den_metrics, ratio_metric]

Expand All @@ -349,7 +379,7 @@ def _convert_metric(
),
filter=None,
metadata=None,
config=None,
config=config,
)
]

Expand Down
30 changes: 25 additions & 5 deletions converters/dbt/tests/helpers.py
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"""Shared test helpers for Ossie converter tests."""

from ossie import (
OssieCustomExtension,
OssieDataset,
OssieDialect,
OssieDialectExpression,
Expand All @@ -29,6 +30,9 @@
OssieRelationship,
OssieSemanticModel,
)
from metricflow_semantic_interfaces.implementations.element_config import (
PydanticSemanticLayerElementConfig,
)
from metricflow_semantic_interfaces.implementations.elements.dimension import (
PydanticDimension,
PydanticDimensionTypeParams,
Expand Down Expand Up @@ -79,6 +83,7 @@ def _simple_metric(
name: str,
measure_name: str,
description: str | None = None,
config: PydanticSemanticLayerElementConfig | None = None,
) -> PydanticMetric:
return PydanticMetric(
name=name,
Expand All @@ -89,7 +94,7 @@ def _simple_metric(
),
filter=None,
metadata=default_meta(),
config=None,
config=config,
)


Expand All @@ -100,6 +105,7 @@ def _dimension(
description: str | None = None,
label: str | None = None,
granularity: TimeGranularity | None = None,
config: PydanticSemanticLayerElementConfig | None = None,
) -> PydanticDimension:
type_params = PydanticDimensionTypeParams(time_granularity=granularity) if granularity else None
return PydanticDimension(
Expand All @@ -110,7 +116,7 @@ def _dimension(
label=label,
type_params=type_params,
metadata=default_meta(),
config=None,
config=config,
)


Expand All @@ -120,6 +126,7 @@ def _measure(
expr: str | None = None,
description: str | None = None,
label: str | None = None,
config: PydanticSemanticLayerElementConfig | None = None,
) -> PydanticMeasure:
return PydanticMeasure(
name=name,
Expand All @@ -130,21 +137,23 @@ def _measure(
create_metric=None,
agg_params=None,
metadata=default_meta(),
config=config,
)


def _entity(
name: str,
entity_type: EntityType = EntityType.PRIMARY,
expr: str | None = None,
config: PydanticSemanticLayerElementConfig | None = None,
) -> PydanticEntity:
return PydanticEntity(
name=name,
type=entity_type,
expr=expr,
description=None,
role=None,
config=None,
config=config,
)


Expand All @@ -167,13 +176,15 @@ def _ossie_field(
is_time: bool | None = None,
description: str | None = None,
label: str | None = None,
custom_extensions: list[OssieCustomExtension] | None = None,
) -> OssieField:
return OssieField(
name=name,
expression=_ossie_expr(expression if expression is not None else name),
dimension=OssieDimension(is_time=is_time) if is_time is not None else None,
description=description,
label=label,
custom_extensions=custom_extensions,
)


Expand All @@ -184,6 +195,7 @@ def _ossie_dataset(
primary_key: list[str] | None = None,
unique_keys: list[list[str]] | None = None,
description: str | None = None,
custom_extensions: list[OssieCustomExtension] | None = None,
) -> OssieDataset:
return OssieDataset(
name=name,
Expand All @@ -192,11 +204,19 @@ def _ossie_dataset(
primary_key=primary_key,
unique_keys=unique_keys,
description=description,
custom_extensions=custom_extensions,
)


def _ossie_metric(name: str, expression: str, description: str | None = None) -> OssieMetric:
return OssieMetric(name=name, expression=_ossie_expr(expression), description=description)
def _ossie_metric(
name: str,
expression: str,
description: str | None = None,
custom_extensions: list[OssieCustomExtension] | None = None,
) -> OssieMetric:
return OssieMetric(
name=name, expression=_ossie_expr(expression), description=description, custom_extensions=custom_extensions
)


def _ossie_relationship(
Expand Down
Loading