Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions lib/mave_core/analytics.ex
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,85 @@ defmodule MaveCore.Analytics do
}}
end

@doc """
Returns the space's most viewed video embeds this year as
`%{embed_hash: hash, views: count}` maps, ordered by views.
"""
def space_top_videos(space_hash, limit \\ 25)

def space_top_videos(space_hash, limit)
when is_binary(space_hash) and is_integer(limit) and limit > 0 do
with {:ok, space_hash} <- parse_space_hash(space_hash) do
if SharedStorageSpace.hash?(space_hash) do
{:ok, []}
else
do_space_top_videos(space_hash, limit)
end
end
end

def space_top_videos(_space_hash, _limit), do: {:error, :invalid_space_hash}

defp do_space_top_videos(space_hash, limit) do
now = DateTime.utc_now()
year_from = DateTime.new!(Date.new!(now.year, 1, 1), ~T[00:00:00], "Etc/UTC")

db_name = clickhouse_database_name()
threshold = @min_watch_seconds * 1000

sql = """
/* mave_perf:spaces.top_videos */
WITH ordered AS (
SELECT
embed_hash,
session_id,
timestamp,
name,
leadInFrame(timestamp) OVER (
PARTITION BY embed_hash, session_id
ORDER BY timestamp
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS next_ts,
leadInFrame(name) OVER (
PARTITION BY embed_hash, session_id
ORDER BY timestamp
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING
) AS next_name
FROM #{db_name}.events
PREWHERE space_hash = #{string_literal(space_hash)}
AND timestamp >= #{datetime_literal(year_from)}
AND timestamp < #{datetime_literal(now)}
WHERE name IN ('play', 'pause')
),
session_aggregates AS (
SELECT
embed_hash,
session_id,
sumIf(
greatest(toInt64(0), toUnixTimestamp64Milli(next_ts) - toUnixTimestamp64Milli(timestamp)),
name = 'play' AND next_name = 'pause'
) AS watch_ms
FROM ordered
GROUP BY embed_hash, session_id
)
SELECT
embed_hash,
countIf(watch_ms >= #{threshold}) AS views
FROM session_aggregates
GROUP BY embed_hash
HAVING views > 0
ORDER BY views DESC, embed_hash ASC
LIMIT #{limit}
"""

query_clickhouse(sql, {:error, :analytics_unavailable}, fn rows ->
{:ok,
Enum.map(rows, fn [embed_hash, views] ->
%{embed_hash: embed_hash, views: to_int(views)}
end)}
end)
end

def space_monthly_watch_time(space_hash) when is_binary(space_hash) do
with {:ok, space_hash} <- parse_space_hash(space_hash) do
if SharedStorageSpace.hash?(space_hash) do
Expand Down
20 changes: 20 additions & 0 deletions lib/mave_core/embeds.ex
Original file line number Diff line number Diff line change
Expand Up @@ -795,6 +795,26 @@ defmodule MaveCore.Embeds do

defp maybe_broadcast_embed_updated(result, _payload), do: result

@doc """
Maps embed hashes of the space's video embeds to their dashboard id and
display name. Deleted embeds and unknown hashes are left out.
"""
@spec dashboard_videos_by_hash(Space.t(), [String.t()]) :: %{
String.t() => %{id: String.t(), name: String.t()}
}
def dashboard_videos_by_hash(%Space{}, []), do: %{}

def dashboard_videos_by_hash(%Space{id: space_id}, hashes) when is_list(hashes) do
Embed
|> where([embed], embed.space_id == ^space_id and embed.hash in ^hashes)
|> where([embed], embed.type == :video and is_nil(embed.deleted_at))
|> preload(asset: [:current_video])
|> Repo.all()
|> Map.new(fn embed ->
{embed.hash, %{id: dashboard_embed_id(embed), name: embed_name(embed)}}
end)
end

@spec dashboard_embed_id(Embed.t()) :: String.t()
def dashboard_embed_id(%Embed{id: id}) when is_binary(id), do: id

Expand Down
4 changes: 4 additions & 0 deletions lib/mave_core_web/dashboard_routes.ex
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,10 @@ defmodule MaveCoreWeb.DashboardRoutes do
path_for(space_or_user, "/data", [], opts)
end

def data_top_list_path(space_or_user, opts \\ []) do
path_for(space_or_user, "/data/top-list", [], opts)
end

def settings_path(space_or_user, tab \\ nil, opts \\ []) do
suffix =
case tab do
Expand Down
Loading
Loading