stats page

This commit is contained in:
byte[] 2019-11-29 17:45:44 -05:00
parent 6e6d08f098
commit d6ba37c882
8 changed files with 334 additions and 6 deletions

89
config/aggregation.json Normal file
View file

@ -0,0 +1,89 @@
{
"comments": {
"aggs": {
"deleted": {
"filter": {
"term": {
"hidden_from_users": true
}
}
},
"last_24h": {
"filter": {
"range": {
"posted_at": {
"gt": "now-24h"
}
}
}
}
}
},
"images": {
"aggs": {
"deleted": {
"filter": {
"term": {
"hidden_from_users": true
}
}
},
"non_deleted": {
"aggs": {
"all_time": {
"date_histogram": {
"field": "created_at",
"interval": "day"
}
},
"avg_comments": {
"avg": {
"field": "comment_count"
}
},
"faves_gt_0": {
"filter": {
"range": {
"faves": {
"gt": 0
}
}
}
},
"last_24h": {
"filter": {
"range": {
"created_at": {
"gt": "now-24h"
}
}
}
},
"score_gt_0": {
"filter": {
"range": {
"score": {
"gt": 0
}
}
}
},
"score_lt_0": {
"filter": {
"range": {
"score": {
"lt": 0
}
}
}
}
},
"filter": {
"term": {
"hidden_from_users": false
}
}
}
}
}
}

View file

@ -22,6 +22,7 @@ config :philomena,
cdn_host: "", cdn_host: "",
proxy_host: nil, proxy_host: nil,
quick_tags_json: File.read!("config/quick_tag_table.json"), quick_tags_json: File.read!("config/quick_tag_table.json"),
aggregation_json: File.read!("config/aggregation.json"),
footer_json: File.read!("config/footer.json") footer_json: File.read!("config/footer.json")
config :philomena, :pow, config :philomena, :pow,

View file

@ -82,19 +82,24 @@ defmodule Philomena.Elasticsearch do
reindex(ecto_query, batch_size, ids) reindex(ecto_query, batch_size, ids)
end end
def search_results(elastic_query, pagination_params \\ %{}) do def search(query_body) do
page_number = pagination_params[:page_number] || 1
page_size = pagination_params[:page_size] || 25
elastic_query = Map.merge(elastic_query, %{from: (page_number - 1) * page_size, size: page_size, _source: false})
{:ok, %{body: results, status_code: 200}} = {:ok, %{body: results, status_code: 200}} =
Elastix.Search.search( Elastix.Search.search(
unquote(elastic_url), unquote(elastic_url),
unquote(index_name), unquote(index_name),
[unquote(doc_type)], [unquote(doc_type)],
elastic_query query_body
) )
results
end
def search_results(elastic_query, pagination_params \\ %{}) do
page_number = pagination_params[:page_number] || 1
page_size = pagination_params[:page_size] || 25
elastic_query = Map.merge(elastic_query, %{from: (page_number - 1) * page_size, size: page_size, _source: false})
results = search(elastic_query)
time = results["took"] time = results["took"]
count = results["hits"]["total"] count = results["hits"]["total"]
entries = results["hits"]["hits"] |> Enum.map(&String.to_integer(&1["_id"])) entries = results["hits"]["hits"] |> Enum.map(&String.to_integer(&1["_id"]))

View file

@ -0,0 +1,65 @@
defmodule PhilomenaWeb.StatController do
use PhilomenaWeb, :controller
alias Philomena.Images.Image
alias Philomena.Comments.Comment
alias Philomena.Topics.Topic
alias Philomena.Forums.Forum
alias Philomena.Posts.Post
alias Philomena.Users.User
alias Philomena.Repo
import Ecto.Query
def index(conn, _params) do
{image_aggs, comment_aggs } = aggregations()
{forums, topics, posts} = forums()
{users, users_24h} = users()
render(
conn,
"index.html",
image_aggs: image_aggs,
comment_aggs: comment_aggs,
forums_count: forums,
topics_count: topics,
posts_count: posts,
users_count: users,
users_24h: users_24h
)
end
defp aggregations do
data =
Application.get_env(:philomena, :aggregation_json)
|> Jason.decode!()
{Image.search(data["images"]), Comment.search(data["comments"])}
end
defp forums do
forums =
Forum
|> where(access_level: "normal")
|> Repo.aggregate(:count, :id)
first_topic = Repo.one(first(Topic))
last_topic = Repo.one(last(Topic))
first_post = Repo.one(first(Post))
last_post = Repo.one(last(Post))
{forums, last_topic.id - first_topic.id, last_post.id - first_post.id}
end
defp users do
first_user = Repo.one(first(User))
last_user = Repo.one(last(User))
time = DateTime.utc_now() |> DateTime.add(-86400, :second)
last_24h =
User
|> where([u], u.created_at > ^time)
|> Repo.aggregate(:count, :id)
{last_user.id - first_user.id, last_24h}
end
end

View file

@ -131,6 +131,7 @@ defmodule PhilomenaWeb.Router do
resources "/pages", PageController, only: [:show] resources "/pages", PageController, only: [:show]
resources "/dnp", DnpEntryController, only: [:index, :show] resources "/dnp", DnpEntryController, only: [:index, :show]
resources "/staff", StaffController, only: [:index] resources "/staff", StaffController, only: [:index]
resources "/stats", StatController, only: [:index]
get "/:id", ImageController, :show get "/:id", ImageController, :show
# get "/:forum_id", ForumController, :show # impossible to do without constraints # get "/:forum_id", ForumController, :show # impossible to do without constraints

View file

@ -0,0 +1,76 @@
h1 Site Statistics
elixir:
img_bucket = @image_aggs["aggregations"]
cmt_bucket = @comment_aggs["aggregations"]
.walloftext
h3 Images
p
' There are
span.stat>
= number_with_delimiter(img_bucket["non_deleted"]["doc_count"])
' non-deleted images total in our database. Of these,
span.stat>
= number_with_delimiter(img_bucket["non_deleted"]["last_24h"]["doc_count"])
' images were uploaded in the last 24 hours.
p
' This net total excludes the
=> number_with_delimiter(img_bucket["deleted"]["doc_count"])
' images that have been deleted or marked as duplicates.
h3 Comments
p
' There are
span.stat>
= number_with_delimiter(@comment_aggs["hits"]["total"])
' comments on the site. Of these,
=> number_with_delimiter(cmt_bucket["deleted"]["doc_count"])
' have been deleted.
p
' In the last 24 hours,
span.stat>
= number_with_delimiter(cmt_bucket["last_24h"]["doc_count"])
' comments have been posted.
p
' There are, on average,
span.stat>
= number_with_delimiter(trunc(img_bucket["non_deleted"]["avg_comments"]["value"]))
' comments on each image on the site.
h3 Votes
p
' Out of
=> number_with_delimiter(img_bucket["doc_count"])
' images,
span.stat>
= number_with_delimiter(img_bucket["score_gt_0"]["doc_count"])
' images have a score above 0, and
span.stat>
= number_with_delimiter(img_bucket["score_lt_0"]["doc_count"])
' images have a score below 0.
span.stat>
= number_with_delimiter(img_bucket["faves_gt_0"]["doc_count"])
' images have been faved by at least one user.
h3 Forums
p
' In our
=> @forums_count
' forums there have been
span.stat>
= number_with_delimiter(@topics_count)
' topics started. There have been
span.stat>
= number_with_delimiter(@posts_count)
' replies to topics in total.
h3 Users
p
' There are
span.stat>
= number_with_delimiter(@users_count)
' users on the site. Of these,
span.stat>
= number_with_delimiter(@users_24h)
' have joined in the last 24 hours.

View file

@ -0,0 +1,3 @@
defmodule PhilomenaWeb.StatView do
use PhilomenaWeb, :view
end

View file

@ -0,0 +1,88 @@
defmodule PhilomenaWeb.StatControllerTest do
use PhilomenaWeb.ConnCase
alias Philomena.Stats
@create_attrs %{}
@update_attrs %{}
@invalid_attrs %{}
def fixture(:stat) do
{:ok, stat} = Stats.create_stat(@create_attrs)
stat
end
describe "index" do
test "lists all stats", %{conn: conn} do
conn = get(conn, Routes.stat_path(conn, :index))
assert html_response(conn, 200) =~ "Listing Stats"
end
end
describe "new stat" do
test "renders form", %{conn: conn} do
conn = get(conn, Routes.stat_path(conn, :new))
assert html_response(conn, 200) =~ "New Stat"
end
end
describe "create stat" do
test "redirects to show when data is valid", %{conn: conn} do
conn = post(conn, Routes.stat_path(conn, :create), stat: @create_attrs)
assert %{id: id} = redirected_params(conn)
assert redirected_to(conn) == Routes.stat_path(conn, :show, id)
conn = get(conn, Routes.stat_path(conn, :show, id))
assert html_response(conn, 200) =~ "Show Stat"
end
test "renders errors when data is invalid", %{conn: conn} do
conn = post(conn, Routes.stat_path(conn, :create), stat: @invalid_attrs)
assert html_response(conn, 200) =~ "New Stat"
end
end
describe "edit stat" do
setup [:create_stat]
test "renders form for editing chosen stat", %{conn: conn, stat: stat} do
conn = get(conn, Routes.stat_path(conn, :edit, stat))
assert html_response(conn, 200) =~ "Edit Stat"
end
end
describe "update stat" do
setup [:create_stat]
test "redirects when data is valid", %{conn: conn, stat: stat} do
conn = put(conn, Routes.stat_path(conn, :update, stat), stat: @update_attrs)
assert redirected_to(conn) == Routes.stat_path(conn, :show, stat)
conn = get(conn, Routes.stat_path(conn, :show, stat))
assert html_response(conn, 200)
end
test "renders errors when data is invalid", %{conn: conn, stat: stat} do
conn = put(conn, Routes.stat_path(conn, :update, stat), stat: @invalid_attrs)
assert html_response(conn, 200) =~ "Edit Stat"
end
end
describe "delete stat" do
setup [:create_stat]
test "deletes chosen stat", %{conn: conn, stat: stat} do
conn = delete(conn, Routes.stat_path(conn, :delete, stat))
assert redirected_to(conn) == Routes.stat_path(conn, :index)
assert_error_sent 404, fn ->
get(conn, Routes.stat_path(conn, :show, stat))
end
end
end
defp create_stat(_) do
stat = fixture(:stat)
{:ok, stat: stat}
end
end