From e4b6d9310cbb9f430e8699f8900abee7ba9c27d7 Mon Sep 17 00:00:00 2001 From: oliverlaslett Date: Sat, 17 Apr 2021 23:47:52 +0100 Subject: [PATCH] add support for measures --- README.md | 32 +++++++++++++++++++++++------ dbt2looker/generator.py | 13 ++++++++++++ dbt2looker/models.py | 36 ++++++++++++++++++++++++++++++++- example/lookml/views/pages.view | 5 +++++ example/models/prod/pages.yml | 5 +++++ pyproject.toml | 2 +- 6 files changed, 85 insertions(+), 8 deletions(-) diff --git a/README.md b/README.md index 0684510..481edd9 100644 --- a/README.md +++ b/README.md @@ -4,15 +4,16 @@ Use `dbt2looker` to generate Looker view files automatically from dbt models. **Features** -* Auto-generates a Looker view per dbt model -* Supports dbt model and column-level descriptions -* Automatically maps raw column types to looker types -* Creates dimension groups for datetime/timestamp/date types -* Currently supports: BigQuery, Snowflake, Redshift (postgres to come) +* **Column descriptions** synced to looker +* **Dimension** for each column in dbt model +* **Dimension groups** for datetime/timestamp/date columns +* **Measures** defined through dbt column `metadata` [see below](#defining-measures) +* Looker types +* Warehouses: BigQuery, Snowflake, Redshift (postgres to come) [![demo](https://raw.githubusercontent.com/hubble-data/dbt2looker/main/docs/demo.gif)](https://asciinema.org/a/407407) -### Usage +## Quickstart Run `dbt2looker` in the root of your dbt project *after compiling looker docs*. @@ -56,3 +57,22 @@ poetry install # Run poetry run dbt2looker ``` + +## Defining measures + +You can define looker measures in your dbt `schema.yml` files. For example: + +```yaml +models: + - name: pages + columns: + - name: url + description: "Page url" + - name: event_id + description: unique event id for page view + meta: + looker.com: # looker config block for column + measures: + - name: Page views + type: count +``` \ No newline at end of file diff --git a/dbt2looker/generator.py b/dbt2looker/generator.py index 8c6b05b..33b9778 100644 --- a/dbt2looker/generator.py +++ b/dbt2looker/generator.py @@ -164,6 +164,18 @@ def lookml_dimensions_from_model(model: models.DbtModel, adapter_type: models.Su ] +def lookml_measures_from_model(model: models.DbtModel): + return [ + { + 'name': measure.name, + 'type': measure.type.value, + 'sql': f'${{TABLE}}.{column.name}', + } + for column in model.columns.values() + for measure in column.meta.looker.measures + ] + + def lookml_view_from_dbt_model(model: models.DbtModel, adapter_type: models.SupportedDbtAdapters): lookml = { 'view': { @@ -171,6 +183,7 @@ def lookml_view_from_dbt_model(model: models.DbtModel, adapter_type: models.Supp 'sql_table_name': f'{model.database}.{model.db_schema}.{model.name}', 'dimension_groups': lookml_dimension_groups_from_model(model, adapter_type), 'dimensions': lookml_dimensions_from_model(model, adapter_type), + 'measures': lookml_measures_from_model(model), } } contents = lkml.dump(lookml) diff --git a/dbt2looker/models.py b/dbt2looker/models.py index ba2fedd..11e601d 100644 --- a/dbt2looker/models.py +++ b/dbt2looker/models.py @@ -3,6 +3,7 @@ from pydantic import BaseModel, Field, PydanticValueError, validator +# dbt2looker utility types class UnsupportedDbtAdapterError(PydanticValueError): code = 'unsupported_dbt_adapter' msg_template = '{wrong_value} is not a supported dbt adapter' @@ -14,10 +15,33 @@ class SupportedDbtAdapters(str, Enum): snowflake = 'snowflake' -class DbtProjectConfig(BaseModel): +# Lookml types +class LookerAggregateMeasures(str, Enum): + average = 'average' + average_distinct = 'average_distinct' + count = 'count' + count_distinct = 'count_distinct' + list = 'list' + max = 'max' + median = 'median' + median_distinct = 'median_distinct' + min = 'min' + percentile = 'percentile' + percentile_distinct = 'percentile_distinct' + sum = 'sum' + sum_distinct = 'sum_distinct' + + +class Dbt2LookerMeasure(BaseModel): name: str + type: LookerAggregateMeasures + + +class Dbt2LookerMeta(BaseModel): + measures: Optional[List[Dbt2LookerMeasure]] = [] +# Looker file types class LookViewFile(BaseModel): filename: str contents: str @@ -28,10 +52,20 @@ class LookModelFile(BaseModel): contents: str +# dbt config types +class DbtProjectConfig(BaseModel): + name: str + + +class DbtModelColumnMeta(BaseModel): + looker: Optional[Dbt2LookerMeta] = Field(Dbt2LookerMeta(), alias='looker.com') + + class DbtModelColumn(BaseModel): name: str description: str data_type: Optional[str] + meta: DbtModelColumnMeta class DbtNode(BaseModel): diff --git a/example/lookml/views/pages.view b/example/lookml/views/pages.view index d591a26..ba757c5 100644 --- a/example/lookml/views/pages.view +++ b/example/lookml/views/pages.view @@ -50,4 +50,9 @@ view: pages { sql: ${TABLE}.referring_domain ;; description: "Website domain of the referrer. e.g. google.com" } + + measure: Page views { + type: count + sql: ${TABLE}.id ;; + } } \ No newline at end of file diff --git a/example/models/prod/pages.yml b/example/models/prod/pages.yml index 42ae236..1232640 100644 --- a/example/models/prod/pages.yml +++ b/example/models/prod/pages.yml @@ -7,6 +7,11 @@ models: columns: - name: id description: "The primary key for this table" + meta: + looker.com: + measures: + - name: Page views + type: count tests: - unique - not_null diff --git a/pyproject.toml b/pyproject.toml index 0606c43..0aea889 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,6 +1,6 @@ [tool.poetry] name = "dbt2looker" -version = "0.5.2" +version = "0.6.0" description = "Generate lookml view files from dbt models" authors = ["oliverlaslett "] license = "MIT"