Skip to content

Commit 0107a41

Browse files
Merge pull request #3121 from annietllnd/fix
Extract LP metadata for analytics tracking
2 parents 980696e + 99e6663 commit 0107a41

6 files changed

Lines changed: 332 additions & 1 deletion

File tree

content/stats/content-inventory.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
---
2+
title: "Content Inventory Emitter"
3+
layout: "content_inventory"
4+
---

themes/arm-design-system-hugo-theme/layouts/partials/general-formatting/path-ads-card.html

Lines changed: 71 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,77 @@
1010
- _default/term.html
1111
*/}}
1212

13-
<ads-card link="{{.learning_path.Permalink}}" class='path-card{{ if .is_pinned }} featured-path-card{{ end }}'>
13+
{{- $lpAuthors := .learning_path.Params.author -}}
14+
{{- if not (reflect.IsSlice $lpAuthors) -}}
15+
{{- if $lpAuthors -}}
16+
{{- $lpAuthors = slice $lpAuthors -}}
17+
{{- else -}}
18+
{{- $lpAuthors = slice -}}
19+
{{- end -}}
20+
{{- end -}}
21+
22+
{{- $lpSubjects := .learning_path.Params.subjects -}}
23+
{{- if not (reflect.IsSlice $lpSubjects) -}}
24+
{{- if $lpSubjects -}}
25+
{{- $lpSubjects = slice $lpSubjects -}}
26+
{{- else -}}
27+
{{- $lpSubjects = slice -}}
28+
{{- end -}}
29+
{{- end -}}
30+
31+
{{- $lpArmIps := .learning_path.Params.armips -}}
32+
{{- if not (reflect.IsSlice $lpArmIps) -}}
33+
{{- if $lpArmIps -}}
34+
{{- $lpArmIps = slice $lpArmIps -}}
35+
{{- else -}}
36+
{{- $lpArmIps = slice -}}
37+
{{- end -}}
38+
{{- end -}}
39+
40+
{{- $lpOs := .learning_path.Params.operatingsystems -}}
41+
{{- if not (reflect.IsSlice $lpOs) -}}
42+
{{- if $lpOs -}}
43+
{{- $lpOs = slice $lpOs -}}
44+
{{- else -}}
45+
{{- $lpOs = slice -}}
46+
{{- end -}}
47+
{{- end -}}
48+
49+
{{- $lpTools := .learning_path.Params.tools_software_languages -}}
50+
{{- if not (reflect.IsSlice $lpTools) -}}
51+
{{- if $lpTools -}}
52+
{{- $lpTools = slice $lpTools -}}
53+
{{- else -}}
54+
{{- $lpTools = slice -}}
55+
{{- end -}}
56+
{{- end -}}
57+
58+
{{- $lpSteps := slice -}}
59+
{{- range .learning_path.Site.Pages.ByWeight -}}
60+
{{- if and .File (eq .File.Dir $.learning_path.File.Dir) (ne .Params.hide_from_navpane true) -}}
61+
{{- $lpSteps = $lpSteps | append (dict
62+
"full-url" (.RelPermalink | absURL)
63+
"weight" .Weight
64+
"step-title" .Title
65+
) -}}
66+
{{- end -}}
67+
{{- end -}}
68+
69+
<ads-card
70+
link="{{.learning_path.Permalink}}"
71+
class='path-card{{ if .is_pinned }} featured-path-card{{ end }}'
72+
data-lp-url="{{ .learning_path.RelPermalink | absURL }}"
73+
data-lp-title="{{ .learning_path.Title }}"
74+
data-lp-steps="{{ $lpSteps | jsonify }}"
75+
data-lp-author="{{ delimit $lpAuthors ", " }}"
76+
data-lp-skill-level="{{ .learning_path.Params.skillLevels }}"
77+
data-lp-subjects="{{ delimit $lpSubjects ", " }}"
78+
data-lp-arm-ip="{{ delimit $lpArmIps ", " }}"
79+
data-lp-os="{{ delimit $lpOs ", " }}"
80+
data-lp-tools-software-languages="{{ delimit $lpTools ", " }}"
81+
data-lp-content-category="{{ .learning_path.Parent.Title }}"
82+
data-lp-minutes-to-complete="{{ .learning_path.Params.minutes_to_complete }}"
83+
data-lp-last-updated-date="{{ .calculated_date.Format "02-Jan-06" }}">
1484
<ads-card-content slot="content">
1585
{{ if .is_pinned }}
1686
<div class="pinned-header-row">

themes/arm-design-system-hugo-theme/layouts/robots.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
User-agent: *
22
Disallow:
3+
Disallow: /stats/content-inventory/
34

45
# Explicitly welcome AI crawlers
56
User-agent: GPTBot
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
{{ define "main" }}
2+
{{/*
3+
Dedicated Adobe Analytics inventory emitter.
4+
This page is intended for scheduled automation, not regular user traffic.
5+
*/}}
6+
7+
{{/* Build LP step-level rows */}}
8+
{{ $lpRows := slice }}
9+
{{ range where .Site.Pages "Section" "learning-paths" }}
10+
{{ $lpPage := . }}
11+
{{ if and $lpPage.File (eq $lpPage.File.TranslationBaseName "_index") }}
12+
{{ $authors := $lpPage.Params.author }}
13+
{{ if not (reflect.IsSlice $authors) }}
14+
{{ if $authors }}
15+
{{ $authors = slice $authors }}
16+
{{ else }}
17+
{{ $authors = slice }}
18+
{{ end }}
19+
{{ end }}
20+
21+
{{ $subjects := $lpPage.Params.subjects }}
22+
{{ if not (reflect.IsSlice $subjects) }}
23+
{{ if $subjects }}
24+
{{ $subjects = slice $subjects }}
25+
{{ else }}
26+
{{ $subjects = slice }}
27+
{{ end }}
28+
{{ end }}
29+
30+
{{ $armIps := $lpPage.Params.armips }}
31+
{{ if not (reflect.IsSlice $armIps) }}
32+
{{ if $armIps }}
33+
{{ $armIps = slice $armIps }}
34+
{{ else }}
35+
{{ $armIps = slice }}
36+
{{ end }}
37+
{{ end }}
38+
39+
{{ $operatingSystems := $lpPage.Params.operatingsystems }}
40+
{{ if not (reflect.IsSlice $operatingSystems) }}
41+
{{ if $operatingSystems }}
42+
{{ $operatingSystems = slice $operatingSystems }}
43+
{{ else }}
44+
{{ $operatingSystems = slice }}
45+
{{ end }}
46+
{{ end }}
47+
48+
{{ $tools := $lpPage.Params.tools_software_languages }}
49+
{{ if not (reflect.IsSlice $tools) }}
50+
{{ if $tools }}
51+
{{ $tools = slice $tools }}
52+
{{ else }}
53+
{{ $tools = slice }}
54+
{{ end }}
55+
{{ end }}
56+
57+
{{ $lastUpdated := partial "server-side-processing/calculate-last-update.html" $lpPage }}
58+
{{ $category := $lpPage.Parent.Title }}
59+
60+
{{ range $lpPage.Site.Pages.ByWeight }}
61+
{{ $step := . }}
62+
{{ if and $step.File (eq $step.File.Dir $lpPage.File.Dir) (ne $step.Params.hide_from_navpane true) }}
63+
{{ $lpRows = $lpRows | append (dict
64+
"full-url" ($step.RelPermalink | absURL)
65+
"learning-path-title" $lpPage.Title
66+
"weight" $step.Weight
67+
"step-title" $step.Title
68+
"author" (delimit $authors ", ")
69+
"skill-level" $lpPage.Params.skillLevels
70+
"subjects" (delimit $subjects ", ")
71+
"arm-ip" (delimit $armIps ", ")
72+
"os" (delimit $operatingSystems ", ")
73+
"tools-software-languages" (delimit $tools ", ")
74+
"content-category" $category
75+
"minutes-to-complete" $lpPage.Params.minutes_to_complete
76+
"last-updated-date" ($lastUpdated.Format "2-Jan-06")
77+
) }}
78+
{{ end }}
79+
{{ end }}
80+
{{ end }}
81+
{{ end }}
82+
83+
{{/* Build Install Guide rows */}}
84+
{{ $installGuideRows := slice }}
85+
{{ range where .Site.Pages "Section" "install-guides" }}
86+
{{ $igStep := . }}
87+
{{ if and $igStep.File (ne $igStep.RelPermalink "/install-guides/") }}
88+
{{ $igRoot := $igStep }}
89+
{{ if and $igStep.Parent $igStep.Parent.File (eq $igStep.Parent.File.TranslationBaseName "_index") }}
90+
{{ $igRoot = $igStep.Parent }}
91+
{{ end }}
92+
93+
{{ $igAuthors := $igRoot.Params.author }}
94+
{{ if not (reflect.IsSlice $igAuthors) }}
95+
{{ if $igAuthors }}
96+
{{ $igAuthors = slice $igAuthors }}
97+
{{ else }}
98+
{{ $igAuthors = slice }}
99+
{{ end }}
100+
{{ end }}
101+
102+
{{ $igOperatingSystems := $igRoot.Params.operatingsystems }}
103+
{{ if not (reflect.IsSlice $igOperatingSystems) }}
104+
{{ if $igOperatingSystems }}
105+
{{ $igOperatingSystems = slice $igOperatingSystems }}
106+
{{ else }}
107+
{{ $igOperatingSystems = slice }}
108+
{{ end }}
109+
{{ end }}
110+
111+
{{ $igTools := $igRoot.Params.tools_software_languages }}
112+
{{ if not (reflect.IsSlice $igTools) }}
113+
{{ if $igTools }}
114+
{{ $igTools = slice $igTools }}
115+
{{ else }}
116+
{{ $igTools = slice }}
117+
{{ end }}
118+
{{ end }}
119+
120+
{{ $installGuideRows = $installGuideRows | append (dict
121+
"full-url" ($igStep.RelPermalink | absURL)
122+
"install-guide-title" $igRoot.Title
123+
"weight" $igStep.Weight
124+
"step-title" $igStep.Title
125+
"author" (delimit $igAuthors ", ")
126+
"os" (delimit $igOperatingSystems ", ")
127+
"tools-software-languages" (delimit $igTools ", ")
128+
"content-category" "Install Guides"
129+
"minutes-to-complete" $igRoot.Params.minutes_to_complete
130+
"last-updated-date" ($igStep.Lastmod.UTC.Format "2-Jan-06")
131+
) }}
132+
{{ end }}
133+
{{ end }}
134+
135+
<main id="main">
136+
<div class="content-box">
137+
<h2>Content Inventory Emitter</h2>
138+
<p>This page supports anonymous analytics reporting so our teams can prioritize the content developers care about most.</p>
139+
<p>It emits Adobe Analytics inventory events only when query param <code>aa_inventory=1</code> is present.</p>
140+
<p>Example: <code>/stats/content-inventory/?aa_inventory=1</code></p>
141+
<p>Learning Path rows: {{ len $lpRows }} | Install Guide rows: {{ len $installGuideRows }}</p>
142+
</div>
143+
</main>
144+
145+
<script id="inventory-lp-rows" type="application/json">{{ $lpRows | jsonify }}</script>
146+
<script id="inventory-install-guide-rows" type="application/json">{{ $installGuideRows | jsonify }}</script>
147+
148+
<script>
149+
(function () {
150+
var params = new URLSearchParams(window.location.search);
151+
if (params.get("aa_inventory") !== "1") {
152+
return;
153+
}
154+
155+
if (!window._satellite || typeof window._satellite.track !== "function") {
156+
console.warn("Adobe _satellite is unavailable. Inventory events were not sent.");
157+
return;
158+
}
159+
160+
function parseRows(id) {
161+
var el = document.getElementById(id);
162+
if (!el || !el.textContent) return [];
163+
try {
164+
var parsed = JSON.parse(el.textContent);
165+
if (typeof parsed === "string") return JSON.parse(parsed);
166+
return Array.isArray(parsed) ? parsed : [];
167+
} catch (error) {
168+
console.warn("Failed to parse inventory rows for", id, error);
169+
return [];
170+
}
171+
}
172+
173+
var lpRows = parseRows("inventory-lp-rows");
174+
var installGuideRows = parseRows("inventory-install-guide-rows");
175+
176+
_satellite.track("content-inventory-summary", {
177+
"inventory-type": "all",
178+
"inventory-run-date": new Date().toISOString(),
179+
"learning-path-row-count": lpRows.length,
180+
"install-guide-row-count": installGuideRows.length,
181+
"total-row-count": lpRows.length + installGuideRows.length
182+
});
183+
184+
for (var i = 0; i < lpRows.length; i++) {
185+
_satellite.track("learning-path-row-metadata", lpRows[i]);
186+
}
187+
188+
for (var j = 0; j < installGuideRows.length; j++) {
189+
_satellite.track("install-guide-row-metadata", installGuideRows[j]);
190+
}
191+
})();
192+
</script>
193+
{{ end }}

themes/arm-design-system-hugo-theme/static/js/anonymous-analytics.js

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,66 @@ function trackHeaderInteraction(type,name){
116116
});
117117
}
118118

119+
function trackLearningPathListMetadata() {
120+
let learning_path_cards = document.querySelectorAll('.path-card:not(.global-nav-path-card)');
121+
if (!learning_path_cards || learning_path_cards.length === 0) {
122+
return;
123+
}
124+
125+
let total_step_rows = 0;
126+
for (let card of learning_path_cards) {
127+
let steps_payload = card.getAttribute('data-lp-steps') || '[]';
128+
try {
129+
let steps = JSON.parse(steps_payload);
130+
total_step_rows += Array.isArray(steps) ? steps.length : 0;
131+
} catch (error) {
132+
// Ignore malformed payload; fallback row is emitted below.
133+
}
134+
}
135+
136+
_satellite.track('learning-path-list-metadata', {
137+
'lp-list-category': window.location.pathname.split('/')[2] || '',
138+
'lp-list-count': learning_path_cards.length,
139+
'lp-step-row-count': total_step_rows
140+
});
141+
142+
for (let card of learning_path_cards) {
143+
let steps = [];
144+
try {
145+
steps = JSON.parse(card.getAttribute('data-lp-steps') || '[]');
146+
} catch (error) {
147+
steps = [];
148+
}
149+
150+
// Fallback for safety if step extraction fails.
151+
if (!Array.isArray(steps) || steps.length === 0) {
152+
steps = [{
153+
'full-url': card.getAttribute('data-lp-url') || '',
154+
'weight': '',
155+
'step-title': card.getAttribute('data-lp-title') || ''
156+
}];
157+
}
158+
159+
for (let step of steps) {
160+
_satellite.track('learning-path-row-metadata', {
161+
'full-url': step['full-url'] || card.getAttribute('data-lp-url') || '',
162+
'learning-path-title': card.getAttribute('data-lp-title') || '',
163+
'weight': step['weight'] || '',
164+
'step-title': step['step-title'] || '',
165+
'author': card.getAttribute('data-lp-author') || '',
166+
'skill-level': card.getAttribute('data-lp-skill-level') || '',
167+
'subjects': card.getAttribute('data-lp-subjects') || '',
168+
'arm-ip': card.getAttribute('data-lp-arm-ip') || '',
169+
'os': card.getAttribute('data-lp-os') || '',
170+
'tools-software-languages': card.getAttribute('data-lp-tools-software-languages') || '',
171+
'content-category': card.getAttribute('data-lp-content-category') || '',
172+
'minutes-to-complete': card.getAttribute('data-lp-minutes-to-complete') || '',
173+
'last-updated-date': card.getAttribute('data-lp-last-updated-date') || ''
174+
});
175+
}
176+
}
177+
}
178+
119179

120180
function doneTyping(search_str) {
121181
//console.log('send',search_str);

tools/maintenance.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -153,6 +153,9 @@ def main():
153153
elif "/migration" in os.path.abspath(args.instructions):
154154
logging.info("Migration paths are not supported for maintenance tests yet.")
155155
exit(0)
156+
elif "/stats" in os.path.abspath(args.instructions):
157+
logging.info("Stats paths are not supported for maintenance tests.")
158+
exit(0)
156159
else:
157160
logging.error("-i/--instructions expects a .md file, a CSV with a list of files or a Learning Path directory")
158161
if results_dict is not None:

0 commit comments

Comments
 (0)