Skip to content

Commit

Permalink
Merge pull request #33 from YotpoLtd/KOALA-830-add-dimensions-support
Browse files Browse the repository at this point in the history
added custom dimensions support
  • Loading branch information
MDura92 authored Dec 19, 2023
2 parents a1a6fc0 + d17a948 commit 7823b3a
Show file tree
Hide file tree
Showing 7 changed files with 105 additions and 5 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -66,6 +66,12 @@ view: ugc__shopper_experience_store_aggregation {
description: "Amount of unique users that did not interact with any widget and did not place any order"
}

dimension: custom_dimension {
description: ""
type: number
sql: case when ${currency} is not null then ${orders_count} else 0 end ;;
}

measure: engagement {
description: ""
type: number
Expand Down
2 changes: 1 addition & 1 deletion tests/resources/test_target/manifest.json

Large diffs are not rendered by default.

17 changes: 17 additions & 0 deletions tests/yoda_dbt2looker/test_generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -379,3 +379,20 @@ def test__get_model_relation_name():
model.meta.integration_config.snowflake.properties.sf_schema = "schema1"
model.meta.integration_config.snowflake.properties.table = "table2"
assert generator._get_model_relation_name(model) == "schema1.table2"


def test_lookml_calculated_dimension():
dimension1 = models.Dbt2LookerExploreDimension(
name="custom_dimension_1",
model="ref('model_1')",
sql="case when 1=1 then 1 else 0 end",
description="custom dimension",
type="number",
)
value = generator.lookml_calculated_dimension(dimension1)
assert value == {
"name": "custom_dimension_1",
"type": "number",
"sql": "case when 1=1 then 1 else 0 end",
"description": "custom dimension",
}
33 changes: 32 additions & 1 deletion tests/yoda_dbt2looker/test_parser.py
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
from unittest.mock import MagicMock
from yoda_dbt2looker import models
from yoda_dbt2looker.parser import _extract_measures_models
from yoda_dbt2looker.parser import (
_extract_measures_models,
_extract_calculated_dimensions_models,
)


def test__extract_measures_models():
Expand Down Expand Up @@ -29,3 +32,31 @@ def test__extract_measures_models():
"model_1": [exposure1.meta.looker.measures[0]],
"model_2": [exposure1.meta.looker.measures[1]],
}


def test__extract_calculated_dimensions_models():
exposure_model_views = set()
exposure = MagicMock()
model = {}
exposure.meta.looker.dimensions = [
models.Dbt2LookerExploreDimension(
name="calculated_dimension_1",
model="ref('model_1')",
sql="case when ${col1} is null then ${col2} else ${col4} end",
description="",
type="number",
),
models.Dbt2LookerExploreDimension(
name="measure_2",
model="ref('model_2')",
sql="case when ${col1} is null then ${col2} else ${col1} end",
description="",
type="string",
),
]
_extract_calculated_dimensions_models(exposure_model_views, model, exposure)
assert exposure_model_views == {"model_1", "model_2"}
assert model == {
"model_1": [exposure.meta.looker.dimensions[0]],
"model_2": [exposure.meta.looker.dimensions[1]],
}
11 changes: 11 additions & 0 deletions yoda_dbt2looker/generator.py
Original file line number Diff line number Diff line change
Expand Up @@ -270,6 +270,8 @@ def lookml_dimensions_from_model(
):
compound_key = _generate_compound_primary_key_if_needed(model)
dimensions = _generate_dimensions(model, adapter_type)
for calculated_dimension in model.calculated_dimension:
dimensions.append(lookml_calculated_dimension(calculated_dimension))
if compound_key:
return dimensions + [compound_key]
return dimensions
Expand Down Expand Up @@ -395,6 +397,15 @@ def lookml_non_aggregative_measure(measure: models.Dbt2LookerExploreMeasure):
}


def lookml_calculated_dimension(dimension: models.Dbt2LookerExploreDimension):
return {
"name": dimension.name,
"description": dimension.description,
"type": dimension.type,
"sql": dimension.sql,
}


def lookml_view_from_dbt_model(
model: models.DbtModel, adapter_type: models.SupportedDbtAdapters
):
Expand Down
10 changes: 10 additions & 0 deletions yoda_dbt2looker/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -170,11 +170,20 @@ class Dbt2LookerExploreMeasure(BaseModel):
description: Optional[str] = ""


class Dbt2LookerExploreDimension(BaseModel):
name: str
model: str
type: str
sql: str
description: Optional[str] = ""


class Dbt2MetaLookerModelMeta(BaseModel):
joins: Optional[List[Dbt2LookerExploreJoin]] = []
main_model: str
connection: str
measures: Optional[List[Dbt2LookerExploreMeasure]] = []
dimensions: Optional[List[Dbt2LookerExploreDimension]] = []


class Dbt2LookerModelMeta(BaseModel):
Expand Down Expand Up @@ -211,6 +220,7 @@ class DbtModel(DbtNode):
meta: DbtModelMeta
create_explorer: bool = True
none_aggregative_exposure: Optional[List[Dbt2LookerExploreMeasure]] = []
calculated_dimension: Optional[List[Dbt2LookerExploreDimension]] = []

@validator("columns")
def case_insensitive_column_names(cls, v: Dict[str, DbtModelColumn]):
Expand Down
31 changes: 28 additions & 3 deletions yoda_dbt2looker/parser.py
Original file line number Diff line number Diff line change
Expand Up @@ -118,12 +118,11 @@ def parse_typed_models(
typed_dbt_exposures: List[models.DbtExposure] = parse_exposures(
raw_manifest, tag=tag
)
exposure_nodes = (
[]
)
exposure_nodes = []

exposure_model_views = set()
model_to_measure = {}
calculated_dimension = {}
for exposure in typed_dbt_exposures:
ref_model = _extract_all_refs(exposure.meta.looker.main_model)
if not ref_model:
Expand Down Expand Up @@ -151,6 +150,9 @@ def parse_typed_models(
):
exposure_model_views.add(item)
_extract_measures_models(exposure_model_views, model_to_measure, exposure)
_extract_calculated_dimensions_models(
exposure_model_views, calculated_dimension, exposure
)

for model in exposure_model_views:
model_loopup = f"model.{dbt_project_name}.{model}"
Expand All @@ -161,6 +163,8 @@ def parse_typed_models(
model_node.create_explorer = False
if model in model_to_measure:
model_node.none_aggregative_exposure = model_to_measure[model]
if model in calculated_dimension:
model_node.calculated_dimension = calculated_dimension[model]
exposure_nodes.append(model_node)

adapter_type = parse_adapter_type(raw_manifest)
Expand Down Expand Up @@ -246,6 +250,27 @@ def _extract_measures_models(
exposure_model_views.update(_extract_all_refs(measure.sql))


def _extract_calculated_dimensions_models(
exposure_model_views: set[str],
model: dict[str, list[models.Dbt2LookerExploreDimension]],
exposure: models.DbtExposure,
):
if exposure.meta.looker.dimensions:
for dimension in exposure.meta.looker.dimensions:
if not _extract_all_refs(dimension.model):
logging.error(
f"Exposure dimension.model {dimension.model} should be ref('model_name')"
)
raise Exception(
f"Exposure dimension.model {dimension.model} should be ref('model_name')"
)
main_model = _extract_all_refs(dimension.model)[0]
exposure_model_views.add(main_model)
if not model.get(main_model):
model[main_model] = []
model[main_model].append(dimension)


def get_column_type_from_catalog(
catalog_nodes: Dict[str, models.DbtCatalogNode], model_id: str, column_name: str
):
Expand Down

0 comments on commit 7823b3a

Please sign in to comment.