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
16 changes: 8 additions & 8 deletions etl/glean_etl.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
from .glean import GleanApp
from .glean_auto_events import get_auto_events_for_app, get_auto_events_names
from .looker import (
get_looker_explore_metadata_for_metric,
get_looker_explore_metadata_for_ping,
get_looker_explores_for_metric,
get_looker_explores_for_ping,
get_looker_monitoring_metadata_for_event,
)
from .search import create_metrics_search_js
Expand Down Expand Up @@ -391,11 +391,11 @@ def write_glean_metadata(output_dir, functions_dir, app_names=None):
table=stable_ping_table_name,
channel=app_channel if app_channel else "release",
)
looker_explore = get_looker_explore_metadata_for_ping(
looker_explores = get_looker_explores_for_ping(
looker_namespaces, app, app_group, ping
)
if not app_is_deprecated and looker_explore:
variant_data.update({"looker_explore": looker_explore})
if not app_is_deprecated and looker_explores:
variant_data.update({"looker_explores": looker_explores})
ping_data["variants"].append(variant_data)
app_variant_table_dir = os.path.join(app_table_dir, _get_resource_path(app.app_id))
os.makedirs(app_variant_table_dir, exist_ok=True)
Expand Down Expand Up @@ -549,16 +549,16 @@ def write_glean_metadata(output_dir, functions_dir, app_names=None):
}
# FIXME: if we allow the metadata format to change, we can
# just set it up all in one go above
looker_metadata = get_looker_explore_metadata_for_metric(
looker_explores = get_looker_explores_for_metric(
looker_namespaces,
app,
app_group,
metric,
ping_name,
ping_name in pings_with_client_id,
)
if looker_metadata:
ping_data[ping_name].update({"looker": looker_metadata})
if looker_explores:
ping_data[ping_name].update({"looker_explores": looker_explores})
glam_metadata = get_glam_metadata_for_metric(app, metric, ping_name)
ping_data[ping_name].update(glam_metadata)

Expand Down
85 changes: 59 additions & 26 deletions etl/looker.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,20 +31,39 @@ def _looker_explore_exists(looker_namespaces, app_name, explore_name):
)


def _get_looker_ping_explore(
def _get_looker_ping_explores(
looker_namespaces, app_name, ping_name, _table_name, app_channel, app_group
):
explores = []
ping_name_snakecase = stringcase.snakecase(ping_name)
if _looker_explore_exists(looker_namespaces, app_name, ping_name_snakecase):
url = furl(f"https://mozilla.cloud.looker.com/explore/{app_name}/{ping_name_snakecase}")
# if there are multiple channels, we need a channel identifier
if len(app_group["app_ids"]) > 1 and app_channel:
url = url.add({f"f[{ping_name_snakecase}.channel]": app_channel})
return {"name": ping_name_snakecase, "url": url.url}
return None
explores.append({"name": ping_name_snakecase, "url": url.url})
return explores or None


def _get_looker_event_explore(looker_namespaces, app_name, app_channel, app_group):
def _get_looker_event_explores(looker_namespaces, app_name, app_channel, app_group):
explores = []

if _looker_explore_exists(looker_namespaces, app_name, "events_stream"):
url = furl(f"https://mozilla.cloud.looker.com/explore/{app_name}/events_stream").add(
{
"fields": ",".join(
(
"events_stream.submission_date",
"events_stream.event_count",
"events_stream.client_count",
)
)
}
)
if len(app_group["app_ids"]) > 1 and app_channel:
url.add({"f[events_stream.normalized_channel]": app_channel})
explores.append({"name": "events_stream", "url": url.url})

# firefox_desktop has an "events" explore that is for legacy telemetry,
# not Glean
if (
Expand All @@ -56,7 +75,7 @@ def _get_looker_event_explore(looker_namespaces, app_name, app_channel, app_grou
)
if len(app_group["app_ids"]) > 1 and app_channel:
url.add({"f[events.normalized_channel]": app_channel})
return {"name": "event_counts", "url": url.url}
explores.append({"name": "event_counts", "url": url.url})
# firefox_desktop Glean events explore is glean_event_counts
elif _looker_explore_exists(looker_namespaces, app_name, "glean_event_counts"):
url = furl(f"https://mozilla.cloud.looker.com/explore/{app_name}/glean_event_counts").add(
Expand All @@ -67,24 +86,25 @@ def _get_looker_event_explore(looker_namespaces, app_name, app_channel, app_grou
)
if len(app_group["app_ids"]) > 1 and app_channel:
url.add({"f[events.normalized_channel]": app_channel})
return {"name": "glean_event_counts", "url": url.url}
explores.append({"name": "glean_event_counts", "url": url.url})
elif _looker_explore_exists(looker_namespaces, app_name, "funnel_analysis"):
url = furl(f"https://mozilla.cloud.looker.com/explore/{app_name}/funnel_analysis").add(
{"fields": "funnel_analysis.count_completed_step_1"}
)
if len(app_group["app_ids"]) > 1 and app_channel:
url.add({"f[funnel_analysis.app_channel]": app_channel})
return {"name": "funnel_analysis", "url": url.url}
return None
explores.append({"name": "funnel_analysis", "url": url.url})

return explores or None

def get_looker_explore_metadata_for_ping(looker_namespaces, app, app_group, ping):

def get_looker_explores_for_ping(looker_namespaces, app, app_group, ping):
if ping.identifier == "events":
return _get_looker_event_explore(
return _get_looker_event_explores(
looker_namespaces, app.app_name, app.app.get("app_channel"), app_group
)

return _get_looker_ping_explore(
return _get_looker_ping_explores(
looker_namespaces,
app.app_name,
ping.identifier,
Expand All @@ -94,19 +114,23 @@ def get_looker_explore_metadata_for_ping(looker_namespaces, app, app_group, ping
)


def get_looker_explore_metadata_for_metric(
def get_looker_explores_for_metric(
looker_namespaces, app, app_group, metric, ping_name, ping_has_client_id
):
# We deliberately don't show Looker information for deprecated applications
if app.app.get("deprecated"):
return None

metric_type = metric.definition["type"]
metric_name_snakecase = stringcase.snakecase(metric.identifier)
ping_name_snakecase = stringcase.snakecase(ping_name)

base_looker_explore = (
_get_looker_event_explore(
base_looker_explores = (
_get_looker_event_explores(
looker_namespaces, app.app_name, app.app.get("app_channel"), app_group
)
if metric_type == "event"
else _get_looker_ping_explore(
else _get_looker_ping_explores(
looker_namespaces,
app.app_name,
ping_name,
Expand All @@ -116,8 +140,8 @@ def get_looker_explore_metadata_for_metric(
)
)

# we deliberately don't show looker information for deprecated applications
if not app.app.get("deprecated") and base_looker_explore:
explores = []
for base_looker_explore in base_looker_explores or []:
looker_metric_link = None
if metric_type == "event":
(metric_category, metric_name) = get_event_name_and_category(metric.identifier)
Expand All @@ -142,12 +166,19 @@ def get_looker_explore_metadata_for_metric(
"f[step_1.category]": f'"{metric_category}"',
}
)
elif base_looker_explore["name"] == "events_stream":
looker_metric_link = furl(base_looker_explore["url"]).add(
{
"f[events_stream.event_category]": f'"{metric_category}"',
"f[events_stream.event_name]": f'"{metric_name}"',
}
)
else:
# this should never happen (unless we made a mistake in getting the
# base looker explore link)
raise Exception(f"Unexpected base looker explore {base_looker_explore['name']}")
# for counters, we can use measures directly
if metric_type == "counter":
elif metric_type == "counter":
looker_metric_link = furl(base_looker_explore["url"]).add(
{
"fields": ",".join(
Expand Down Expand Up @@ -254,15 +285,17 @@ def get_looker_explore_metadata_for_metric(
)

if looker_metric_link:
return {
"base": base_looker_explore,
"metric": {
"name": metric.identifier,
"url": looker_metric_link.add({"toggle": "vis"}).url,
},
}
explores.append(
{
"base": base_looker_explore,
"metric": {
"name": metric.identifier,
"url": looker_metric_link.add({"toggle": "vis"}).url,
},
}
)

return None
return explores or None


def get_looker_monitoring_metadata_for_event(app, app_group, metric):
Expand Down
62 changes: 33 additions & 29 deletions etl_tests/test_looker.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import pytest

from etl.glean import GleanApp, GleanMetric, GleanPing
from etl.looker import get_looker_explore_metadata_for_metric, get_looker_explore_metadata_for_ping
from etl.looker import get_looker_explores_for_metric, get_looker_explores_for_ping


@pytest.fixture
Expand Down Expand Up @@ -56,35 +56,37 @@ def fake_app_group():
return dict(name="fenix", app_ids=["org.mozilla.firefox"])


def test_get_looker_explore_metadata_for_ping(fake_namespaces, fake_app, fake_app_group, fake_ping):
assert get_looker_explore_metadata_for_ping(
fake_namespaces, fake_app, fake_app_group, fake_ping
) == {"name": "metrics", "url": "https://mozilla.cloud.looker.com/explore/fenix/metrics"}
def test_get_looker_explores_for_ping(fake_namespaces, fake_app, fake_app_group, fake_ping):
assert get_looker_explores_for_ping(fake_namespaces, fake_app, fake_app_group, fake_ping) == [
{"name": "metrics", "url": "https://mozilla.cloud.looker.com/explore/fenix/metrics"}
]


def test_get_looker_explore_metadata_for_timespan_metric(
def test_get_looker_explores_for_timespan_metric(
fake_namespaces, fake_app, fake_app_group, fake_ping, fake_timespan_metric
):
assert get_looker_explore_metadata_for_metric(
assert get_looker_explores_for_metric(
fake_namespaces,
fake_app,
fake_app_group,
fake_timespan_metric,
fake_ping.identifier,
False,
) == {
"base": {
"name": "metrics",
"url": "https://mozilla.cloud.looker.com/explore/fenix/metrics",
},
"metric": {
"name": "mytimespan",
"url": "https://mozilla.cloud.looker.com/explore/fenix/metrics?fields=metrics.submission_date%2Cmedian_of_mytimespan&dynamic_fields=%5B%7B%22measure%22%3A+%22median_of_mytimespan%22%2C+%22label%22%3A+%22Median+of+mytimespan%22%2C+%22based_on%22%3A+%22metrics.metrics__timespan__mytimespan__value%22%2C+%22expression%22%3A+%22%22%2C+%22type%22%3A+%22median%22%7D%5D&toggle=vis", # noqa
},
}
) == [
{
"base": {
"name": "metrics",
"url": "https://mozilla.cloud.looker.com/explore/fenix/metrics",
},
"metric": {
"name": "mytimespan",
"url": "https://mozilla.cloud.looker.com/explore/fenix/metrics?fields=metrics.submission_date%2Cmedian_of_mytimespan&dynamic_fields=%5B%7B%22measure%22%3A+%22median_of_mytimespan%22%2C+%22label%22%3A+%22Median+of+mytimespan%22%2C+%22based_on%22%3A+%22metrics.metrics__timespan__mytimespan__value%22%2C+%22expression%22%3A+%22%22%2C+%22type%22%3A+%22median%22%7D%5D&toggle=vis", # noqa
},
}
]


def test_get_looker_explore_metadata_for_metric_unioned_app(
def test_get_looker_explores_for_metric_unioned_app(
fake_namespaces, fake_app, fake_ping, fake_timespan_metric
):
# The application needs an "app channel" to trigger the relevant
Expand All @@ -106,20 +108,22 @@ def test_get_looker_explore_metadata_for_metric_unioned_app(
"app_name": "fenix",
}

assert get_looker_explore_metadata_for_metric(
assert get_looker_explores_for_metric(
fake_namespaces,
fake_app,
FAKE_APP_GROUP,
fake_timespan_metric,
fake_ping.identifier,
False,
) == {
"base": {
"name": "metrics",
"url": "https://mozilla.cloud.looker.com/explore/fenix/metrics?f%5Bmetrics.channel%5D=nightly", # noqa
},
"metric": {
"name": "mytimespan",
"url": "https://mozilla.cloud.looker.com/explore/fenix/metrics?f%5Bmetrics.channel%5D=nightly&fields=metrics.submission_date%2Cmedian_of_mytimespan&dynamic_fields=%5B%7B%22measure%22%3A+%22median_of_mytimespan%22%2C+%22label%22%3A+%22Median+of+mytimespan%22%2C+%22based_on%22%3A+%22metrics.metrics__timespan__mytimespan__value%22%2C+%22expression%22%3A+%22%22%2C+%22type%22%3A+%22median%22%7D%5D&toggle=vis", # noqa
},
}
) == [
{
"base": {
"name": "metrics",
"url": "https://mozilla.cloud.looker.com/explore/fenix/metrics?f%5Bmetrics.channel%5D=nightly", # noqa
},
"metric": {
"name": "mytimespan",
"url": "https://mozilla.cloud.looker.com/explore/fenix/metrics?f%5Bmetrics.channel%5D=nightly&fields=metrics.submission_date%2Cmedian_of_mytimespan&dynamic_fields=%5B%7B%22measure%22%3A+%22median_of_mytimespan%22%2C+%22label%22%3A+%22Median+of+mytimespan%22%2C+%22based_on%22%3A+%22metrics.metrics__timespan__mytimespan__value%22%2C+%22expression%22%3A+%22%22%2C+%22type%22%3A+%22median%22%7D%5D&toggle=vis", # noqa
},
}
]
42 changes: 22 additions & 20 deletions src/pages/MetricDetail.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@
{/if}
</td>
</tr>
{#if pingData.looker}
{#if pingData.looker_explores}
<tr>
<td
>Looker <HelpHoverable
Expand All @@ -469,26 +469,28 @@
<td>
{#if metric.send_in_pings.length === 1 && metric.send_in_pings[0] === "events" && metric.type !== "event"}
This metric is a <code>{metric.type}</code> metric. Currently,
event count explores only support <code>event</code> metrics.
event explores only support <code>event</code> metrics.
{:else}
<div>
In
<AuthenticatedLink
href={pingData.looker.base.url}
label={pingData.looker.base.name}
type="MetricDetail.Access.Looker.PingData.BaseURL"
>
{pingData.looker.base.name}
</AuthenticatedLink>
as
<AuthenticatedLink
href={pingData.looker.metric.url}
label={pingData.looker.metric.name}
type="MetricDetail.Access.Looker.PingData.MetricURL"
>
{pingData.looker.metric.name}
</AuthenticatedLink>
</div>
{#each pingData.looker_explores as explore}
<div>
In
<AuthenticatedLink
href={explore.base.url}
label={explore.base.name}
type="MetricDetail.Access.Looker.PingData.BaseURL"
>
{explore.base.name}
</AuthenticatedLink>
as
<AuthenticatedLink
href={explore.metric.url}
label={explore.metric.name}
type="MetricDetail.Access.Looker.PingData.MetricURL"
>
{explore.metric.name}
</AuthenticatedLink>
</div>
{/each}
{/if}
{#if pingData.event_monitoring}
<div>
Expand Down
12 changes: 8 additions & 4 deletions src/pages/PingDetail.svelte
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@
{params.app}.{params.ping}
</AuthenticatedLink>
</td>
{#if selectedAppVariant.looker_explore}
{#if selectedAppVariant.looker_explores}
<tr>
<td>
Looker
Expand All @@ -197,9 +197,13 @@
/>
</td>
<td>
<AuthenticatedLink href={selectedAppVariant.looker_explore.url}>
{selectedAppVariant.looker_explore.name}
</AuthenticatedLink>
{#each selectedAppVariant.looker_explores as explore}
<div>
<AuthenticatedLink href={explore.url}>
{explore.name}
</AuthenticatedLink>
</div>
{/each}
</td>
</tr>
{/if}
Expand Down