Source code for mmm_framework.builders.base

"""
Base classes and mixins for configuration builders.

Provides shared functionality used across all builder types.
"""

from __future__ import annotations

from typing import TYPE_CHECKING, Protocol, TypeVar, runtime_checkable

from ..config import DimensionType

if TYPE_CHECKING:
    from typing import Self

T = TypeVar("T")


[docs] @runtime_checkable class BuilderProtocol(Protocol[T]): """Protocol for configuration builders. All builders should implement a build() method that returns the final configuration object. """
[docs] def build(self) -> T: """Build and return the configuration object.""" ...
[docs] class VariableConfigBuilderMixin: """ Mixin providing shared methods for variable configuration builders. This mixin provides common functionality for builders that configure variables with display names, units, and dimensions. It should be used by MediaChannelConfigBuilder, ControlVariableConfigBuilder, and KPIConfigBuilder to eliminate code duplication. Attributes ---------- _display_name : str | None Human-readable display name for the variable. _unit : str | None Unit of measurement (e.g., 'USD', 'GRPs'). _dimensions : list[DimensionType] Dimensions this variable is defined over. """ _display_name: str | None _unit: str | None _dimensions: list[DimensionType] def _init_variable_fields(self) -> None: """Initialize common variable fields. Call in subclass __init__.""" self._display_name = None self._unit = None self._dimensions = [DimensionType.PERIOD]
[docs] def with_display_name(self, name: str) -> Self: """Set human-readable display name. Parameters ---------- name : str Display name for the variable. Returns ------- Self Builder instance for method chaining. """ self._display_name = name return self
[docs] def with_unit(self, unit: str) -> Self: """Set unit of measurement. Parameters ---------- unit : str Unit of measurement (e.g., 'USD', 'GRPs', 'Index'). Returns ------- Self Builder instance for method chaining. """ self._unit = unit return self
[docs] def with_dimensions(self, *dims: DimensionType) -> Self: """Set dimensions this variable is defined over. If PERIOD is not included in the provided dimensions, it will be automatically inserted at the beginning. Args: *dims: Variable number of dimension types. Returns: Builder instance for method chaining. """ self._dimensions = list(dims) if DimensionType.PERIOD not in self._dimensions: self._dimensions.insert(0, DimensionType.PERIOD) return self
[docs] def national(self) -> Self: """Set as national-level (Period only). This is a convenience method equivalent to: `with_dimensions(DimensionType.PERIOD)` Returns ------- Self Builder instance for method chaining. """ self._dimensions = [DimensionType.PERIOD] return self
[docs] def by_geo(self) -> Self: """Set as geo-level (Period + Geography). This is a convenience method equivalent to: `with_dimensions(DimensionType.PERIOD, DimensionType.GEOGRAPHY)` Returns ------- Self Builder instance for method chaining. """ self._dimensions = [DimensionType.PERIOD, DimensionType.GEOGRAPHY] return self
[docs] def by_product(self) -> Self: """Set as product-level (Period + Product). This is a convenience method equivalent to: `with_dimensions(DimensionType.PERIOD, DimensionType.PRODUCT)` Returns ------- Self Builder instance for method chaining. """ self._dimensions = [DimensionType.PERIOD, DimensionType.PRODUCT] return self
[docs] def by_geo_and_product(self) -> Self: """Set as geo+product level (Period + Geography + Product). This is a convenience method equivalent to: `with_dimensions(DimensionType.PERIOD, DimensionType.GEOGRAPHY, DimensionType.PRODUCT)` Returns ------- Self Builder instance for method chaining. """ self._dimensions = [ DimensionType.PERIOD, DimensionType.GEOGRAPHY, DimensionType.PRODUCT, ] return self
__all__ = [ "BuilderProtocol", "VariableConfigBuilderMixin", ]