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

from typing import TYPE_CHECKING, Dict, List, Optional import attr from metric_config_parser.metric import MetricReference from metric_config_parser.util import converter if TYPE_CHECKING: from metric_config_parser.config import ConfigCollection from metric_config_parser.monitoring import MonitoringSpec from metric_config_parser.project import ProjectConfiguration @attr.s(auto_attribs=True, kw_only=True) class MetricGroup: """Represents a set of metrics that are related and should be displayed together.""" name: str description: Optional[str] = None friendly_name: Optional[str] = None metrics: List[MetricReference] = attr.Factory(list) @attr.s(auto_attribs=True) class MetricGroupDefinition: """Describes the interface for defining a metric group in configuration.""" name: str # implicit in configuration friendly_name: Optional[str] = None description: Optional[str] = None metrics: List[MetricReference] = attr.Factory(list) def resolve( self, spec: "MonitoringSpec", _conf: "ProjectConfiguration", _configs: "ConfigCollection", ) -> MetricGroup: """Create and return a `MetricGroup` from the definition.""" for metric_ref in self.metrics: if metric_ref not in spec.project.metrics: raise ValueError( f"Metric {metric_ref} is part of metric group {self.name} " + "but not referenced as project metric" ) return MetricGroup( name=self.name, metrics=self.metrics, friendly_name=self.friendly_name, description=self.description, ) def merge(self, other: "MetricGroupDefinition"): """Merge with another metric group 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 MetricGroupsSpec: """Describes the interface for defining custom dimensions.""" definitions: Dict[str, MetricGroupDefinition] = attr.Factory(dict) @classmethod def from_dict(cls, d: dict) -> "MetricGroupsSpec": """Create a `MetricGroupsSpec` from a dictionary.""" d = dict((k.lower(), v) for k, v in d.items()) definitions = { k: converter.structure({"name": k, **v}, MetricGroupDefinition) for k, v in d.items() } return cls(definitions=definitions) def merge(self, other: "MetricGroupsSpec"): """ Merge another metric group spec into the current one. The `other` MetricGroupsSpec 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( MetricGroupsSpec, lambda obj, _type: MetricGroupsSpec.from_dict(obj) )