Skip to content
Merged
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
4 changes: 0 additions & 4 deletions experimenter/experimenter/experiments/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -995,10 +995,6 @@ class FirefoxLabsGroups(models.TextChoices):
Optional - We believe this outcome will <describe impact> on <core metric>
""" # noqa

KPI_AREA = "KPI Metrics"
NOTABLE_CHANGES_AREA = "Notable Changes"
DEFAULT_METRIC_AREAS = [NOTABLE_CHANGES_AREA, KPI_AREA]

DAILY_ACTIVE_USERS = "client_level_daily_active_users_v2"
DAYS_OF_USE = "days_of_use"
RETENTION = "retained"
Expand Down
140 changes: 107 additions & 33 deletions experimenter/experimenter/experiments/models.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
from collections import defaultdict
from dataclasses import dataclass
from decimal import Decimal
from itertools import chain
from pathlib import Path
from typing import Any, Optional
from urllib.parse import urlencode, urljoin
Expand Down Expand Up @@ -32,6 +33,7 @@
TargetingMultipleKintoCollectionsError,
)
from experimenter.nimbus_ui.constants import NimbusUIConstants
from experimenter.outcomes import Outcomes
from experimenter.projects.models import Project
from experimenter.targeting.constants import TargetingConstants

Expand Down Expand Up @@ -1245,7 +1247,8 @@ def results_sidebar_sections(self):
{
"title": "All metrics",
"subitems": [
{"title": metric} for metric in self.default_metrics.values()
{"title": metric["friendly_name"]}
for metric in self.get_remaining_metrics_metadata()
],
},
]
Expand Down Expand Up @@ -1308,30 +1311,109 @@ def timeline(self):

return timeline_entries

@property
def metric_areas(self):
metric_areas = NimbusConstants.DEFAULT_METRIC_AREAS.copy()
def get_metric_areas(
self, analysis_basis, segment, reference_branch, window="overall"
):
metric_areas = {
NimbusUIConstants.NOTABLE_METRIC_AREA: [],
NimbusUIConstants.KPI_AREA: self.get_kpi_metrics(
analysis_basis, segment, reference_branch, window
),
}

metrics_metadata = {}
if self.results_data:
metrics_metadata = (
self.results_data.get("v3", {}).get("metadata", {}).get("metrics", {})
)

all_outcome_metric_slugs = []
for slug in chain(self.primary_outcomes, self.secondary_outcomes):
outcome = Outcomes.get_by_slug_and_application(slug, self.application)
metrics = outcome.metrics if outcome else []
outcome_metrics = []

for metric in metrics:
formatted_metric = {
"slug": metric.slug,
"description": (
metric.description
if metric.description
else metrics_metadata.get(metric.slug, {}).get("description", "")
),
"group": "other_metrics",
"friendly_name": (
metric.friendly_name
if metric.friendly_name
else metrics_metadata.get(metric.slug, {}).get(
"friendly_name", metric.slug
)
),
}
if formatted_metric not in outcome_metrics:
outcome_metrics.append(formatted_metric)
all_outcome_metric_slugs.append(metric.slug)

outcome_metrics.sort(key=lambda m: m["friendly_name"])
metric_areas[outcome.friendly_name if outcome else slug] = outcome_metrics

metric_areas[NimbusUIConstants.OTHER_METRICS_AREA] = (
self.get_remaining_metrics_metadata(exclude_slugs=all_outcome_metric_slugs)
)

# TODO(13721): check all metrics with data and add their areas to metric_areas
# if not already present
window_results = self.get_window_results(analysis_basis, segment, window)

def is_metric_notable(slug, group):
for branch_data in window_results.values():
metric_data = (
branch_data.get("branch_data", {}).get(group, {}).get(slug, {})
)
for branch_significance in metric_data.get("significance", {}).values():
if (
"positive" in branch_significance.get(window, {}).values()
or "negative" in branch_significance.get(window, {}).values()
):
return True
return False

for metrics in metric_areas.values():
for metric in metrics:
if (
is_metric_notable(metric["slug"], metric["group"])
and metric not in metric_areas[NimbusUIConstants.NOTABLE_METRIC_AREA]
):
metric_areas[NimbusUIConstants.NOTABLE_METRIC_AREA].append(metric)

metric_areas[NimbusUIConstants.NOTABLE_METRIC_AREA].sort(
key=lambda m: m["friendly_name"]
)
return metric_areas

@property
def default_metrics(self):
def get_remaining_metrics_metadata(self, exclude_slugs=None):
analysis_data = self.results_data.get("v3", {}) if self.results_data else {}
other_metrics = analysis_data.get("other_metrics", {})
metadata = analysis_data.get("metadata", {})
metrics_metadata = metadata.get("metrics", {}) if metadata else {}
default_metrics = {}
defaults = []

for value in other_metrics.values():
for metricKey, metricValue in value.items():
default_metrics[metricKey] = metrics_metadata.get(metricKey, {}).get(
"friendlyName", metricValue
for group, default_metrics in other_metrics.items():
for slug, metric_friendly_name in default_metrics.items():
if exclude_slugs and slug in exclude_slugs:
continue
defaults.append(
{
"slug": slug,
"description": metrics_metadata.get(slug, {}).get(
"description", ""
),
"group": group,
"friendly_name": metric_friendly_name,
}
)

return default_metrics
defaults.sort(key=lambda m: m["friendly_name"])

return defaults

def get_branch_data(self, analysis_basis, selected_segment, window="overall"):
window_results = self.get_window_results(analysis_basis, selected_segment, window)
Expand Down Expand Up @@ -1475,27 +1557,19 @@ def get_kpi_metrics(
def get_metric_data(
self, analysis_basis, segment, reference_branch, window="overall"
):
metric_areas = self.metric_areas
metric_areas = self.get_metric_areas(
analysis_basis, segment, reference_branch, window
)
metric_data = {}

for area in metric_areas:
match area:
case NimbusConstants.KPI_AREA:
kpi_metric_list = self.get_metric_area_data(
self.get_kpi_metrics(
analysis_basis,
segment,
reference_branch,
window,
),
analysis_basis,
segment,
reference_branch,
window,
)
metric_data[area] = kpi_metric_list
case _:
continue
for area, metrics in metric_areas.items():
metric_data[area] = self.get_metric_area_data(
metrics,
analysis_basis,
segment,
reference_branch,
window,
)

return metric_data

Expand Down
50 changes: 31 additions & 19 deletions experimenter/experimenter/experiments/tests/test_models.py
Original file line number Diff line number Diff line change
Expand Up @@ -2300,25 +2300,6 @@ def test_sidebar_links_sets_active_flag_correctly(self):
f"{link['title']} should not be active for path {path}",
)

def test_default_metrics_set_on_creation(self):
experiment = NimbusExperimentFactory.create()

experiment.results_data = {
"v3": {
"other_metrics": {"group": {"metricA": "Metric A"}},
"metadata": {
"metrics": {"metricA": {"friendlyName": "Friendly Metric A"}},
},
}
}

experiment.save()

self.assertEqual(
experiment.default_metrics,
{"metricA": "Friendly Metric A"},
)

def test_get_branch_data_returns_correct_data(self):
experiment = NimbusExperimentFactory.create()
branch_a = NimbusBranchFactory.create(
Expand Down Expand Up @@ -2654,6 +2635,37 @@ def test_get_kpi_metrics_returns_correct_metrics(

self.assertListEqual(kpi_metrics, expected_kpi_metrics)

def test_get_defaults_metrics_with_exclusions(self):
experiment = NimbusExperimentFactory.create()

experiment.results_data = {
"v3": {
"metadata": {
"metrics": {
"metricA": {
"retained": "Retained",
"search_count": "Search Count",
}
},
},
"other_metrics": {
"other_metrics": {
"retained": "2 Week Retention",
"search_count": "Search Count",
}
},
}
}
experiment.save()

remaining_metrics = experiment.get_remaining_metrics_metadata(
exclude_slugs=["search_count"]
)
metric_slugs = [metric.get("slug") for metric in remaining_metrics]

self.assertIn("retained", metric_slugs)
self.assertNotIn("search_count", metric_slugs)

def test_get_max_metric_value(self):
experiment = NimbusExperimentFactory.create()
branch_a = NimbusBranchFactory.create(
Expand Down
3 changes: 3 additions & 0 deletions experimenter/experimenter/nimbus_ui/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -145,6 +145,9 @@ class NimbusUIConstants:
"Next steps",
"Project impact",
]
KPI_AREA = "KPI Metrics"
OTHER_METRICS_AREA = "Other Metrics"
NOTABLE_METRIC_AREA = "Notable Changes"
FEATURE_PAGE_LINKS = {
"feature_learn_more_url": "https://experimenter.info/for-product#track-your-feature-health",
"deliveries_table_tooltip": """This shows all Nimbus experiments, rollouts, Labs
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
<div class="modal fade"
id="{{ experiment_slug }}-{{ metric_info.slug }}"
id="{{ experiment_slug }}-{{ metric_info.slug }}-{{ area|slugify }}"
tabindex="-1"
aria-labelledby="exampleModalLabel"
aria-hidden="true">
Expand Down
Loading