Skip to content

Show total and unique view/click analytics when individual tracking is on#3085

Open
sysrow wants to merge 2 commits into
knadh:masterfrom
sysrow:feat-analytics-unique-views
Open

Show total and unique view/click analytics when individual tracking is on#3085
sysrow wants to merge 2 commits into
knadh:masterfrom
sysrow:feat-analytics-unique-views

Conversation

@sysrow

@sysrow sysrow commented Jun 15, 2026

Copy link
Copy Markdown
Contributor

Closes #3083.

When privacy.individual_tracking is on, the analytics page now shows both total and unique view/click graphs, instead of silently replacing the totals with unique counts.

Changes

  • prepareQueries() (cmd/init.go): always prepares both the total and unique count statements for views and clicks, reusing the existing get-campaign-analytics-counts / get-campaign-analytics-unique-counts queries.
  • GetCampaignViewAnalytics reads a ?unique=true query param; GetCampaignAnalyticsCounts routes to the unique statement.
  • CampaignAnalytics.vue: renders "Unique views" / "Unique clicks" charts only when serverConfig.privacy.individual_tracking is true.
  • Two new en.json keys (campaigns.uniqueViews, campaigns.uniqueClicks).

When individual tracking is off, behavior is unchanged.

Tested: go build / go vet / eslint clean; verified against a running instance (total vs. unique counts render correctly per campaign).

When individual subscriber tracking is enabled, the analytics page used
to silently replace the total view/click counts with unique counts. It
now prepares both the total and unique-subscriber count queries and shows
them as separate "Views"/"Unique views" and "Clicks"/"Unique clicks"
graphs, so totals are no longer lost. Behaviour is unchanged when
individual tracking is off.
@sysrow

sysrow commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author
firefox_PcWnCU39UL

@MaximilianKohler

Copy link
Copy Markdown
Contributor

@sysrow since you're working on this area, do you think you could fix the circles & percentages too? #1728 (comment)

Or maybe give me some feedback on the AI answers I got, so I can try to do it?

@sysrow

sysrow commented Jun 15, 2026

Copy link
Copy Markdown
Contributor Author

@MaximilianKohler sure, Ill give it a try tomorrow

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Issues Found

Critical (P0/P1)

  • [P1] Block or ignore unique=true when individual tracking is disabled (cmd/campaigns.go:616-637)
    • The new unique=true query param is honored unconditionally, so callers can request unique view/click counts even when privacy.individual_tracking is off (UI hides it, but the API still serves it). Depending on how non-individual tracking data is stored (eg: subscriber_id always NULL/0), this can either expose distinct-subscriber analytics contrary to the privacy setting or return misleading counts; the handler should reject or force unique=false unless individual tracking is enabled.

Important (P2)

  • [P2] Avoid unconditional dependency on unique-count SQL when preparing queries (cmd/init.go:402-416)
    • prepareQueries() now always builds the *-unique statements by dereferencing qMap["get-campaign-analytics-unique-counts"]. If an operator supplies a custom queries directory (or older query set) that omits this query, listmonk will fail to start even if unique analytics are never used; consider gating this on privacy.individual_tracking (or at least checking presence and failing with a clearer error).

Summary

Total issues: 1 critical, 1 important, 0 minor.

Overall Verdict

Status: Patch has blocking issues

Explanation: The core feature works, but the API now exposes (or computes) unique analytics counts via ?unique=true without enforcing the privacy.individual_tracking setting, and query preparation now unconditionally depends on the unique-count SQL template being present.


Review generated by Hodor (model: gpt-5.2)

Review Metrics — 19 turns, 23 tool calls, 3m 24s

  • Tokens: in 24.1K | cached 302.2K | out 11.6K (total 337.9K)
  • Cost: $0.2575

@sysrow

sysrow commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

Ill fix the findings later tonight. Thanks.

- Force unique=false unless individual_tracking is on, so the API never serves
  distinct-subscriber counts against the privacy setting.
- Build the *-unique statements from the unique-count query only when tracking
  is on; a missing source query now fails with a clear error instead of a nil panic.
@sysrow

sysrow commented Jun 19, 2026

Copy link
Copy Markdown
Contributor Author

Thanks for the review. Addressed both:

  • P1: GetCampaignViewAnalytics now forces unique=false unless privacy.individual_tracking is enabled, so the API never serves distinct-subscriber counts against the privacy setting. (Defense in depth: when tracking is off the -unique statements are prepared from the total-count SQL, so even a stray unique=true returns totals.)
  • P2: prepareQueries only sources the -unique statements from get-campaign-analytics-unique-counts when individual tracking is on; otherwise it uses the total-count template, so a custom query set omitting the unique query no longer fails to start. A missing source query now fails with a clear error instead of a nil-pointer panic.

Verified end to end against the HTTP API in both states: with tracking on, ?unique=true returns distinct-subscriber counts and ?unique=false returns totals; with tracking off, ?unique=true returns totals (no distinct-subscriber data is served).

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Issues Found

No issues found.

Summary

Total issues: 0 critical, 0 important, 0 minor.

Overall Verdict

Status: Patch is correct

Explanation: Reviewing only the PR diff, the backend query preparation and routing logic correctly separates total vs. unique counts, and the frontend only requests/renders unique charts when individual tracking is enabled. I did not find any production-breaking bugs introduced by these changes.


Review generated by Hodor (model: gpt-5.2)

Review Metrics — 20 turns, 24 tool calls, 2m 44s

  • Tokens: in 17.7K | cached 271.4K | out 9.0K (total 298.0K)
  • Cost: $0.2041

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

hodor-review Automated AI code review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Show total counts alongside unique counts in campaign analytics when individual tracking is on

3 participants