PhoenixKit.Modules.Publishing.Views (PhoenixKitPublishing v0.7.0)

Copy Markdown View Source

Post view counting over core migration V159's phoenix_kit_publishing_post_views — one (post_uuid, view_date) row per day, incremented in place. Totals are SUM(count).

What counts as a view

A public post-page render for a group with views_enabled on, where:

  • the visitor's User-Agent doesn't look like a bot/CLI (bot_ua?/1), and
  • the visitor's session hasn't already viewed this post today — dedup rides the Phoenix session cookie (mark_viewed/2), so there is no server-side visitor state and no reader PII: the DB only ever stores accepted counts. A cookieless client (most bots) still passes the session check every time, which is why the UA filter runs first.

Recording is fire-and-forget (Task.Supervisor under core's PhoenixKit.TaskSupervisor) — a slow or failing insert never delays or crashes the page.

Summary

Functions

True when the User-Agent looks like a bot/CLI (or is absent).

Records a view for post_uuid unless the request is a bot or this session already viewed the post today. Returns the (possibly updated) conn — callers thread it so the dedup marker lands in the session cookie.

Increments today's rollup row for a post (upsert). Safe under concurrency — the conflict target is the primary key and the update is an atomic count = count + 1.

A group's top posts by views in the trailing days window: [{post_uuid, views}], most-viewed first, capped at limit.

All-time view total for one post.

All-time view totals for a list of post uuids: %{post_uuid => total}.

Functions

bot_ua?(ua)

True when the User-Agent looks like a bot/CLI (or is absent).

maybe_record_view(conn, post_uuid)

Records a view for post_uuid unless the request is a bot or this session already viewed the post today. Returns the (possibly updated) conn — callers thread it so the dedup marker lands in the session cookie.

record_view(post_uuid, date \\ nil)

Increments today's rollup row for a post (upsert). Safe under concurrency — the conflict target is the primary key and the update is an atomic count = count + 1.

top_posts(group_slug, days, limit)

A group's top posts by views in the trailing days window: [{post_uuid, views}], most-viewed first, capped at limit.

total(post_uuid)

All-time view total for one post.

totals(post_uuids)

All-time view totals for a list of post uuids: %{post_uuid => total}.