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

WIP - enable object entity syntax in metric filters #285

Draft
wants to merge 4 commits into
base: main
Choose a base branch
from
Draft
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
2 changes: 1 addition & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ test:
export FORMAT_JSON_LOGS="1" && hatch -v run dev-env:pytest -n auto tests

lint:
hatch run dev-env:pre-commit run --show-diff-on-failure --color=always --all-files
hatch run dev-env:pre-commit run --color=always --all-files

json_schema:
hatch run dev-env:python dbt_semantic_interfaces/parsing/generate_json_schema_file.py
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
from typing import Optional, Sequence
from typing import Optional, Sequence, Union

from dbt_semantic_interfaces.call_parameter_sets import (
DimensionCallParameterSet,
Expand All @@ -12,6 +12,9 @@
METRIC_TIME_ELEMENT_NAME,
is_metric_time_name,
)
from dbt_semantic_interfaces.parsing.where_filter.where_filter_objects import (
WhereFilterEntity,
)
from dbt_semantic_interfaces.references import (
DimensionReference,
EntityReference,
Expand Down Expand Up @@ -106,7 +109,9 @@ def create_entity(entity_name: str, entity_path: Sequence[str] = ()) -> EntityCa
)

@staticmethod
def create_metric(metric_name: str, group_by: Sequence[str] = ()) -> MetricCallParameterSet:
def create_metric(
metric_name: str, group_by: Sequence[Union[str, WhereFilterEntity]] = ()
) -> MetricCallParameterSet:
"""Gets called by Jinja when rendering {{ Metric(...) }}."""
if not group_by:
raise ParseWhereFilterException(
Expand All @@ -115,5 +120,15 @@ def create_metric(metric_name: str, group_by: Sequence[str] = ()) -> MetricCallP
)
return MetricCallParameterSet(
metric_reference=MetricReference(element_name=metric_name),
group_by=tuple([LinkableElementReference(element_name=group_by_name) for group_by_name in group_by]),
group_by=tuple(
[
LinkableElementReference(
# TODO: add entity_links
group_by_item
if isinstance(group_by_item, str)
else group_by_item.element_name
)
for group_by_item in group_by
]
),
)

This file was deleted.

This file was deleted.

Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
from __future__ import annotations

from typing import List, Optional, Sequence

from dbt_semantic_interfaces.call_parameter_sets import (
EntityCallParameterSet,
MetricCallParameterSet,
TimeDimensionCallParameterSet,
)
from dbt_semantic_interfaces.errors import InvalidQuerySyntax
from dbt_semantic_interfaces.parsing.where_filter.parameter_set_factory import (
ParameterSetFactory,
)
from dbt_semantic_interfaces.parsing.where_filter.where_filter_objects import (
WhereFilterDimension,
WhereFilterEntity,
WhereFilterMetric,
WhereFilterTimeDimension,
)
from dbt_semantic_interfaces.type_enums import DatePart, TimeGranularity


# Rename these factories: RenderedWhereFilterEntityFactory (MF) vs. ParsedWhereFilterParser (DSI)
# Add protocols back. Rename them to: WhereFilterEntity (JinjaWhereFilterEntity? Can we reuse them in the JDBC interface & saved queries?),
# JinjaEntityFactory (do we even need a protocol for this? I guess to make sure the create() method is aligned?)
# Can we use an ABC instead of a protocol? Something that lets me add functional methods to the base class to avoid duplication.
class WhereFilterEntityFactory:
"""Executes in the Jinja sandbox to produce parameter sets and append them to a list."""

def __init__(self) -> None: # noqa
self.entity_call_parameter_sets: List[EntityCallParameterSet] = []

def create(self, entity_name: str, entity_path: Sequence[str] = ()) -> WhereFilterEntity:
"""Gets called by Jinja when rendering {{ Entity(...) }}."""
self.entity_call_parameter_sets.append(ParameterSetFactory.create_entity(entity_name, entity_path))
return WhereFilterEntity(element_name=entity_name, entity_links=entity_path)


class WhereFilterMetricFactory:
"""Executes in the Jinja sandbox to produce parameter sets and append them to a list."""

def __init__(self) -> None: # noqa: D
self.metric_call_parameter_sets: List[MetricCallParameterSet] = []

def create(self, metric_name: str, group_by: Sequence[str] = ()) -> WhereFilterMetric:
"""Create a WhereFilterMetric.

Note that group_by is required, but uses a default arg here so that we can return a readable error to the user
if they leave it out.
"""
self.metric_call_parameter_sets.append(
ParameterSetFactory.create_metric(metric_name=metric_name, group_by=group_by)
)
return WhereFilterMetric(element_name=metric_name, group_by=group_by)


class WhereFilterDimensionFactory:
"""Creates a WhereFilterDimension.

Each call to `create` adds a WhereFilterDimension to `created`.
"""

def __init__(self) -> None: # noqa
self.created: List[WhereFilterDimension] = []

def create(self, dimension_name: str, entity_path: Sequence[str] = ()) -> WhereFilterDimension:
"""Gets called by Jinja when rendering {{ Dimension(...) }}."""
dimension = WhereFilterDimension(dimension_name, entity_path)
self.created.append(dimension)
return dimension


class WhereFilterTimeDimensionFactory:
"""Executes in the Jinja sandbox to produce parameter sets and append them to a list."""

def __init__(self) -> None: # noqa
self.time_dimension_call_parameter_sets: List[TimeDimensionCallParameterSet] = []

def create(
self,
time_dimension_name: str,
time_granularity_name: Optional[str] = None,
entity_path: Sequence[str] = (),
descending: Optional[bool] = None,
date_part_name: Optional[str] = None,
) -> WhereFilterTimeDimension:
"""Gets called by Jinja when rendering {{ TimeDimension(...) }}."""
if descending is not None:
raise InvalidQuerySyntax("descending is invalid in the where parameter and filter spec")
self.time_dimension_call_parameter_sets.append(
ParameterSetFactory.create_time_dimension(
time_dimension_name=time_dimension_name,
time_granularity_name=time_granularity_name,
entity_path=entity_path,
date_part_name=date_part_name,
)
)
return WhereFilterTimeDimension(
element_name=time_dimension_name,
time_granularity=TimeGranularity(time_granularity_name.lower()) if time_granularity_name else None,
entity_path=entity_path,
date_part_name=DatePart(date_part_name.lower()) if date_part_name else None,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
from __future__ import annotations

from dataclasses import dataclass
from typing import Optional, Sequence

from dbt_semantic_interfaces.type_enums import DatePart, TimeGranularity


@dataclass
class WhereFilterEntity:
"""A dimension that is passed in through the where filter parameter using Jinja syntax."""

element_name: str
entity_links: Sequence[str] = ()


@dataclass
class WhereFilterMetric:
"""A metric that is passed in through the where filter parameter using Jinja syntax."""

element_name: str
group_by: Sequence[str]


@dataclass
class WhereFilterDimension:
"""A dimension that is passed in through the where filter parameter using Jinja syntax."""

name: str
entity_path: Sequence[str] = () # Default is new here - consistent with TimeDimension
# Behavior change: allows passing these params on init (why shouldn't we allow that?)
# don't love the names, though. Copy MFS jinja object?
time_granularity_name: Optional[str] = None
date_part_name: Optional[str] = None

def grain(self, time_granularity: str) -> WhereFilterDimension: # noqa: D
if self.time_granularity_name:
raise RuntimeError("Grain was already set in the Dimension object parameters.")
self.time_granularity_name = time_granularity
return self

def date_part(self, date_part: str) -> WhereFilterDimension: # noqa: D
if self.date_part_name:
raise RuntimeError("Date part was already set in the Dimension object parameters.")
self.date_part_name = date_part
return self


@dataclass
class WhereFilterTimeDimension:
"""A time dimension that is passed in through the where filter parameter using Jinja syntax."""

element_name: str
time_granularity: Optional[TimeGranularity] = None # not str?
entity_path: Sequence[str] = ()
# Can we change the name below? Breaking change bo one is using date part anyway, right? And it's not documented?
date_part_name: Optional[DatePart] = None

def grain(self, time_granularity: str) -> WhereFilterTimeDimension: # noqa: D
if self.time_granularity:
raise RuntimeError("Grain was already set in the Dimension object parameters.")
self.time_granularity = TimeGranularity(time_granularity.lower())
return self

def date_part(self, date_part: str) -> WhereFilterTimeDimension: # noqa: D
if self.date_part_name:
raise RuntimeError("Date part was already set in the Dimension object parameters.")
self.date_part_name = DatePart(date_part.lower())
return self
Original file line number Diff line number Diff line change
Expand Up @@ -11,14 +11,10 @@
from dbt_semantic_interfaces.parsing.where_filter.parameter_set_factory import (
ParameterSetFactory,
)
from dbt_semantic_interfaces.parsing.where_filter.where_filter_dimension import (
from dbt_semantic_interfaces.parsing.where_filter.where_filter_factories import (
WhereFilterDimensionFactory,
)
from dbt_semantic_interfaces.parsing.where_filter.where_filter_entity import (
WhereFilterEntityFactory,
WhereFilterMetricFactory,
)
from dbt_semantic_interfaces.parsing.where_filter.where_filter_time_dimension import (
WhereFilterTimeDimensionFactory,
)

Expand Down
Loading
Loading