diff --git a/etl/glean_etl.py b/etl/glean_etl.py index df08a0fc2..e9785f4ce 100644 --- a/etl/glean_etl.py +++ b/etl/glean_etl.py @@ -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 @@ -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) @@ -549,7 +549,7 @@ 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, @@ -557,8 +557,8 @@ def write_glean_metadata(output_dir, functions_dir, app_names=None): 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) diff --git a/etl/looker.py b/etl/looker.py index 78490e7cf..4f91f0554 100644 --- a/etl/looker.py +++ b/etl/looker.py @@ -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 ( @@ -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( @@ -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, @@ -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, @@ -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) @@ -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( @@ -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): diff --git a/etl_tests/test_looker.py b/etl_tests/test_looker.py index 44caa0517..4aefd426d 100644 --- a/etl_tests/test_looker.py +++ b/etl_tests/test_looker.py @@ -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 @@ -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 @@ -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 + }, + } + ] diff --git a/src/pages/MetricDetail.svelte b/src/pages/MetricDetail.svelte index 452baa741..0b14fb160 100644 --- a/src/pages/MetricDetail.svelte +++ b/src/pages/MetricDetail.svelte @@ -459,7 +459,7 @@ {/if} - {#if pingData.looker} + {#if pingData.looker_explores}
{metric.type} metric. Currently,
- event count explores only support event metrics.
+ event explores only support event metrics.
{:else}
-