diff --git a/lib/philomena_web/controllers/profile_controller.ex b/lib/philomena_web/controllers/profile_controller.ex index 82fd97d4..4b777089 100644 --- a/lib/philomena_web/controllers/profile_controller.ex +++ b/lib/philomena_web/controllers/profile_controller.ex @@ -3,6 +3,7 @@ defmodule PhilomenaWeb.ProfileController do alias PhilomenaWeb.ImageLoader alias Philomena.Textile.Renderer + alias Philomena.UserStatistics.UserStatistic alias Philomena.Users.User alias Philomena.Galleries.Gallery alias Philomena.Posts.Post @@ -82,6 +83,8 @@ defmodule PhilomenaWeb.ProfileController do |> limit(5) |> Repo.all() + statistics = calculate_statistics(user) + interactions = Interactions.user_interactions([recent_uploads, recent_faves], current_user) @@ -95,7 +98,38 @@ defmodule PhilomenaWeb.ProfileController do recent_comments: recent_comments, recent_posts: recent_posts, recent_galleries: recent_galleries, + statistics: statistics, layout_class: "layout--wide" ) end + + defp calculate_statistics(user) do + now = + DateTime.utc_now() + |> DateTime.to_unix(:second) + |> div(86400) + + last_90 = + UserStatistic + |> where(user_id: ^user.id) + |> where([us], us.day > ^(now - 89)) + |> Repo.all() + |> Map.new(&{&1.day, &1}) + + %{ + uploads: individual_stat(last_90, :uploads), + images_favourited: individual_stat(last_90, :images_favourited), + comments_posted: individual_stat(last_90, :comments_posted), + votes_cast: individual_stat(last_90, :votes_cast), + metadata_updates: individual_stat(last_90, :metadata_updates), + forum_posts: individual_stat(last_90, :forum_posts) + } + end + + defp individual_stat(mapping, stat_name) do + Enum.map((89..0), &map_fetch(mapping[&1], stat_name) || 0) + end + + defp map_fetch(nil, _field_name), do: nil + defp map_fetch(map, field_name), do: Map.get(map, field_name) end diff --git a/lib/philomena_web/templates/profile/_statistics.html.slime b/lib/philomena_web/templates/profile/_statistics.html.slime new file mode 100644 index 00000000..b9b40ab3 --- /dev/null +++ b/lib/philomena_web/templates/profile/_statistics.html.slime @@ -0,0 +1,28 @@ +.block + .block__header: span.block__header__title Statistics + .block__content + table.table.table--stats.center + tr + td.table--stats__parameter Uploads + td.table--stats__value = number_with_delimiter(@user.uploads_count) + td.table--stats__sparkline: .sparkline = sparkline_data(@statistics.uploads) + tr + td.table--stats__parameter Favorites + td.table--stats__value = number_with_delimiter(@user.images_favourited_count) + td.table--stats__sparkline: .sparkline = sparkline_data(@statistics.images_favourited) + tr + td.table--stats__parameter Comments + td.table--stats__value = number_with_delimiter(@user.comments_posted_count) + td.table--stats__sparkline: .sparkline = sparkline_data(@statistics.comments_posted) + tr + td.table--stats__parameter Votes + td.table--stats__value = number_with_delimiter(@user.votes_cast_count) + td.table--stats__sparkline: .sparkline = sparkline_data(@statistics.votes_cast) + tr + td.table--stats__parameter Metadata Updates + td.table--stats__value = number_with_delimiter(@user.metadata_updates_count) + td.table--stats__sparkline: .sparkline = sparkline_data(@statistics.metadata_updates) + tr + td.table--stats__parameter Forum Posts + td.table--stats__value = number_with_delimiter(@user.forum_posts_count) + td.table--stats__sparkline: .sparkline = sparkline_data(@statistics.forum_posts) diff --git a/lib/philomena_web/templates/profile/show.html.slime b/lib/philomena_web/templates/profile/show.html.slime index 146b3eb6..88d7169f 100644 --- a/lib/philomena_web/templates/profile/show.html.slime +++ b/lib/philomena_web/templates/profile/show.html.slime @@ -51,6 +51,7 @@ = pretty_time(award.awarded_on) .column-layout__main + = render PhilomenaWeb.ProfileView, "_statistics.html", user: @user, statistics: @statistics, conn: @conn = render PhilomenaWeb.ProfileView, "_recent_images.html", title: "Recent Uploads", images: @recent_uploads, view_all_path: Routes.search_path(@conn, :index, q: "uploader_id:#{@user.id}"), conn: @conn = render PhilomenaWeb.ProfileView, "_recent_images.html", title: "Recent Favorites", images: @recent_faves, view_all_path: Routes.search_path(@conn, :index, q: "faved_by_id:#{@user.id}"), conn: @conn = render PhilomenaWeb.ProfileView, "_recent_galleries.html", galleries: @recent_galleries, user: @user, conn: @conn diff --git a/lib/philomena_web/views/app_view.ex b/lib/philomena_web/views/app_view.ex index d3dda133..dfa29b1f 100644 --- a/lib/philomena_web/views/app_view.ex +++ b/lib/philomena_web/views/app_view.ex @@ -64,6 +64,7 @@ defmodule PhilomenaWeb.AppView do Canada.Can.can?(conn.assigns.current_user, action, model) end + def number_with_delimiter(nil), do: "0" def number_with_delimiter(number) do number |> to_charlist() diff --git a/lib/philomena_web/views/profile_view.ex b/lib/philomena_web/views/profile_view.ex index 1b530ed5..48b2dd12 100644 --- a/lib/philomena_web/views/profile_view.ex +++ b/lib/philomena_web/views/profile_view.ex @@ -17,6 +17,33 @@ defmodule PhilomenaWeb.ProfileView do def award_title(award), do: award.badge_name + def sparkline_data(data) do + # Normalize range + {min, max} = Enum.min_max(data) + max = max(max, 0) + min = max(min, 0) + + content_tag :svg, [width: "100%", preserveAspectRatio: "none", viewBox: "0 0 90 20"] do + for {val, i} <- Enum.with_index(data) do + # Filter out negative values + calc = max(val, 0) + + # Lerp or 0 if not present + height = zero_div((calc - min) * 20, max - min) + + # In SVG coords, y grows down + y = 20 - height + + content_tag :rect, [class: "barline__bar", x: i, y: y, width: 1, height: height] do + content_tag :title, val + end + end + end + end + + defp zero_div(_num, 0), do: 0 + defp zero_div(num, den), do: div(num, den) + defp badge_url_root do Application.get_env(:philomena, :badge_url_root) end