Working metrics for all forms of interactions and updated .env.example
This commit is contained in:
11
.env.example
11
.env.example
@@ -27,3 +27,14 @@ S3_VIRTUAL_HOST=false
|
|||||||
|
|
||||||
# Email (Brevo)
|
# Email (Brevo)
|
||||||
BREVO_API_KEY=your-brevo-api-key
|
BREVO_API_KEY=your-brevo-api-key
|
||||||
|
|
||||||
|
# ClickHouse (analytics / metrics)
|
||||||
|
# single connection URL (overrides all individual vars below)
|
||||||
|
CLICKHOUSE_URL=http://default:password@localhost:8123/mixer_metrics
|
||||||
|
# individual vars (used when CLICKHOUSE_URL is not set)
|
||||||
|
CLICKHOUSE_HOST=localhost
|
||||||
|
CLICKHOUSE_PORT=8123
|
||||||
|
CLICKHOUSE_DATABASE=mixer_metrics
|
||||||
|
CLICKHOUSE_USERNAME=default
|
||||||
|
CLICKHOUSE_PASSWORD=
|
||||||
|
CLICKHOUSE_SCHEME=http
|
||||||
|
|||||||
@@ -33,7 +33,8 @@ defmodule Mixer.Metrics do
|
|||||||
# Event types
|
# Event types
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
@type event_type :: :view | :like | :unlike | :comment | :share
|
@type event_type ::
|
||||||
|
:view | :post | :comment | :like | :unlike | :share | :delete_post | :delete_comment
|
||||||
|
|
||||||
@type track_opt ::
|
@type track_opt ::
|
||||||
{:user_id, binary() | nil}
|
{:user_id, binary() | nil}
|
||||||
@@ -70,6 +71,33 @@ defmodule Mixer.Metrics do
|
|||||||
@spec track_share(binary(), [track_opt()]) :: :ok
|
@spec track_share(binary(), [track_opt()]) :: :ok
|
||||||
def track_share(tweet_id, opts \\ []), do: enqueue("share", tweet_id, opts)
|
def track_share(tweet_id, opts \\ []), do: enqueue("share", tweet_id, opts)
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Track a new top-level tweet being published.
|
||||||
|
|
||||||
|
The event is recorded against the new tweet's own ID.
|
||||||
|
"""
|
||||||
|
@spec track_post(binary(), [track_opt()]) :: :ok
|
||||||
|
def track_post(tweet_id, opts \\ []), do: enqueue("post", tweet_id, opts)
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Track a top-level tweet being deleted.
|
||||||
|
|
||||||
|
The event is recorded against the deleted tweet's ID.
|
||||||
|
Note: cascade-deleted comments are not individually tracked — only the
|
||||||
|
explicit user-initiated destroy action emits this event.
|
||||||
|
"""
|
||||||
|
@spec track_delete_post(binary(), [track_opt()]) :: :ok
|
||||||
|
def track_delete_post(tweet_id, opts \\ []), do: enqueue("delete_post", tweet_id, opts)
|
||||||
|
|
||||||
|
@doc """
|
||||||
|
Track a comment (reply) being deleted.
|
||||||
|
|
||||||
|
The event is recorded against the *parent* tweet's ID so that
|
||||||
|
`get_summary/1` can reflect net comment activity on a tweet.
|
||||||
|
"""
|
||||||
|
@spec track_delete_comment(binary(), [track_opt()]) :: :ok
|
||||||
|
def track_delete_comment(tweet_id, opts \\ []), do: enqueue("delete_comment", tweet_id, opts)
|
||||||
|
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
# Query helpers
|
# Query helpers
|
||||||
# ---------------------------------------------------------------------------
|
# ---------------------------------------------------------------------------
|
||||||
|
|||||||
@@ -8,13 +8,16 @@ defmodule Mixer.Metrics.PostEvent do
|
|||||||
|
|
||||||
## Event types
|
## Event types
|
||||||
|
|
||||||
| event_type | Description |
|
| event_type | `tweet_id` refers to | Description |
|
||||||
|-------------|------------------------------------------|
|
|--------------------|-----------------------|-------------------------------------------------|
|
||||||
| `"view"` | A tweet was displayed to a user |
|
| `"view"` | the viewed tweet | Tweet detail page was loaded |
|
||||||
| `"like"` | A user liked a tweet |
|
| `"post"` | the new tweet | A new top-level tweet was published |
|
||||||
| `"unlike"` | A user removed their like from a tweet |
|
| `"comment"` | the parent tweet | A reply was posted; count against the parent |
|
||||||
| `"comment"` | A user replied to a tweet |
|
| `"like"` | the liked tweet | A user liked a tweet |
|
||||||
| `"share"` | A user shared / reposted a tweet |
|
| `"unlike"` | the unliked tweet | A user removed their like |
|
||||||
|
| `"share"` | the shared tweet | A user shared / reposted a tweet |
|
||||||
|
| `"delete_post"` | the deleted tweet | A top-level tweet was deleted by its author |
|
||||||
|
| `"delete_comment"` | the parent tweet | A reply was deleted; count against the parent |
|
||||||
"""
|
"""
|
||||||
|
|
||||||
use Ecto.Schema
|
use Ecto.Schema
|
||||||
|
|||||||
@@ -32,7 +32,7 @@ defmodule Mixer.Posts.Tweet do
|
|||||||
end
|
end
|
||||||
|
|
||||||
actions do
|
actions do
|
||||||
defaults [:read, :destroy]
|
defaults [:read]
|
||||||
|
|
||||||
read :following_feed do
|
read :following_feed do
|
||||||
filter expr(
|
filter expr(
|
||||||
@@ -67,25 +67,48 @@ defmodule Mixer.Posts.Tweet do
|
|||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
# Track a "comment" metric event whenever a reply is posted. We record
|
# Track post / comment creation metrics.
|
||||||
# the event against the *parent* tweet so that `get_summary/1` and
|
# Root tweets emit a "post" event recorded against their own ID.
|
||||||
# `get_bulk_summaries/1` can count how many comments each tweet received.
|
# Replies emit a "comment" event recorded against the parent tweet ID so
|
||||||
|
# that `get_summary/1` can count how many replies a tweet has received.
|
||||||
change fn changeset, context ->
|
change fn changeset, context ->
|
||||||
case Ash.Changeset.get_attribute(changeset, :parent_tweet_id) do
|
parent_tweet_id = Ash.Changeset.get_attribute(changeset, :parent_tweet_id)
|
||||||
nil ->
|
user_id = context.actor && context.actor.id
|
||||||
changeset
|
|
||||||
|
|
||||||
parent_tweet_id ->
|
|
||||||
Ash.Changeset.after_action(changeset, fn _changeset, tweet ->
|
Ash.Changeset.after_action(changeset, fn _changeset, tweet ->
|
||||||
Mixer.Metrics.track_comment(
|
if parent_tweet_id do
|
||||||
parent_tweet_id,
|
Mixer.Metrics.track_comment(parent_tweet_id, user_id: user_id)
|
||||||
user_id: context.actor && context.actor.id
|
else
|
||||||
)
|
Mixer.Metrics.track_post(tweet.id, user_id: user_id)
|
||||||
|
end
|
||||||
|
|
||||||
{:ok, tweet}
|
{:ok, tweet}
|
||||||
end)
|
end)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
# Explicit destroy so we can attach a metrics hook. The policy and cascade
|
||||||
|
# behaviour are identical to the previous default :destroy action.
|
||||||
|
destroy :destroy do
|
||||||
|
require_atomic? false
|
||||||
|
|
||||||
|
change fn changeset, context ->
|
||||||
|
# Capture the record's identity *before* deletion — after the action
|
||||||
|
# completes the row no longer exists.
|
||||||
|
tweet_id = changeset.data.id
|
||||||
|
parent_tweet_id = changeset.data.parent_tweet_id
|
||||||
|
user_id = context.actor && context.actor.id
|
||||||
|
|
||||||
|
Ash.Changeset.after_action(changeset, fn _changeset, result ->
|
||||||
|
if parent_tweet_id do
|
||||||
|
Mixer.Metrics.track_delete_comment(parent_tweet_id, user_id: user_id)
|
||||||
|
else
|
||||||
|
Mixer.Metrics.track_delete_post(tweet_id, user_id: user_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
{:ok, result}
|
||||||
|
end)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
update :update do
|
update :update do
|
||||||
|
|||||||
Reference in New Issue
Block a user