analytics
Analytics for Good Measure Giving
Analyze user behavior, track conversions, and understand engagement patterns using three data sources:
- Cloudflare — primary traffic source (zone-level HTTP metrics + RUM beacon for real browser visits)
- Firestore — user signups, feature adoption, giving activity
- GA4 — event-level engagement tracking (blocked by ~80%+ of visitors)
- PMF Analysis — engagement tier segmentation (Superhuman PMF framework)
When This Skill Activates
- Checking site traffic and engagement
- Analyzing user funnels (browse → view → convert)
- Reviewing search behavior and popular charities
- Tracking login/signup conversions
- Understanding time on site and engagement metrics
- Checking user signups, feature adoption, or giving activity
- Investigating Firestore user data or reported issues
- Comparing traffic across data sources
Data Sources
Cloudflare (Primary Traffic Metrics)
Script: scripts/cloudflare_analytics.py
source ~/.secrets/api_keys.sh && uv run python scripts/cloudflare_analytics.py 2>/tmp/cf_analytics.err
Outputs JSON with:
- daily[] — merged zone + RUM daily metrics
- totals.zone — HTTP-level totals (includes bots/crawlers)
- totals.rum — Real browser visits (beacon-based, most accurate)
- top_paths.rum[] — Top pages by visit count
- top_countries.rum[] — Geographic breakdown
- browsers[] — Browser/device split
Key metric relationships:
zone.unique_ips= all unique IPs (bots + humans) — inflatedrum.visits= real human browser visits — most accurate traffic countga4.sessions= human visits without ad blockers — undercounts by ~80%
Environment variables (from ~/.secrets/api_keys.sh):
CLOUDFLARE_API_TOKENCLOUDFLARE_ZONE_ID(goodmeasuregiving.org)CLOUDFLARE_ACCOUNT_ID
Firestore
Script: scripts/firestore_analytics.py
uv run python scripts/firestore_analytics.py 2>/tmp/firestore_analytics.err
See Firestore schema section below.
PMF Analysis
Script: scripts/pmf_analysis.py
uv run python scripts/pmf_analysis.py 2>/tmp/pmf_analysis.err
Outputs formatted text (not JSON) with:
- Tier distribution — Champions (Tier 1), Interested (Tier 2), Passive (Tier 3)
- PMF proxy score — % of 30+ day users in Tier 1
- Tier 1 "Nicole" profile — feature adoption patterns of best users
- Gap analysis — what separates each tier (the activation gap)
- GA4 acquisition funnel — visitor → sign-in → registered conversion rates
- DAU/WAU/MAU stickiness — daily engagement trends
Uses both Firestore (gcloud auth) and GA4 (service account at ~/.secrets/Roshni-3eada2766db6.json).
Pass --detail for per-user breakdown.
GA4
GA4 Property ID: Set via GA4_PROPERTY_ID environment variable or .env file.
Important: GA4 undercounts traffic significantly (~80%+ of visitors have ad blockers). Use GA4 for event-level behavior insights (which charities get clicked, search terms, funnel events), NOT for traffic volume. Use Cloudflare RUM for traffic counts.
Project Context
Custom Events Tracked (GA4):
| Event | Parameters | Purpose |
|---|---|---|
page_view |
page_path, page_title, flow_* | Navigation tracking |
charity_view |
charity_id, charity_name, view_type, flow_* | Detail page opens |
charity_card_click |
charity_id, charity_name, charity_tier, list_position, flow_* | Browse engagement |
search |
search_term, result_count, flow_* | Search behavior |
donate_click |
charity_id, charity_name, destination_url, flow_* | Donation intent |
outbound_click |
charity_id, charity_name, destination_url | External links |
sign_in_start |
method, flow_* | Login funnel start |
sign_in_success |
method, auth_type, flow_* | Login funnel complete (auth_type: 'signup' or 'login') |
hero_cta_click |
cta_name, destination_path | Landing engagement |
Flow Tracking Parameters (on all major events):
| Parameter | Example | Purpose |
|---|---|---|
flow_id |
1706234567890-abc123 |
Unique session identifier |
flow_path |
landing>browse>card_click>charity_view |
User journey sequence |
flow_step |
4 |
Step number in journey |
Key Goals:
- Primary: Login conversions (sign_in_start events)
- Secondary: Time on site / engagement
- Tertiary: Feature adoption after signup
Cross-Source Analysis Patterns
Traffic Sanity Check
Compare these three numbers to understand measurement coverage:
- CF zone unique IPs (inflated — includes bots)
- CF RUM visits (real human traffic)
- GA4 sessions (humans without ad blockers)
Conversion Funnel (full stack)
- Cloudflare RUM visits → total real visitors
- GA4 sessions → visitors without ad blockers
- GA4 sign_in_start → login intent
- GA4 sign_in_success → login completion
- Firestore user count → registered users
- Firestore feature adoption → active users
Content Performance Cross-Reference
- CF RUM top_paths → most visited pages
- GA4 charity_card_click → most engaged charities
- Firestore bookmarks → most saved charities
- Overlap analysis: are visited ≈ engaged ≈ saved?
Analytics Queries (GA4)
Realtime Overview
Use mcp__analytics-mcp__run_realtime_report with:
- property_id: (use GA4_PROPERTY_ID from .env)
- dimensions: ["eventName"]
- metrics: ["eventCount"]
Top Charities by Card Clicks (Last 7 Days)
Use mcp__analytics-mcp__run_report with:
- property_id: (use GA4_PROPERTY_ID from .env)
- date_ranges: [{"start_date": "7daysAgo", "end_date": "yesterday"}]
- dimensions: ["customEvent:charity_name"]
- metrics: ["eventCount"]
- dimension_filter: {"filter": {"field_name": "eventName", "string_filter": {"match_type": 1, "value": "charity_card_click"}}}
- order_bys: [{"metric": {"metric_name": "eventCount"}, "desc": true}]
- limit: 20
Search Terms Analysis
Use mcp__analytics-mcp__run_report with:
- property_id: (use GA4_PROPERTY_ID from .env)
- date_ranges: [{"start_date": "7daysAgo", "end_date": "yesterday"}]
- dimensions: ["customEvent:search_term"]
- metrics: ["eventCount"]
- dimension_filter: {"filter": {"field_name": "eventName", "string_filter": {"match_type": 1, "value": "search"}}}
- order_bys: [{"metric": {"metric_name": "eventCount"}, "desc": true}]
- limit: 20
Engagement Metrics (Time on Site)
Use mcp__analytics-mcp__run_report with:
- property_id: (use GA4_PROPERTY_ID from .env)
- date_ranges: [{"start_date": "7daysAgo", "end_date": "yesterday"}]
- dimensions: ["date"]
- metrics: ["averageSessionDuration", "engagedSessions", "sessions", "activeUsers"]
- order_bys: [{"dimension": {"dimension_name": "date"}, "desc": false}]
Login Funnel Analysis
Use mcp__analytics-mcp__run_report with:
- property_id: (use GA4_PROPERTY_ID from .env)
- date_ranges: [{"start_date": "7daysAgo", "end_date": "yesterday"}]
- dimensions: ["eventName"]
- metrics: ["eventCount"]
- dimension_filter: {"filter": {"field_name": "eventName", "in_list_filter": {"values": ["sign_in_start", "page_view"], "case_sensitive": true}}}
Funnel Analysis Pattern
Browse → Card Click → Charity View → Donate Funnel
Step 1: Count users who visited /browse
dimensions: ["pagePath"]
metrics: ["activeUsers"]
dimension_filter: pagePath contains "/browse"
Step 2: Count charity_card_click events
dimensions: ["eventName"]
metrics: ["eventCount"]
dimension_filter: eventName = "charity_card_click"
Step 3: Count charity_view events
dimensions: ["eventName"]
metrics: ["eventCount"]
dimension_filter: eventName = "charity_view"
Step 4: Count donate_click events
dimensions: ["eventName"]
metrics: ["eventCount"]
dimension_filter: eventName = "donate_click"
Calculate drop-off rates between each step.
Standard Reports to Run
Daily Health Check
Run these queries and summarize:
- Traffic (Cloudflare): RUM visits, pageloads, zone requests, unique IPs
- Engagement (GA4): Avg session duration, engaged sessions (note: GA4 undercounts)
- Top Pages (Cloudflare RUM): Most visited paths
- Top Charities (GA4): Most clicked charity cards
- Conversions (GA4): sign_in_start count
- Users (Firestore): Total registered users, recent signups
Weekly Deep Dive
- Traffic Trends (Cloudflare): Week-over-week RUM visits, geographic shifts
- Search Analysis (GA4): What are users searching for? Gaps in charity coverage?
- Funnel Metrics: CF RUM visits → GA4 sessions → card clicks → views → donates → signups
- Device Split (Cloudflare): Browser breakdown, mobile vs desktop
- Feature Adoption (Firestore): Which features are growing/stagnant?
Interpreting Results
Engagement Benchmarks
| Metric | Poor | Average | Good |
|---|---|---|---|
| Avg Session Duration | <1 min | 1-3 min | >3 min |
| Pages per Session | <2 | 2-4 | >4 |
| Bounce Rate | >70% | 40-70% | <40% |
| Card Click Rate | <5% | 5-15% | >15% |
Red Flags to Watch
- CF RUM visits high but GA4 near-zero → GA4 is broken (check
initializeAnalyticscall) - CF zone unique_ips >> CF RUM visits → normal (bots), but ratio > 10:1 warrants investigation
- High bounce rate on /browse → cards may not be compelling
- Low charity_view after card_click → page load issues?
- Zero search events → search feature not discoverable
- sign_in_start with no sign_in_success → auth issues
Flow Path Analysis
Query common user journeys:
Use mcp__analytics-mcp__run_report with:
- property_id: (use GA4_PROPERTY_ID from .env)
- date_ranges: [{"start_date": "7daysAgo", "end_date": "yesterday"}]
- dimensions: ["customEvent:flow_path"]
- metrics: ["eventCount"]
- dimension_filter: {"filter": {"field_name": "eventName", "string_filter": {"match_type": 1, "value": "donate_click"}}}
- limit: 20
This shows what paths lead to donations.
Quick Commands
Realtime snapshot:
"What's happening on the site right now?"
Weekly report:
"Give me a weekly analytics summary"
Search insights:
"What are users searching for?"
Charity performance:
"Which charities get the most clicks?"
Engagement check:
"How's our time on site trending?"
Full report (all sources):
"Run /analytics"
Firestore Data Source
Script
scripts/firestore_analytics.py — queries Firestore via REST API using gcloud auth print-access-token. No additional dependencies (stdlib only). Outputs structured JSON to stdout, logs to stderr.
uv run python scripts/firestore_analytics.py 2>/tmp/firestore_analytics.err
Firestore Schema
users (top-level collection)
| Field | Type | Description |
|---|---|---|
createdAt |
timestamp | Account creation date |
updatedAt |
timestamp | Last profile update |
targetZakatAmount |
number/null | Annual zakat target |
zakatAnniversary |
string/null | Zakat calculation anniversary date |
givingBuckets |
array | Custom giving categories (id, name, tags, percentage, color) |
charityBucketAssignments |
array | Charities assigned to buckets (charityEin, bucketId) |
geographicPreferences |
array | Preferred regions for giving |
fiqhPreferences |
map | Islamic jurisprudence preferences |
givingPriorities |
map | Cause area priorities |
users/{uid}/bookmarks (subcollection)
| Field | Type | Description |
|---|---|---|
charityEin |
string | Bookmarked charity EIN |
notes |
string/null | User notes |
createdAt |
timestamp | When bookmarked |
users/{uid}/giving_history (subcollection)
| Field | Type | Description |
|---|---|---|
charityEin |
string | Charity EIN |
charityName |
string | Charity display name |
amount |
number | Donation amount |
date |
string | Donation date (YYYY-MM-DD) |
category |
string | zakat, sadaqah, or other |
zakatYear |
number | Applicable zakat year |
taxDeductible |
boolean | Tax deductible flag |
receiptReceived |
boolean | Receipt received flag |
matchEligible |
boolean | Employer match eligible |
matchStatus |
string/null | Match status |
matchAmount |
number/null | Match amount |
paymentSource |
string/null | Payment method |
notes |
string/null | User notes |
createdAt |
timestamp | When recorded |
users/{uid}/charity_targets (subcollection) — planned giving targets (currently empty)
reported_issues (top-level collection) — user-reported data issues (currently empty)
Feature Adoption Metrics
The script calculates adoption as: (users with feature) / (total users) * 100%
Features tracked:
- Bookmarks: user has any docs in
bookmarkssubcollection - Giving History: user has any docs in
giving_historysubcollection - Zakat Target:
targetZakatAmountis non-null and non-zero - Giving Buckets:
givingBucketsarray is non-empty - Bucket Assignments:
charityBucketAssignmentsarray is non-empty - Geographic Preferences:
geographicPreferencesarray is non-empty - Fiqh Preferences:
fiqhPreferencesmap has any truthy values - Zakat Anniversary:
zakatAnniversaryis non-null
MCP Tool Limitations
The Firebase MCP tools (firestore_query_collection, firestore_get_documents) cannot query subcollections. The Python script uses the Firestore REST API with allDescendants: true collection group queries to access bookmarks, giving_history, and charity_targets data across all users in a single API call per collection type.
More from uabbasi/good-measure-giving
frontend-design
Create distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, artifacts, posters, or applications (examples include websites, landing pages, dashboards, React components, HTML/CSS layouts, or when styling/beautifying any web UI). Generates creative, polished code and UI design that avoids generic AI aesthetics.
11form990-expert
Deep expertise on IRS Form 990 structure, fields, and nonprofit financial analysis. Activates when working on Form 990 parsing, ProPublica API integration, or charity financial evaluation code.
1zakat-fiqh
Islamic jurisprudence expertise on zakat - the 8 categories of recipients (asnaf), scholarly interpretations across madhabs, and how to assess charity alignment with zakat eligibility. Activates when working on zakat classification, wallet tags, or donor guidance.
1webapp-testing
Test web applications using Playwright. Supports verifying frontend functionality, debugging UI behavior, capturing screenshots, and viewing browser logs. Use when testing the website or debugging UI issues.
1llm-prompting
Expert guidance on LLM prompting patterns used in this project. Covers versioned prompts, schema enforcement, category calibration, multi-provider fallback, and narrative generation. Activates when working on prompts, LLM integration, or evaluation logic.
1data-pipeline
Run the charity evaluation pipeline - extraction, 4-stage V2 workflow, Supabase patterns, versioning. Use when working on collectors, scrapers, pipeline code, database queries, or debugging data flow.
1