Skip to content

Commit

Permalink
[admin] Add feed stats views for podcasts, feeds
Browse files Browse the repository at this point in the history
  • Loading branch information
jerodsanto committed Aug 15, 2024
1 parent 152f909 commit 97c51e2
Show file tree
Hide file tree
Showing 10 changed files with 162 additions and 5 deletions.
2 changes: 2 additions & 0 deletions lib/changelog/policies/admin/feed.ex
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ defmodule Changelog.Policies.Admin.Feed do
use Changelog.Policies.Default

def index(actor), do: is_admin(actor)
def show(actor, _feed), do: is_admin(actor)
def feed_stats(actor, feed), do: show(actor, feed)
def create(actor), do: is_admin(actor)
def update(actor, _feed), do: is_admin(actor)
def refresh(actor, feed), do: update(actor, feed)
Expand Down
1 change: 1 addition & 0 deletions lib/changelog/policies/admin/podcast.ex
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ defmodule Changelog.Policies.Admin.Podcast do
def create(actor), do: is_admin(actor)
def index(actor), do: is_admin_or_host(actor)
def show(actor, podcast), do: is_admin(actor) || is_host(actor, podcast)
def feed_stats(actor, podcast), do: show(actor, podcast)
def update(actor, _), do: is_admin(actor)
def feed(actor, podcast), do: update(actor, podcast)
def delete(actor, _), do: is_admin(actor)
Expand Down
6 changes: 6 additions & 0 deletions lib/changelog/schema/feed_stat.ex
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,12 @@ defmodule Changelog.FeedStat do

def on_date(query \\ __MODULE__, date), do: from(q in query, where: q.date == ^date)

def next_after(query \\ __MODULE__, stat),
do: from(q in query, where: q.date > ^stat.date)

def previous_to(query \\ __MODULE__, stat),
do: from(q in query, where: q.date < ^stat.date)

def oldest_date do
Repo.one(from s in __MODULE__, select: [min(s.date)], limit: 1)
|> List.first()
Expand Down
29 changes: 27 additions & 2 deletions lib/changelog_web/controllers/admin/feed_controller.ex
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
defmodule ChangelogWeb.Admin.FeedController do
use ChangelogWeb, :controller

alias Changelog.{Feed, Podcast}
alias Changelog.{Feed, FeedStat, Podcast}
alias Changelog.ObanWorkers.FeedUpdater

plug :assign_feed when action in [:edit, :update, :delete, :refresh]
plug :assign_feed when action in [:edit, :update, :delete, :refresh, :feed_stats]
plug :assign_podcasts when action in [:index, :new, :create, :edit, :update]
plug Authorize, [Policies.Admin.Feed, :feed]
plug :scrub_params, "feed" when action in [:create, :update]
Expand Down Expand Up @@ -83,6 +83,31 @@ defmodule ChangelogWeb.Admin.FeedController do
|> redirect(to: ~p"/admin/feeds")
end

def feed_stats(conn = %{assigns: %{feed: feed}}, params) do
stat = if params["date"] do
feed
|> assoc(:feed_stats)
|> FeedStat.on_date(params["date"])
|> FeedStat.limit(1)
|> Repo.one()
else
feed
|> assoc(:feed_stats)
|> FeedStat.newest_first()
|> FeedStat.limit(1)
|> Repo.one()
end

prev = FeedStat.previous_to(stat) |> FeedStat.limit(1) |> Repo.one()
next = FeedStat.next_after(stat) |> FeedStat.limit(1) |> Repo.one()

conn
|> assign(:stat, stat)
|> assign(:prev, prev)
|> assign(:next, next)
|> render(:feed_stats)
end

defp assign_feed(conn = %{params: %{"id" => id}}, _params) do
feed = Feed |> Repo.get(id) |> Feed.preload_all()
assign(conn, :feed, feed)
Expand Down
29 changes: 27 additions & 2 deletions lib/changelog_web/controllers/admin/podcast_controller.ex
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
defmodule ChangelogWeb.Admin.PodcastController do
use ChangelogWeb, :controller

alias Changelog.{Cache, Podcast}
alias Changelog.{Cache, FeedStat, Podcast}
alias Changelog.ObanWorkers.FeedUpdater

plug :assign_podcast when action in [:show, :edit, :update, :feed]
plug :assign_podcast when action in [:show, :edit, :update, :feed, :feed_stats]
plug Authorize, [Policies.Admin.Podcast, :podcast]
plug :scrub_params, "podcast" when action in [:create, :update]

Expand Down Expand Up @@ -118,6 +118,31 @@ defmodule ChangelogWeb.Admin.PodcastController do
|> redirect(to: Routes.admin_podcast_path(conn, :index))
end

def feed_stats(conn = %{assigns: %{podcast: podcast}}, params) do
stat = if params["date"] do
podcast
|> assoc(:feed_stats)
|> FeedStat.on_date(params["date"])
|> FeedStat.limit(1)
|> Repo.one()
else
podcast
|> assoc(:feed_stats)
|> FeedStat.newest_first()
|> FeedStat.limit(1)
|> Repo.one()
end

prev = FeedStat.previous_to(stat) |> FeedStat.limit(1) |> Repo.one()
next = FeedStat.next_after(stat) |> FeedStat.limit(1) |> Repo.one()

conn
|> assign(:stat, stat)
|> assign(:prev, prev)
|> assign(:next, next)
|> render(:feed_stats)
end

defp assign_podcast(conn = %{params: %{"id" => id}}, _) do
podcast = Repo.get_by!(Podcast, slug: id) |> Podcast.preload_hosts()
assign(conn, :podcast, podcast)
Expand Down
2 changes: 2 additions & 0 deletions lib/changelog_web/router.ex
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ defmodule ChangelogWeb.Router do
get "/search/:type", SearchController, :one

resources "/feeds", FeedController
get "/feeds/:id/feed_stats", FeedController, :feed_stats
post "/feeds/:id/refresh", FeedController, :refresh, as: :feed

resources "/memberships", MembershipController, except: [:create, :delete]
Expand Down Expand Up @@ -119,6 +120,7 @@ defmodule ChangelogWeb.Router do

resources "/subscriptions", PodcastSubscriptionController, as: :subscription, only: [:index]
end
get "/podcasts/:id/feed_stats", PodcastController, :feed_stats

post "/podcasts/:id/feed", PodcastController, :feed, as: :podcast

Expand Down
2 changes: 2 additions & 0 deletions lib/changelog_web/templates/admin/feed/_table.html.heex
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
<th>Created at</th>
<th>Refreshed at</th>
<th>Starts on</th>
<th>Agents</th>
<th>Podcasts</th>
<th></th>
</tr>
Expand Down Expand Up @@ -41,6 +42,7 @@
<td><%= AdminHelpers.ts(feed.inserted_at) %></td>
<td><%= AdminHelpers.ts(feed.refreshed_at) %></td>
<td><%= TimeView.hacker_date(feed.starts_on) %></td>
<td><%= link(length(feed.agents), to: ~p"/admin/feeds/#{feed}/feed_stats") %></td>
<td class="ui mini images">
<%= for podcast <- @podcasts do %>
<%= if Enum.member?(feed.podcast_ids, podcast.id) do %>
Expand Down
46 changes: 46 additions & 0 deletions lib/changelog_web/templates/admin/feed/feed_stats.html.heex
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
<div class="ui basic segment">
<h1 class="ui dividing header">
<%= link("Feeds", to: ~p"/admin/feeds") %>
&raquo;<%= @feed.id %> (<%= @feed.name %>)
&raquo;Feed Stats
</h1>

<div class="actions right">
<%= if @prev do %>
<%= link to: ~p"/admin/feeds/#{@feed}/feed_stats" <> "?date=#{@prev.date}", class: "ui right floated basic button" do %>
<i class="icon angle double left"></i>
Previous Day
<% end %>
<% end %>
<%= if @next do %>
<%= link to: ~p"/admin/feeds/#{@feed}/feed_stats" <> "?date=#{@next.date}", class: "ui right floated basic button" do %>
<i class="icon angle double right"></i>
Next Day
<% end %>
<% end %>
</div>
</div>

<div class="ui basic segment">
<h2 class="ui header"><%= @stat.date %></h2>
<table class="ui striped table">
<thead>
<tr>
<th>Agent</th>
<th>Type</th>
<th>Requests</th>
<th>Raw</th>
</tr>
</thead>
<tbody>
<%= for {name, data} <- Enum.sort_by(@stat.agents, fn {_name, data} -> data["requests"] end, :desc) do %>
<tr>
<td><%= name %></td>
<td><%= data["type"] %></td>
<td><%= data["requests"] %></td>
<td><%= data["raw"] %></td>
</tr>
<% end %>
</tbody>
</table>
</div>
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@
<td class="center aligned">
<%= link to: ~p"/admin/podcasts/#{podcast.slug}/episodes" do %>
<h2 class="ui image header">
<img src="<%= cover_url(podcast, :small) %>" class="ui image" />
<img src={cover_url(podcast, :small)} class="ui image" />
</h2>
<% end %>
</td>
Expand All @@ -34,6 +34,7 @@
<%= for {client, count} <- (podcast.subscribers || %{}) do %>
<strong><%= client %>:</strong> <%= SharedHelpers.comma_separated(count) %><br/>
<% end %>
<%= link("Feed Stats", to: ~p"/admin/podcasts/#{podcast.slug}/feed_stats") %>
</td>
<td><%= episode_count(podcast) %></td>
<td><%= SharedHelpers.pretty_downloads(podcast) %></td>
Expand Down
47 changes: 47 additions & 0 deletions lib/changelog_web/templates/admin/podcast/feed_stats.html.heex
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
<div class="ui basic segment">
<h1 class="ui dividing header">
<%= link("Podcasts", to: ~p"/admin/podcasts") %>
<%= render("_dropdown_list.html", assigns) %>
<%= @podcast.name %>
&raquo;Feed Stats
</h1>

<div class="actions right">
<%= if @prev do %>
<%= link to: ~p"/admin/podcasts/#{@podcast.slug}/feed_stats" <> "?date=#{@prev.date}", class: "ui right floated basic button" do %>
<i class="icon angle double left"></i>
Previous Day
<% end %>
<% end %>
<%= if @next do %>
<%= link to: ~p"/admin/podcasts/#{@podcast.slug}/feed_stats" <> "?date=#{@next.date}", class: "ui right floated basic button" do %>
<i class="icon angle double right"></i>
Next Day
<% end %>
<% end %>
</div>
</div>

<div class="ui basic segment">
<h2 class="ui header"><%= @stat.date %></h2>
<table class="ui striped table">
<thead>
<tr>
<th>Agent</th>
<th>Type</th>
<th>Requests</th>
<th>Raw</th>
</tr>
</thead>
<tbody>
<%= for {name, data} <- Enum.sort_by(@stat.agents, fn {_name, data} -> data["requests"] end, :desc) do %>
<tr>
<td><%= name %></td>
<td><%= data["type"] %></td>
<td><%= data["requests"] %></td>
<td><%= data["raw"] %></td>
</tr>
<% end %>
</tbody>
</table>
</div>

0 comments on commit 97c51e2

Please sign in to comment.