Skip to content

add visit level widget table#9346

Merged
jaredsnyder merged 10 commits into
mainfrom
widget_visits_table
May 15, 2026
Merged

add visit level widget table#9346
jaredsnyder merged 10 commits into
mainfrom
widget_visits_table

Conversation

@jaredsnyder
Copy link
Copy Markdown
Contributor

This PR adds a visit-level aggregate table for widgets.

Widgets key experiments related to widgets are currently happening, and widgets are expected to be rolled out widely in June. This table enables the analysis of widget interactions at the visit level and will be used to create a client-level table that can be used in experiments.

The table was modeled on firefox_desktop_derived.newtab_visits_daily_v1. It copies the same strategy for creating dimensions for visits.

The table was manually verified for several visits, as documented here: https://docs.google.com/document/d/1d4H1JegVWJ4EhvAvVd95VD0WftS8nozdD6xitx81ji0/edit?tab=t.0

Copy link
Copy Markdown
Contributor

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall this looks like a clean, well-scoped addition that closely mirrors the established newtab_visits_daily_v2 pattern, which makes it easy to read and reason about. Manual verification via the linked doc and the consistent dimension handling between visit-level and widget-level grains are both nice. Comments below are mostly polish / robustness suggestions — none are blockers.

Themes:

  • Grain & contracts. The metadata describes the grain as (newtab_visit_id, widget_name, widget_size) per submission_date, but the SQL doesn't guard against NULL widget_name/widget_size from malformed extras, and the LEFT JOIN can leave user_action_counts as NULL for impression-only widgets. Worth deciding whether either is intentional and documenting it.
  • DRY. Three CTEs each re-extract newtab_visit_id / widget_name / widget_size from event_details; hoisting these into events_unnested would simplify the rest of the query.
  • View surface. The new view adds windows_version but keeps the underlying windows_build_number. Excluding the raw build number (or at least describing both in the view metadata) would tighten the user-facing contract. The metadata description also currently omits the app_name column the view adds.
  • Style. A couple of comment-indentation slips in visit_aggregations and curly-apostrophe Unicode characters in two schema descriptions — ./bqetl format plus a quick s//'/ should clean these up.
  • Data quality. No checks.sql was added; given this table will back experiment analysis, a few standard partition/uniqueness/non-null checks would be cheap insurance.
  • Minor: The PR description references newtab_visits_daily_v1 as the model table, but the actual current table in the repo is newtab_visits_daily_v2. Worth updating the description for future readers chasing the lineage.

The reviewer checklist item about not duplicating an existing dataset is worth a quick gut-check from a domain owner — confirming that no existing firefox_desktop_derived widget aggregation already covers this — but assuming that's been validated, the structure here is sound.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

@github-actions

This comment has been minimized.

Copy link
Copy Markdown
Contributor

@land-edi land-edi left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am wondering whether we should create a new dataset for widgets given it is copying all the same columns we already have in newtab_visits_daily, which include today everything except widgets. Do you think it is will be better to just include the few widget columns you are creating itno that dataset? Beside that, everything else looks good

@jaredsnyder
Copy link
Copy Markdown
Contributor Author

Did a quick run of the most recent file and compared to the validated v1 version. There were <1000 discrepancies and the enabled and disabled columns look fine in the aggregate so I think we're good. Updated the validation doc with this info.

@github-actions
Copy link
Copy Markdown
Contributor

Integration report for "add visit level widget table"

sql.diff

Click to expand!
diff -bur --no-dereference --new-file /tmp/workspace/main-generated-sql/dags/bqetl_newtab.py /tmp/workspace/generated-dags/dags/bqetl_newtab.py
--- /tmp/workspace/main-generated-sql/dags/bqetl_newtab.py	2026-05-15 16:09:11.675042989 +0000
+++ /tmp/workspace/generated-dags/dags/bqetl_newtab.py	2026-05-15 16:09:10.778050686 +0000
@@ -289,6 +289,21 @@
         depends_on_past=False,
     )
 
+    firefox_desktop_derived__widgets_visit_daily__v1 = bigquery_etl_query(
+        task_id="firefox_desktop_derived__widgets_visit_daily__v1",
+        destination_table="widgets_visit_daily_v1",
+        dataset_id="firefox_desktop_derived",
+        project_id="moz-fx-data-shared-prod",
+        owner="jsnyder@mozilla.com",
+        email=[
+            "jsnyder@mozilla.com",
+            "mbowerman@mozilla.com",
+            "telemetry-alerts@mozilla.com",
+        ],
+        date_partition_parameter="submission_date",
+        depends_on_past=False,
+    )
+
     telemetry_derived__newtab_clients_daily_aggregates__v1 = bigquery_etl_query(
         task_id="telemetry_derived__newtab_clients_daily_aggregates__v1",
         destination_table="newtab_clients_daily_aggregates_v1",
@@ -421,6 +436,10 @@
         wait_for_copy_deduplicate_all
     )
 
+    firefox_desktop_derived__widgets_visit_daily__v1.set_upstream(
+        wait_for_copy_deduplicate_all
+    )
+
     telemetry_derived__newtab_clients_daily_aggregates__v1.set_upstream(
         wait_for_telemetry_derived__newtab_clients_daily__v1
     )
Only in /tmp/workspace/generated-sql/sql/moz-fx-data-shared-prod/firefox_desktop: widgets_visit_daily
Only in /tmp/workspace/generated-sql/sql/moz-fx-data-shared-prod/firefox_desktop_derived: widgets_visit_daily_v1
diff -bur --no-dereference --new-file /tmp/workspace/main-generated-sql/sql/moz-fx-data-shared-prod/firefox_desktop/widgets_visit_daily/metadata.yaml /tmp/workspace/generated-sql/sql/moz-fx-data-shared-prod/firefox_desktop/widgets_visit_daily/metadata.yaml
--- /tmp/workspace/main-generated-sql/sql/moz-fx-data-shared-prod/firefox_desktop/widgets_visit_daily/metadata.yaml	1970-01-01 00:00:00.000000000 +0000
+++ /tmp/workspace/generated-sql/sql/moz-fx-data-shared-prod/firefox_desktop/widgets_visit_daily/metadata.yaml	2026-05-15 16:09:08.163998290 +0000
@@ -0,0 +1,18 @@
+friendly_name: Widgets Visit Daily
+description: |-
+  Daily aggregation of newtab widget impression and user-event metrics.
+  The granularity is one row per Firefox Desktop newtab_visit_id, widget_name, widget_size
+  and submission_date, unpacking the `widgets_impression` and `widgets_user_event`
+  events from the `newtab` event category. Adds a derived `windows_version` column
+  over the underlying `firefox_desktop_derived.widgets_visit_daily_v1` table.
+owners:
+- jsnyder@mozilla.com
+labels:
+  owner1: jsnyder
+workgroup_access:
+- role: roles/bigquery.dataViewer
+  members:
+  - workgroup:mozilla-confidential/data-viewers
+references:
+  view.sql:
+  - moz-fx-data-shared-prod.firefox_desktop_derived.widgets_visit_daily_v1
diff -bur --no-dereference --new-file /tmp/workspace/main-generated-sql/sql/moz-fx-data-shared-prod/firefox_desktop/widgets_visit_daily/view.sql /tmp/workspace/generated-sql/sql/moz-fx-data-shared-prod/firefox_desktop/widgets_visit_daily/view.sql
--- /tmp/workspace/main-generated-sql/sql/moz-fx-data-shared-prod/firefox_desktop/widgets_visit_daily/view.sql	1970-01-01 00:00:00.000000000 +0000
+++ /tmp/workspace/generated-sql/sql/moz-fx-data-shared-prod/firefox_desktop/widgets_visit_daily/view.sql	2026-05-15 16:09:08.172998485 +0000
@@ -0,0 +1,8 @@
+CREATE OR REPLACE VIEW
+  `moz-fx-data-shared-prod.firefox_desktop.widgets_visit_daily`
+AS
+SELECT
+  mozfun.norm.glean_windows_version_info(os, os_version, windows_build_number) AS windows_version,
+  *,
+FROM
+  `moz-fx-data-shared-prod.firefox_desktop_derived.widgets_visit_daily_v1`
diff -bur --no-dereference --new-file /tmp/workspace/main-generated-sql/sql/moz-fx-data-shared-prod/firefox_desktop_derived/widgets_visit_daily_v1/metadata.yaml /tmp/workspace/generated-sql/sql/moz-fx-data-shared-prod/firefox_desktop_derived/widgets_visit_daily_v1/metadata.yaml
--- /tmp/workspace/main-generated-sql/sql/moz-fx-data-shared-prod/firefox_desktop_derived/widgets_visit_daily_v1/metadata.yaml	1970-01-01 00:00:00.000000000 +0000
+++ /tmp/workspace/generated-sql/sql/moz-fx-data-shared-prod/firefox_desktop_derived/widgets_visit_daily_v1/metadata.yaml	2026-05-15 16:09:08.275000704 +0000
@@ -0,0 +1,35 @@
+friendly_name: Widgets Visit Daily
+description: |-
+  A daily computation of newtab widgets impression and user-event metrics.
+  One row per newtab_visit_id, widget_name, widget_size, and submission_date, unpacking
+  the `widgets_impression` and `widgets_user_event` events from the
+  `newtab` event category.
+owners:
+- jsnyder@mozilla.com
+labels:
+  application: newtab
+  incremental: true
+  schedule: daily
+  dag: bqetl_newtab
+  owner1: jsnyder
+scheduling:
+  dag_name: bqetl_newtab
+bigquery:
+  time_partitioning:
+    type: day
+    field: submission_date
+    require_partition_filter: true
+    expiration_days: null
+  clustering:
+    fields:
+    - channel
+    - country
+    - widget_name
+    - sample_id
+workgroup_access:
+- role: roles/bigquery.dataViewer
+  members:
+  - workgroup:mozilla-confidential/data-viewers
+references:
+  query.sql:
+  - moz-fx-data-shared-prod.firefox_desktop_stable.newtab_v1
diff -bur --no-dereference --new-file /tmp/workspace/main-generated-sql/sql/moz-fx-data-shared-prod/firefox_desktop_derived/widgets_visit_daily_v1/query.sql /tmp/workspace/generated-sql/sql/moz-fx-data-shared-prod/firefox_desktop_derived/widgets_visit_daily_v1/query.sql
--- /tmp/workspace/main-generated-sql/sql/moz-fx-data-shared-prod/firefox_desktop_derived/widgets_visit_daily_v1/query.sql	1970-01-01 00:00:00.000000000 +0000
+++ /tmp/workspace/generated-sql/sql/moz-fx-data-shared-prod/firefox_desktop_derived/widgets_visit_daily_v1/query.sql	2026-05-15 16:09:08.335002008 +0000
@@ -0,0 +1,167 @@
+WITH events_unnested AS (
+  SELECT
+    DATE(submission_timestamp) AS submission_date,
+    submission_timestamp,
+    client_info.client_id AS client_id,
+    SAFE_CAST(
+      mozfun.norm.browser_version_info(client_info.app_display_version).major_version AS INT64
+    ) AS app_version,
+    normalized_os AS os,
+    normalized_os_version AS os_version,
+    client_info.windows_build_number AS windows_build_number,
+    normalized_channel AS channel,
+    client_info.locale AS locale,
+    normalized_country_code AS country,
+    sample_id,
+    metrics.string.newtab_homepage_category AS homepage_category,
+    metrics.string.newtab_newtab_category AS newtab_category,
+    metrics.boolean.pocket_enabled AS organic_content_enabled,
+    metrics.boolean.pocket_sponsored_stories_enabled AS sponsored_content_enabled,
+    metrics.boolean.topsites_sponsored_enabled AS sponsored_topsites_enabled,
+    metrics.boolean.topsites_enabled AS organic_topsites_enabled,
+    metrics.boolean.newtab_search_enabled AS newtab_search_enabled,
+    metrics.boolean.newtab_weather_enabled AS newtab_weather_enabled,
+    metrics.uuid.legacy_telemetry_client_id AS legacy_telemetry_client_id,
+    metrics.uuid.legacy_telemetry_profile_group_id AS profile_group_id,
+    metadata.geo.subdivision1 AS geo_subdivision,
+    metrics.string.search_engine_default_engine_id AS default_search_engine,
+    metrics.string.search_engine_private_engine_id AS default_private_search_engine,
+    metrics.quantity.topsites_rows AS topsite_rows,
+    metrics.quantity.topsites_sponsored_tiles_configured AS topsite_sponsored_tiles_configured,
+    metrics.string_list.newtab_blocked_sponsors AS newtab_blocked_sponsors,
+    metrics.string.newtab_locale AS newtab_locale,
+    metrics.string.newtab_content_surface_id AS newtab_content_surface_id,
+    ping_info AS ping_info,
+    mozfun.newtab.is_default_ui_v1(
+      category,
+      name,
+      extra,
+      metrics.string.newtab_homepage_category,
+      metrics.string.newtab_newtab_category
+    ) AS is_default_ui,
+    category AS event_category,
+    name AS event_name,
+    extra AS event_details,
+  FROM
+    `moz-fx-data-shared-prod.firefox_desktop_stable.newtab_v1`,
+    UNNEST(events)
+  WHERE
+    DATE(submission_timestamp) = @submission_date
+    AND category = 'newtab'
+    -- include `opened` so that is_default_ui can be derived per visit
+    AND name IN ('opened', 'widgets_impression', 'widgets_user_event', 'widgets_enabled')
+),
+visit_aggregations AS (
+  SELECT
+    submission_date,
+    client_id,
+    mozfun.map.get_key(event_details, 'newtab_visit_id') AS newtab_visit_id,
+      -- earliest ping submission_timestamp contributing to this visit
+    MIN(submission_timestamp) AS submission_timestamp,
+      -- ANY_VALUE: visit-level dimensions are consistent across events in a visit
+    ANY_VALUE(app_version) AS app_version,
+    ANY_VALUE(os) AS os,
+    ANY_VALUE(os_version) AS os_version,
+    ANY_VALUE(windows_build_number) AS windows_build_number,
+    ANY_VALUE(channel) AS channel,
+    ANY_VALUE(locale) AS locale,
+    ANY_VALUE(country) AS country,
+    ANY_VALUE(sample_id) AS sample_id,
+    ANY_VALUE(legacy_telemetry_client_id) AS legacy_telemetry_client_id,
+    ANY_VALUE(profile_group_id) AS profile_group_id,
+    ANY_VALUE(geo_subdivision) AS geo_subdivision,
+    ANY_VALUE(homepage_category) AS homepage_category,
+    ANY_VALUE(newtab_category) AS newtab_category,
+    ANY_VALUE(organic_content_enabled) AS organic_content_enabled,
+    ANY_VALUE(sponsored_content_enabled) AS sponsored_content_enabled,
+    ANY_VALUE(sponsored_topsites_enabled) AS sponsored_topsites_enabled,
+    ANY_VALUE(organic_topsites_enabled) AS organic_topsites_enabled,
+    ANY_VALUE(newtab_search_enabled) AS newtab_search_enabled,
+    ANY_VALUE(newtab_weather_enabled) AS newtab_weather_enabled,
+    ANY_VALUE(default_search_engine) AS default_search_engine,
+    ANY_VALUE(default_private_search_engine) AS default_private_search_engine,
+    ANY_VALUE(topsite_rows) AS topsite_rows,
+    ANY_VALUE(topsite_sponsored_tiles_configured) AS topsite_sponsored_tiles_configured,
+    ANY_VALUE(newtab_blocked_sponsors) AS newtab_blocked_sponsors,
+    IFNULL(
+      ANY_VALUE(newtab_content_surface_id),
+      mozfun.newtab.scheduled_surface_id_v1(ANY_VALUE(country), ANY_VALUE(newtab_locale))
+    ) AS newtab_content_surface_id,
+    ANY_VALUE(ping_info.experiments) AS experiments,
+    LOGICAL_OR(is_default_ui) AS is_default_ui,
+  FROM
+    events_unnested
+  GROUP BY
+    submission_date,
+    client_id,
+    newtab_visit_id
+),
+user_action_counts_per_widget AS (
+    -- Per-event-name, per-user_action, per-enabled-flag counts at widget grain.
+    -- Base CTE that is rolled up by widget_metrics.
+    -- widgets_impression rows have user_action = NULL and enabled = NULL.
+    -- widgets_enabled rows have user_action = NULL and enabled = TRUE/FALSE.
+  SELECT
+    submission_date,
+    client_id,
+    mozfun.map.get_key(event_details, 'newtab_visit_id') AS newtab_visit_id,
+    mozfun.map.get_key(event_details, 'widget_name') AS widget_name,
+    mozfun.map.get_key(event_details, 'widget_size') AS widget_size,
+    event_name,
+    mozfun.map.get_key(event_details, 'user_action') AS user_action,
+    SAFE_CAST(mozfun.map.get_key(event_details, 'enabled') AS BOOL) AS widget_enabled,
+    COUNT(*) AS count,
+  FROM
+    events_unnested
+  WHERE
+    event_name IN ('widgets_impression', 'widgets_user_event', 'widgets_enabled')
+    AND mozfun.map.get_key(event_details, 'widget_name') IS NOT NULL
+  GROUP BY
+    submission_date,
+    client_id,
+    newtab_visit_id,
+    widget_name,
+    widget_size,
+    event_name,
+    user_action,
+    widget_enabled
+),
+widget_metrics AS (
+  SELECT
+    submission_date,
+    client_id,
+    newtab_visit_id,
+    widget_name,
+    widget_size,
+    SUM(IF(event_name = 'widgets_impression', count, 0)) AS impression_count,
+    SUM(IF(event_name = 'widgets_user_event', count, 0)) AS user_event_count,
+    SUM(
+      IF(
+        event_name = 'widgets_user_event'
+        AND user_action IN ('change_size', 'learn_more'),
+        count,
+        0
+      )
+    ) AS change_size_or_learn_more_count,
+    SUM(IF(event_name = 'widgets_enabled' AND widget_enabled, count, 0)) AS enabled_count,
+    SUM(IF(event_name = 'widgets_enabled' AND NOT widget_enabled, count, 0)) AS disabled_count,
+    ARRAY_AGG(
+      IF(event_name = 'widgets_user_event', STRUCT(user_action, count), NULL) IGNORE NULLS
+    ) AS user_action_counts,
+  FROM
+    user_action_counts_per_widget
+  GROUP BY
+    submission_date,
+    client_id,
+    newtab_visit_id,
+    widget_name,
+    widget_size
+)
+SELECT
+  widget_metrics.*,
+  visit_aggregations.* EXCEPT (submission_date, client_id, newtab_visit_id),
+FROM
+  widget_metrics
+LEFT JOIN
+  visit_aggregations
+  USING (submission_date, client_id, newtab_visit_id)
diff -bur --no-dereference --new-file /tmp/workspace/main-generated-sql/sql/moz-fx-data-shared-prod/firefox_desktop_derived/widgets_visit_daily_v1/schema.yaml /tmp/workspace/generated-sql/sql/moz-fx-data-shared-prod/firefox_desktop_derived/widgets_visit_daily_v1/schema.yaml
--- /tmp/workspace/main-generated-sql/sql/moz-fx-data-shared-prod/firefox_desktop_derived/widgets_visit_daily_v1/schema.yaml	1970-01-01 00:00:00.000000000 +0000
+++ /tmp/workspace/generated-sql/sql/moz-fx-data-shared-prod/firefox_desktop_derived/widgets_visit_daily_v1/schema.yaml	2026-05-15 16:09:08.335002008 +0000
@@ -0,0 +1,187 @@
+fields:
+- name: submission_date
+  type: DATE
+  mode: NULLABLE
+  description: Day the event was received in the newtab ping
+- name: client_id
+  type: STRING
+  mode: NULLABLE
+  description: Unique ID for the client installation.
+- name: newtab_visit_id
+  type: STRING
+  mode: NULLABLE
+  description: Unique identifier for the newtab visit
+- name: widget_name
+  type: STRING
+  mode: NULLABLE
+  description: Name of the widget that emits the event (e.g., lists, focus_timer, weather).
+- name: widget_size
+  type: STRING
+  mode: NULLABLE
+  description: Size of widget (e.g., mini, small, medium).
+- name: impression_count
+  type: INTEGER
+  mode: NULLABLE
+  description: Count of widgets_impression events for this widget and size within the visit
+- name: user_event_count
+  type: INTEGER
+  mode: NULLABLE
+  description: Count of widgets_user_event events for this widget and size within the visit
+- name: change_size_or_learn_more_count
+  type: INTEGER
+  mode: NULLABLE
+  description: Count of widgets_user_event events whose user_action is 'change_size' or 'learn_more'
+- name: enabled_count
+  type: INTEGER
+  mode: NULLABLE
+  description: Count of widgets_enabled events where the widget was enabled (enabled extra = TRUE)
+- name: disabled_count
+  type: INTEGER
+  mode: NULLABLE
+  description: Count of widgets_enabled events where the widget was disabled (enabled extra = FALSE)
+- name: submission_timestamp
+  type: TIMESTAMP
+  mode: NULLABLE
+  description: Earliest submission_timestamp of the newtab pings contributing to this visit
+- name: app_version
+  type: INTEGER
+  mode: NULLABLE
+  description: Firefox Desktop major version
+- name: os
+  type: STRING
+  mode: NULLABLE
+  description: Operating system of the host system
+- name: os_version
+  type: STRING
+  mode: NULLABLE
+  description: Normalized OS version
+- name: windows_build_number
+  type: INTEGER
+  mode: NULLABLE
+  description: Windows OS build number, used to distinguish Windows 10 (<22000) from Windows 11 (>=22000)
+- name: channel
+  type: STRING
+  mode: NULLABLE
+  description: Firefox Desktop deployment channel (e.g. release, beta)
+- name: locale
+  type: STRING
+  mode: NULLABLE
+  description: language or country based preferences of the host system
+- name: country
+  type: STRING
+  mode: NULLABLE
+  description: Country where the newtab event is reported
+- name: sample_id
+  type: INTEGER
+  mode: NULLABLE
+  description: Hashed version of client_id (0-99). Useful for distribution analysis
+- name: legacy_telemetry_client_id
+  type: STRING
+  mode: NULLABLE
+  description: Legacy telemetry client_id. Enables joining to legacy telemetry main-ping data
+- name: profile_group_id
+  type: STRING
+  mode: NULLABLE
+  description: Legacy telemetry. Enables the calculation of metrics by profile_id
+- name: geo_subdivision
+  type: STRING
+  mode: NULLABLE
+  description: First major country subdivision, typically a state, province, or county
+- name: homepage_category
+  type: STRING
+  mode: NULLABLE
+  description: User's chosen homepage/new window appearance
+- name: newtab_category
+  type: STRING
+  mode: NULLABLE
+  description: User's chosen newtab appearance
+- name: organic_content_enabled
+  type: BOOLEAN
+  mode: NULLABLE
+  description: TRUE if the user has enabled stories in the browser.
+- name: sponsored_content_enabled
+  type: BOOLEAN
+  mode: NULLABLE
+  description: TRUE if the user has enabled sponsored stories in the browser.
+- name: sponsored_topsites_enabled
+  type: BOOLEAN
+  mode: NULLABLE
+  description: TRUE if the user has enabled sponsored topsites in the browser.
+- name: organic_topsites_enabled
+  type: BOOLEAN
+  mode: NULLABLE
+  description: TRUE if the user has enabled topsites in the browser.
+- name: newtab_search_enabled
+  type: BOOLEAN
+  mode: NULLABLE
+  description: TRUE if the user has enabled search in the browser.
+- name: newtab_weather_enabled
+  type: BOOLEAN
+  mode: NULLABLE
+  description: Whether the weather widget is enabled
+- name: default_search_engine
+  type: STRING
+  mode: NULLABLE
+  description: Engine_id of the default search engine
+- name: default_private_search_engine
+  type: STRING
+  mode: NULLABLE
+  description: Engine_id of the default private search engine
+- name: topsite_rows
+  type: INTEGER
+  mode: NULLABLE
+  description: The number of topsite tile rows configured to be shown on the newtab page
+- name: topsite_sponsored_tiles_configured
+  type: INTEGER
+  mode: NULLABLE
+  description: The number of topsite tiles configured to be shown on newtab
+- name: newtab_blocked_sponsors
+  type: STRING
+  mode: REPEATED
+  description: Array of advertiser names that have been dismissed by the user
+- name: newtab_content_surface_id
+  type: STRING
+  mode: NULLABLE
+  description: Scheduled surface identifier based on locale and region, with fallback to computed value from country and locale
+- name: experiments
+  type: RECORD
+  mode: REPEATED
+  description: Array of experiments to associate with each visit
+  fields:
+  - name: key
+    type: STRING
+    mode: NULLABLE
+  - name: value
+    type: RECORD
+    mode: NULLABLE
+    fields:
+    - name: branch
+      type: STRING
+      mode: NULLABLE
+    - name: extra
+      type: RECORD
+      mode: NULLABLE
+      fields:
+      - name: type
+        type: STRING
+        mode: NULLABLE
+      - name: enrollment_id
+        type: STRING
+        mode: NULLABLE
+- name: is_default_ui
+  type: BOOLEAN
+  mode: NULLABLE
+  description: TRUE if the newtab open was a default ui
+- name: user_action_counts
+  type: RECORD
+  mode: REPEATED
+  description: Per-user_action counts of widgets_user_event events for this widget within the visit. Will be null in the abscence of interactions
+  fields:
+  - name: user_action
+    type: STRING
+    mode: NULLABLE
+    description: Specific action taken by the widget (e.g., list_copy, list_create, timer_set, timer_play, timer_pause, change_location, detect_location).
+  - name: count
+    type: INTEGER
+    mode: NULLABLE
+    description: Number of widgets_user_event events with this user_action

Link to full diff

@jaredsnyder jaredsnyder added this pull request to the merge queue May 15, 2026
Merged via the queue into main with commit 0963239 May 15, 2026
36 of 37 checks passed
@jaredsnyder jaredsnyder deleted the widget_visits_table branch May 15, 2026 16:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants