From 680666602853e06c232c104fdda2fdbf613815ba Mon Sep 17 00:00:00 2001 From: Annie Tallund Date: Wed, 1 Apr 2026 15:04:17 -0700 Subject: [PATCH 1/4] Extract LP metadata for analytics tracking - Matches previous CSV approach - Uses JS implementation instead of having to manually update CSV --- .../general-formatting/path-ads-card.html | 72 ++++++++++++++++++- .../static/js/anonymous-analytics.js | 63 ++++++++++++++++ 2 files changed, 134 insertions(+), 1 deletion(-) diff --git a/themes/arm-design-system-hugo-theme/layouts/partials/general-formatting/path-ads-card.html b/themes/arm-design-system-hugo-theme/layouts/partials/general-formatting/path-ads-card.html index 4dd76bdc8f..5bc81c2e1e 100644 --- a/themes/arm-design-system-hugo-theme/layouts/partials/general-formatting/path-ads-card.html +++ b/themes/arm-design-system-hugo-theme/layouts/partials/general-formatting/path-ads-card.html @@ -10,7 +10,77 @@ - _default/term.html */}} - +{{- $lpAuthors := .learning_path.Params.author -}} +{{- if not (reflect.IsSlice $lpAuthors) -}} + {{- if $lpAuthors -}} + {{- $lpAuthors = slice $lpAuthors -}} + {{- else -}} + {{- $lpAuthors = slice -}} + {{- end -}} +{{- end -}} + +{{- $lpSubjects := .learning_path.Params.subjects -}} +{{- if not (reflect.IsSlice $lpSubjects) -}} + {{- if $lpSubjects -}} + {{- $lpSubjects = slice $lpSubjects -}} + {{- else -}} + {{- $lpSubjects = slice -}} + {{- end -}} +{{- end -}} + +{{- $lpArmIps := .learning_path.Params.armips -}} +{{- if not (reflect.IsSlice $lpArmIps) -}} + {{- if $lpArmIps -}} + {{- $lpArmIps = slice $lpArmIps -}} + {{- else -}} + {{- $lpArmIps = slice -}} + {{- end -}} +{{- end -}} + +{{- $lpOs := .learning_path.Params.operatingsystems -}} +{{- if not (reflect.IsSlice $lpOs) -}} + {{- if $lpOs -}} + {{- $lpOs = slice $lpOs -}} + {{- else -}} + {{- $lpOs = slice -}} + {{- end -}} +{{- end -}} + +{{- $lpTools := .learning_path.Params.tools_software_languages -}} +{{- if not (reflect.IsSlice $lpTools) -}} + {{- if $lpTools -}} + {{- $lpTools = slice $lpTools -}} + {{- else -}} + {{- $lpTools = slice -}} + {{- end -}} +{{- end -}} + +{{- $lpSteps := slice -}} +{{- range .learning_path.Site.Pages.ByWeight -}} + {{- if and .File (eq .File.Dir $.learning_path.File.Dir) (ne .Params.hide_from_navpane true) -}} + {{- $lpSteps = $lpSteps | append (dict + "full-url" (.RelPermalink | absURL) + "weight" .Weight + "step-title" .Title + ) -}} + {{- end -}} +{{- end -}} + + {{ if .is_pinned }}
diff --git a/themes/arm-design-system-hugo-theme/static/js/anonymous-analytics.js b/themes/arm-design-system-hugo-theme/static/js/anonymous-analytics.js index 5cc1fd65b8..2de82a2687 100644 --- a/themes/arm-design-system-hugo-theme/static/js/anonymous-analytics.js +++ b/themes/arm-design-system-hugo-theme/static/js/anonymous-analytics.js @@ -116,6 +116,66 @@ function trackHeaderInteraction(type,name){ }); } +function trackLearningPathListMetadata() { + let learning_path_cards = document.querySelectorAll('.path-card:not(.global-nav-path-card)'); + if (!learning_path_cards || learning_path_cards.length === 0) { + return; + } + + let total_step_rows = 0; + for (let card of learning_path_cards) { + let steps_payload = card.getAttribute('data-lp-steps') || '[]'; + try { + let steps = JSON.parse(steps_payload); + total_step_rows += Array.isArray(steps) ? steps.length : 0; + } catch (error) { + // Ignore malformed payload; fallback row is emitted below. + } + } + + _satellite.track('learning-path-list-metadata', { + 'lp-list-category': window.location.pathname.split('/')[2] || '', + 'lp-list-count': learning_path_cards.length, + 'lp-step-row-count': total_step_rows + }); + + for (let card of learning_path_cards) { + let steps = []; + try { + steps = JSON.parse(card.getAttribute('data-lp-steps') || '[]'); + } catch (error) { + steps = []; + } + + // Fallback for safety if step extraction fails. + if (!Array.isArray(steps) || steps.length === 0) { + steps = [{ + 'full-url': card.getAttribute('data-lp-url') || '', + 'weight': '', + 'step-title': card.getAttribute('data-lp-title') || '' + }]; + } + + for (let step of steps) { + _satellite.track('learning-path-row-metadata', { + 'full-url': step['full-url'] || card.getAttribute('data-lp-url') || '', + 'learning-path-title': card.getAttribute('data-lp-title') || '', + 'weight': step['weight'] || '', + 'step-title': step['step-title'] || '', + 'author': card.getAttribute('data-lp-author') || '', + 'skill-level': card.getAttribute('data-lp-skill-level') || '', + 'subjects': card.getAttribute('data-lp-subjects') || '', + 'arm-ip': card.getAttribute('data-lp-arm-ip') || '', + 'os': card.getAttribute('data-lp-os') || '', + 'tools-software-languages': card.getAttribute('data-lp-tools-software-languages') || '', + 'content-category': card.getAttribute('data-lp-content-category') || '', + 'minutes-to-complete': card.getAttribute('data-lp-minutes-to-complete') || '', + 'last-updated-date': card.getAttribute('data-lp-last-updated-date') || '' + }); + } + } +} + function doneTyping(search_str) { //console.log('send',search_str); @@ -248,6 +308,9 @@ function attachPageFindSearchTracker() { trackSearchInteraction(); }); + // 1a) Dispatch LP metadata rows for Adobe Analytics ingestion + trackLearningPathListMetadata(); + // 2) Facet boxes let filter_facet_elements = document.querySelectorAll('ads-checkbox'); for (let facet of filter_facet_elements) { From 40d16cf0b91e1c036be9089ebb29568c062c97ae Mon Sep 17 00:00:00 2001 From: Annie Tallund Date: Mon, 6 Apr 2026 09:42:28 -0700 Subject: [PATCH 2/4] Create dedicated content stats emission page --- content/stats/content-inventory.md | 4 + .../layouts/stats/content_inventory.html | 192 ++++++++++++++++++ .../static/js/anonymous-analytics.js | 3 - 3 files changed, 196 insertions(+), 3 deletions(-) create mode 100644 content/stats/content-inventory.md create mode 100644 themes/arm-design-system-hugo-theme/layouts/stats/content_inventory.html diff --git a/content/stats/content-inventory.md b/content/stats/content-inventory.md new file mode 100644 index 0000000000..a4267bf067 --- /dev/null +++ b/content/stats/content-inventory.md @@ -0,0 +1,4 @@ +--- +title: "Content Inventory Emitter" +layout: "content_inventory" +--- diff --git a/themes/arm-design-system-hugo-theme/layouts/stats/content_inventory.html b/themes/arm-design-system-hugo-theme/layouts/stats/content_inventory.html new file mode 100644 index 0000000000..8d5cc60b80 --- /dev/null +++ b/themes/arm-design-system-hugo-theme/layouts/stats/content_inventory.html @@ -0,0 +1,192 @@ +{{ define "main" }} +{{/* +Dedicated Adobe Analytics inventory emitter. +This page is intended for scheduled automation, not regular user traffic. +*/}} + +{{/* Build LP step-level rows */}} +{{ $lpRows := slice }} +{{ range where .Site.Pages "Section" "learning-paths" }} + {{ $lpPage := . }} + {{ if and $lpPage.File (eq $lpPage.File.TranslationBaseName "_index") }} + {{ $authors := $lpPage.Params.author }} + {{ if not (reflect.IsSlice $authors) }} + {{ if $authors }} + {{ $authors = slice $authors }} + {{ else }} + {{ $authors = slice }} + {{ end }} + {{ end }} + + {{ $subjects := $lpPage.Params.subjects }} + {{ if not (reflect.IsSlice $subjects) }} + {{ if $subjects }} + {{ $subjects = slice $subjects }} + {{ else }} + {{ $subjects = slice }} + {{ end }} + {{ end }} + + {{ $armIps := $lpPage.Params.armips }} + {{ if not (reflect.IsSlice $armIps) }} + {{ if $armIps }} + {{ $armIps = slice $armIps }} + {{ else }} + {{ $armIps = slice }} + {{ end }} + {{ end }} + + {{ $operatingSystems := $lpPage.Params.operatingsystems }} + {{ if not (reflect.IsSlice $operatingSystems) }} + {{ if $operatingSystems }} + {{ $operatingSystems = slice $operatingSystems }} + {{ else }} + {{ $operatingSystems = slice }} + {{ end }} + {{ end }} + + {{ $tools := $lpPage.Params.tools_software_languages }} + {{ if not (reflect.IsSlice $tools) }} + {{ if $tools }} + {{ $tools = slice $tools }} + {{ else }} + {{ $tools = slice }} + {{ end }} + {{ end }} + + {{ $lastUpdated := partial "server-side-processing/calculate-last-update.html" $lpPage }} + {{ $category := $lpPage.Parent.Title }} + + {{ range $lpPage.Site.Pages.ByWeight }} + {{ $step := . }} + {{ if and $step.File (eq $step.File.Dir $lpPage.File.Dir) (ne $step.Params.hide_from_navpane true) }} + {{ $lpRows = $lpRows | append (dict + "full-url" ($step.RelPermalink | absURL) + "learning-path-title" $lpPage.Title + "weight" $step.Weight + "step-title" $step.Title + "author" (delimit $authors ", ") + "skill-level" $lpPage.Params.skillLevels + "subjects" (delimit $subjects ", ") + "arm-ip" (delimit $armIps ", ") + "os" (delimit $operatingSystems ", ") + "tools-software-languages" (delimit $tools ", ") + "content-category" $category + "minutes-to-complete" $lpPage.Params.minutes_to_complete + "last-updated-date" ($lastUpdated.Format "2-Jan-06") + ) }} + {{ end }} + {{ end }} + {{ end }} +{{ end }} + +{{/* Build Install Guide rows */}} +{{ $installGuideRows := slice }} +{{ range where .Site.Pages "Section" "install-guides" }} + {{ $igStep := . }} + {{ if and $igStep.File (ne $igStep.RelPermalink "/install-guides/") }} + {{ $igRoot := $igStep }} + {{ if and $igStep.Parent $igStep.Parent.File (eq $igStep.Parent.File.TranslationBaseName "_index") }} + {{ $igRoot = $igStep.Parent }} + {{ end }} + + {{ $igAuthors := $igRoot.Params.author }} + {{ if not (reflect.IsSlice $igAuthors) }} + {{ if $igAuthors }} + {{ $igAuthors = slice $igAuthors }} + {{ else }} + {{ $igAuthors = slice }} + {{ end }} + {{ end }} + + {{ $igOperatingSystems := $igRoot.Params.operatingsystems }} + {{ if not (reflect.IsSlice $igOperatingSystems) }} + {{ if $igOperatingSystems }} + {{ $igOperatingSystems = slice $igOperatingSystems }} + {{ else }} + {{ $igOperatingSystems = slice }} + {{ end }} + {{ end }} + + {{ $igTools := $igRoot.Params.tools_software_languages }} + {{ if not (reflect.IsSlice $igTools) }} + {{ if $igTools }} + {{ $igTools = slice $igTools }} + {{ else }} + {{ $igTools = slice }} + {{ end }} + {{ end }} + + {{ $installGuideRows = $installGuideRows | append (dict + "full-url" ($igStep.RelPermalink | absURL) + "install-guide-title" $igRoot.Title + "weight" $igStep.Weight + "step-title" $igStep.Title + "author" (delimit $igAuthors ", ") + "os" (delimit $igOperatingSystems ", ") + "tools-software-languages" (delimit $igTools ", ") + "content-category" "Install Guides" + "minutes-to-complete" $igRoot.Params.minutes_to_complete + "last-updated-date" ($igStep.Lastmod.UTC.Format "2-Jan-06") + ) }} + {{ end }} +{{ end }} + +
+
+

Content Inventory Emitter

+

This page emits Adobe Analytics inventory events only when query param aa_inventory=1 is present.

+

Example: /stats/content-inventory/?aa_inventory=1

+

Learning Path rows: {{ len $lpRows }} | Install Guide rows: {{ len $installGuideRows }}

+
+
+ + + + + +{{ end }} diff --git a/themes/arm-design-system-hugo-theme/static/js/anonymous-analytics.js b/themes/arm-design-system-hugo-theme/static/js/anonymous-analytics.js index 2de82a2687..43c73c21a5 100644 --- a/themes/arm-design-system-hugo-theme/static/js/anonymous-analytics.js +++ b/themes/arm-design-system-hugo-theme/static/js/anonymous-analytics.js @@ -308,9 +308,6 @@ function attachPageFindSearchTracker() { trackSearchInteraction(); }); - // 1a) Dispatch LP metadata rows for Adobe Analytics ingestion - trackLearningPathListMetadata(); - // 2) Facet boxes let filter_facet_elements = document.querySelectorAll('ads-checkbox'); for (let facet of filter_facet_elements) { From 4e6f84c1617f90f2c6351d0e9aa21776d7df34eb Mon Sep 17 00:00:00 2001 From: Annie Tallund Date: Mon, 6 Apr 2026 09:48:29 -0700 Subject: [PATCH 3/4] Exempt stats from testing --- tools/maintenance.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/tools/maintenance.py b/tools/maintenance.py index b6fd5d0d04..bcde7cd595 100755 --- a/tools/maintenance.py +++ b/tools/maintenance.py @@ -153,6 +153,9 @@ def main(): elif "/migration" in os.path.abspath(args.instructions): logging.info("Migration paths are not supported for maintenance tests yet.") exit(0) + elif "/stats" in os.path.abspath(args.instructions): + logging.info("Stats paths are not supported for maintenance tests.") + exit(0) else: logging.error("-i/--instructions expects a .md file, a CSV with a list of files or a Learning Path directory") if results_dict is not None: From 99e666309f81b522ca1dc616dbf3b1ae5ba14c7a Mon Sep 17 00:00:00 2001 From: Annie Tallund Date: Mon, 6 Apr 2026 13:37:38 -0700 Subject: [PATCH 4/4] Unlist content inventory page and clarify intent --- themes/arm-design-system-hugo-theme/layouts/robots.txt | 1 + .../layouts/stats/content_inventory.html | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/themes/arm-design-system-hugo-theme/layouts/robots.txt b/themes/arm-design-system-hugo-theme/layouts/robots.txt index b2be206d5c..a8a9ca1c00 100644 --- a/themes/arm-design-system-hugo-theme/layouts/robots.txt +++ b/themes/arm-design-system-hugo-theme/layouts/robots.txt @@ -1,5 +1,6 @@ User-agent: * Disallow: +Disallow: /stats/content-inventory/ # Explicitly welcome AI crawlers User-agent: GPTBot diff --git a/themes/arm-design-system-hugo-theme/layouts/stats/content_inventory.html b/themes/arm-design-system-hugo-theme/layouts/stats/content_inventory.html index 8d5cc60b80..7cad14bace 100644 --- a/themes/arm-design-system-hugo-theme/layouts/stats/content_inventory.html +++ b/themes/arm-design-system-hugo-theme/layouts/stats/content_inventory.html @@ -135,7 +135,8 @@

Content Inventory Emitter

-

This page emits Adobe Analytics inventory events only when query param aa_inventory=1 is present.

+

This page supports anonymous analytics reporting so our teams can prioritize the content developers care about most.

+

It emits Adobe Analytics inventory events only when query param aa_inventory=1 is present.

Example: /stats/content-inventory/?aa_inventory=1

Learning Path rows: {{ len $lpRows }} | Install Guide rows: {{ len $installGuideRows }}