📊 Guided Analytics Pipeline¶
Built: 25 April 2026 Status: ✅ Live — dual-track (GA4 + Cloudflare Analytics Engine) Dashboard: aguidetocloud.com/site-analytics/ → Guided tab
What It Is¶
A complete analytics pipeline for the Guided certification prep platform. Tracks user engagement, quiz behavior, learning patterns, and platform health across two independent data streams — GA4 for historical analysis and Cloudflare Analytics Engine for real-time metrics.
The dashboard is embedded in the existing Site Analytics page as the "Guided" tab.
Architecture: Dual-Track Analytics¶
User Action (quiz, page view, bookmark)
│
├──→ window.gtag() ──→ GA4 (G-2HWWZGWCD0)
│ ↓
│ GA4 Data API (24-48h delay)
│ ↓
│ /api/stats?guided=1 ──→ Dashboard
│
└──→ sendBeacon() ──→ /api/guided-event
↓
CF Analytics Engine (instant)
↓
(future: real-time dashboard)
Why dual-track?
- GA4 is free and powerful for historical trends, but has 24-48h data freshness delay
- Analytics Engine is instant (edge-native, zero delay) but has 90-day retention
- If either fails, the other keeps collecting — no data gaps
What's Tracked¶
11 Custom Events¶
| Event | Trigger | Key Parameters |
|---|---|---|
guided_home_view |
Homepage loads | — |
guided_explore_view |
Explore catalogue loads | total_certs |
guided_cert_view |
Cert landing page loads | cert_code, vendor, questions, domains |
guided_practice_view |
Practice page loads | cert_code, vendor, question_count, domain_count |
guided_quiz_start |
User starts a quiz | cert_code, mode, question_count, is_free |
guided_quiz_complete |
Quiz finishes → results screen | cert_code, mode, score_pct, time_seconds, is_free, question_count |
guided_flashcard_start |
Flashcard session begins | cert_code, card_count, due_cards |
guided_bookmark |
Bookmark toggled | cert_code, action (add/remove) |
guided_streak_update |
Daily streak updated | streak_days, total_xp, badges_count, total_questions |
guided_purchase_complete |
User returns from Stripe checkout | cert_code, product_type |
guided_activation_success |
Licence key activated | cert_code |
5 GA4 Custom Dimensions (Registered)¶
| Parameter | Display Name | Scope | Purpose |
|---|---|---|---|
cert_code |
Cert Code | Event | Per-cert quiz breakdowns |
mode |
Quiz Mode | Event | Study vs exam vs flashcard analysis |
score_pct |
Score Percentage | Event | Average score calculations |
is_free |
Is Free User | Event | Free vs paid user segmentation |
question_count |
Question Count | Event | Quiz size analysis |
Registered via GA4 Admin API using service account analytics-dashboard@aguidetocloud-mcp.iam.gserviceaccount.com (needs Editor role to register, downgrade to Viewer after).
Dashboard: The Guided Tab¶
Located in /site-analytics/ → click "Guided" tab. Lazy-loads on first click (no wasted API calls).
Sections¶
| Section | What It Shows |
|---|---|
| Platform Pulse | 6 hero KPIs: Learners (30d), Quiz Starts, Completed, Completion Rate, Page Views, Live Now |
| Daily Engagement | Chart.js line chart — views + users over 30 days |
| Quiz Mode Distribution | Doughnut chart — study/exam/flashcard/domain/weak breakdown |
| Cert Performance | Table — cert code, views, users, quiz completions, bar chart |
| Event Breakdown | All tracked actions with counts, sorted by frequency |
| Top Guided Pages | Most viewed pages within the platform |
Data Flow¶
Tab click → fetch('/api/stats?guided=1')
↓
Cloudflare Pages Function (functions/api/stats.js)
↓
7 parallel GA4 Data API queries:
1. Page views on /guided/* paths
2. Custom event counts (all guided_* events)
3. Daily trend of guided page views
4. Per-cert practice page views
5. Quiz mode breakdown (customEvent:mode)
6. Realtime guided users
7. Per-cert quiz completions (customEvent:cert_code)
↓
JSON response → inline JS renders dashboard
Cached 5 minutes server-side (same as main stats).
Files¶
Guided Platform (Astro)¶
| File | Purpose |
|---|---|
src/layouts/BaseLayout.astro |
GA4 + Clarity injection (prod-only), View Transition page_view tracking |
src/lib/analytics.ts |
trackEvent() — dual-track helper (GA4 gtag + AE sendBeacon) |
src/components/analytics/PageTracker.astro |
Reusable Astro component for page-level events (define:vars) |
src/components/interactive/PracticeQuiz.tsx |
7 trackEvent calls at key trigger points |
src/pages/index.astro |
guided_home_view via PageTracker |
src/pages/explore/index.astro |
guided_explore_view via PageTracker |
src/pages/[slug]/index.astro |
guided_cert_view via PageTracker |
src/pages/[cert]/practice.astro |
guided_practice_view via PageTracker |
functions/guided/api/guided-event.ts |
Analytics Engine ingestion endpoint |
functions/env.d.ts |
GUIDED_ANALYTICS: AnalyticsEngineDataset type |
wrangler.toml |
[[analytics_engine_datasets]] binding |
Main Site (Hugo)¶
| File | Purpose |
|---|---|
functions/api/stats.js |
handleGuided() — 7 parallel GA4 queries, ?guided=1 endpoint |
layouts/site-analytics/list.html |
Guided tab HTML + inline JS rendering |
Analytics Engine Schema¶
Each event written to the guided_events dataset:
| Field | Slot | Content |
|---|---|---|
| Index | indexes[0] |
Event name (e.g., quiz_complete) |
| Blob 1 | blobs[0] |
cert_code |
| Blob 2 | blobs[1] |
mode |
| Blob 3 | blobs[2] |
vendor |
| Blob 4 | blobs[3] |
Country (from cf-ipcountry header) |
| Blob 5 | blobs[4] |
action (for bookmarks) |
| Double 1 | doubles[0] |
score_pct (-1 if N/A) |
| Double 2 | doubles[1] |
time_seconds |
| Double 3 | doubles[2] |
question_count |
| Double 4 | doubles[3] |
is_free (1 or 0) |
Endpoint: POST /api/guided-event (on the Guided Pages deployment).
Validation: Only allowed event names accepted. Invalid JSON or unknown events → 400.
Privacy & Compliance¶
- ❌ No PII in any event (no email, name, user ID)
- ✅ GA4 uses
anonymize_ip: true - ✅ Analytics Engine uses anonymous data points (no cookies)
- ✅ sendBeacon is fire-and-forget — doesn't block user interactions
- ✅ All dashboard data is aggregated — never shows individual user data
- ✅ Purchase/revenue data is NOT shown on the public dashboard
- ⚠️
marketingConsentflag in KV — respect before any re-engagement features
Future Roadmap¶
Phase 2: Real-time Dashboard (Analytics Engine)¶
- Query AE SQL API for instant metrics (no 48h GA4 delay)
- Add "last 5 minutes" activity pulse
- Real-time quiz score distribution
Phase 3: Advanced Metrics (needs 2-3 weeks of data)¶
| Metric | Description | Data Source |
|---|---|---|
| Time-to-Competency | How many attempts until >80% consistently | GA4 + user pseudo-ID sequences |
| Free-to-Paid Inflection | At what score/attempt do free users convert? | GA4 custom dimensions |
| Retention Cohorts | Weekly cohort return rates | AE or D1 |
| Domain Weakness Heatmap | Cross-cert × domain score matrix | GA4 domainScores |
| Study Pattern Clustering | Crammer vs steady vs flashcard-heavy | AE event sequences |
| Content Effectiveness | Do study guide readers score higher? | GA4 correlation |
| Question Difficulty Index | % correct × discrimination index | GA4 per-question data |
| Churn Risk Score | Broken streak + days since last visit | AE + localStorage |
GA4 Setup Checklist¶
- [x] GA4 property:
530486519(A Guide to Cloud & AI) - [x] Measurement ID:
G-2HWWZGWCD0 - [x] Service account:
analytics-dashboard@aguidetocloud-mcp.iam.gserviceaccount.com - [x] Admin API enabled (project
570500576423) - [x] 5 custom dimensions registered (cert_code, mode, score_pct, is_free, question_count)
- [ ] Downgrade service account back to Viewer role (Editor was only needed for dimension registration)
Debugging¶
Dashboard shows "Loading" on Guided tab:
1. Check API: curl https://www.aguidetocloud.com/api/stats?guided=1
2. If API returns data → browser cache issue → Ctrl+Shift+R
3. If API errors → check functions/api/stats.js for syntax issues
No events appearing in GA4:
1. Events take 24-48h to appear in GA4 Data API
2. Check real-time view in GA4 Admin to confirm events are flowing
3. Verify import.meta.env.PROD is true (events only fire in production)
Analytics Engine not receiving events:
1. Check GUIDED_ANALYTICS binding in Cloudflare Pages settings
2. Test: curl -X POST https://www.aguidetocloud.com/guided/api/guided-event -H 'Content-Type: application/json' -d '{"event":"home_view"}'
3. Should return {"ok":true} — if {"ok":true,"skipped":true}, the binding isn't active
Related¶
- Site Analytics Dashboard — the parent dashboard this tab lives in
- Guided Platform Overview — platform architecture
- Stripe Payments — purchase flow that generates purchase events
- Pricing Strategy — free tier and conversion context