lib/metric-config-parser/metric_config_parser/dimension.py (73 lines of code) (raw):

from typing import TYPE_CHECKING, Dict, Optional import attr if TYPE_CHECKING: from metric_config_parser.config import ConfigCollection from metric_config_parser.monitoring import MonitoringSpec from metric_config_parser.project import ProjectConfiguration from metric_config_parser.data_source import DataSource, DataSourceReference from metric_config_parser.util import converter @attr.s(auto_attribs=True) class Dimension: """Represents a dimension for segmenting client populations.""" name: str data_source: DataSource select_expression: str friendly_name: Optional[str] = None description: Optional[str] = None @attr.s(auto_attribs=True) class DimensionDefinition: """Describes the interface for defining a dimension in configuration.""" name: str # implicit in configuration select_expression: str data_source: DataSourceReference friendly_name: Optional[str] = None description: Optional[str] = None def resolve( self, spec: "MonitoringSpec", conf: "ProjectConfiguration", configs: "ConfigCollection", ) -> Dimension: """Create and return a `Dimension` from the definition.""" return Dimension( name=self.name, data_source=self.data_source.resolve(spec, conf, configs), select_expression=self.select_expression, friendly_name=self.friendly_name, description=self.description, ) def merge(self, other: "DimensionDefinition"): """Merge with another dimension definition.""" for key in attr.fields_dict(type(self)): setattr(self, key, getattr(other, key) or getattr(self, key)) @attr.s(auto_attribs=True) class DimensionsSpec: """Describes the interface for defining custom dimensions.""" definitions: Dict[str, DimensionDefinition] = attr.Factory(dict) @classmethod def from_dict(cls, d: dict) -> "DimensionsSpec": """Create a `DimensionsSpec` from a dictionary.""" d = dict((k.lower(), v) for k, v in d.items()) definitions = { k: converter.structure({"name": k, **v}, DimensionDefinition) for k, v in d.items() } return cls(definitions=definitions) def merge(self, other: "DimensionsSpec"): """ Merge another dimension spec into the current one. The `other` DimensionsSpec overwrites existing keys. """ seen = [] for key, _ in self.definitions.items(): if key in other.definitions: self.definitions[key].merge(other.definitions[key]) seen.append(key) for key, definition in other.definitions.items(): if key not in seen: self.definitions[key] = definition converter.register_structure_hook(DimensionsSpec, lambda obj, _type: DimensionsSpec.from_dict(obj)) @attr.s(auto_attribs=True) class DimensionReference: """Represents a reference to a dimension.""" name: str def resolve( self, spec: "MonitoringSpec", conf: "ProjectConfiguration", configs: "ConfigCollection", ) -> Dimension: """Return the referenced `Dimension`.""" if self.name in spec.dimensions.definitions: return spec.dimensions.definitions[self.name].resolve(spec, conf, configs) raise ValueError(f"Could not locate dimension {self.name}") converter.register_structure_hook( DimensionReference, lambda obj, _type: DimensionReference(name=obj) )