Skip to content

Commit 1c565b3

Browse files
authored
feat: ability to override the analysis unit for an experiment (#1274)
1 parent 3874a67 commit 1c565b3

File tree

4 files changed

+39
-2
lines changed

4 files changed

+39
-2
lines changed

lib/metric-config-parser/metric_config_parser/experiment.py

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -166,6 +166,9 @@ def analysis_unit(self) -> AnalysisUnit | None:
166166
"""Retrieve the appropriate analysis unit, which is
167167
derived from the experiment's randomization unit.
168168
"""
169+
if self.experiment_spec.analysis_unit is not None:
170+
return AnalysisUnit(self.experiment_spec.analysis_unit)
171+
169172
if self.randomization_unit and self.randomization_unit == RandomizationUnit.GROUP_ID:
170173
return AnalysisUnit.PROFILE_GROUP
171174

@@ -268,6 +271,11 @@ def _validate_dataset_id(instance: Any, attribute, value):
268271
raise ValueError("dataset_id must be set to a custom dataset for private experiments")
269272

270273

274+
def _validate_analysis_unit(instance: Any, attribute, value):
275+
if value is not None:
276+
AnalysisUnit(value)
277+
278+
271279
@attr.s(auto_attribs=True, kw_only=True)
272280
class ExperimentSpec:
273281
"""Describes the interface for overriding experiment details."""
@@ -283,6 +291,7 @@ class ExperimentSpec:
283291
is_private: bool = False
284292
dataset_id: str | None = attr.ib(default=None, validator=_validate_dataset_id)
285293
sample_size: int | None = None
294+
analysis_unit: str | None = attr.ib(default=None, validator=_validate_analysis_unit)
286295

287296
def resolve(
288297
self,

lib/metric-config-parser/metric_config_parser/tests/conftest.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@
77
from git import Repo
88

99
from metric_config_parser.config import ConfigCollection
10-
from metric_config_parser.experiment import Branch, Experiment
10+
from metric_config_parser.experiment import Branch, BucketConfig, Experiment
1111

1212
TEST_DIR = Path(__file__).parent
1313

@@ -27,6 +27,9 @@ def experiments():
2727
reference_branch="b",
2828
is_high_population=False,
2929
app_name="firefox_desktop",
30+
bucket_config=BucketConfig(
31+
randomization_unit="group_id", namespace="namespace", start=0, count=10
32+
),
3033
),
3134
Experiment(
3235
experimenter_slug="test_slug",

lib/metric-config-parser/metric_config_parser/tests/test_experiment.py

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,31 @@ def test_control_branch(self, experiments, config_collection):
7373
configured = spec.resolve(experiments[0], config_collection)
7474
assert configured.experiment.reference_branch == "a"
7575

76+
def test_analysis_unit(self, experiments, config_collection):
77+
trivial = AnalysisSpec().resolve(experiments[0], config_collection)
78+
assert trivial.experiment.analysis_unit == AnalysisUnit.PROFILE_GROUP
79+
80+
conf = dedent(
81+
"""
82+
[experiment]
83+
analysis_unit = "client_id"
84+
"""
85+
)
86+
spec = AnalysisSpec.from_dict(toml.loads(conf))
87+
configured = spec.resolve(experiments[0], config_collection)
88+
assert configured.experiment.analysis_unit == AnalysisUnit.CLIENT
89+
90+
def test_analysis_unit_invalid(self):
91+
conf = dedent(
92+
"""
93+
[experiment]
94+
analysis_unit = "not_valid_id"
95+
"""
96+
)
97+
98+
with pytest.raises(ClassValidationError):
99+
AnalysisSpec.from_dict(toml.loads(conf))
100+
76101
def test_recognizes_segments(self, experiments, config_collection):
77102
conf = dedent(
78103
"""

lib/metric-config-parser/pyproject.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
44

55
[project]
66
name = "mozilla-metric-config-parser"
7-
version = "2025.11.1"
7+
version = "2025.12.1"
88
authors = [{ name = "Mozilla Corporation", email = "[email protected]" }]
99
description = "Parses metric configuration files."
1010
readme = "README.md"

0 commit comments

Comments
 (0)