otwarchive-symphonyarchive/app/controllers/stats_controller.rb

131 lines
4.6 KiB
Ruby
Raw Permalink Normal View History

2026-03-11 22:22:11 +00:00
class StatsController < ApplicationController
before_action :users_only
before_action :load_user
before_action :check_ownership
# only the current user
def load_user
@user = current_user
@check_ownership_of = @user
end
# gather statistics for the user on all their works
def index
user_works = Work.joins(pseuds: :user).where(users: { id: @user.id }).where(posted: true)
user_chapters = Chapter.joins(pseuds: :user).where(users: { id: @user.id }).where(posted: true)
work_query = user_works
.joins(:taggings)
.joins("inner join tags on taggings.tagger_id = tags.id AND tags.type = 'Fandom'")
.select("distinct tags.name as fandom, works.id as id, works.title as title")
# sort
# NOTE: Because we are going to be eval'ing the @sort variable later we MUST make sure that its content is
# checked against the allowlist of valid options
sort_options = %w[hits date kudos.count comment_thread_count bookmarks.count subscriptions.count word_count]
@sort = sort_options.include?(params[:sort_column]) ? params[:sort_column] : "hits"
@dir = params[:sort_direction] == "ASC" ? "ASC" : "DESC"
params[:sort_column] = @sort
params[:sort_direction] = @dir
# gather works and sort by specified count
@years = ["All Years"] + user_chapters.pluck(:published_at).map { |date| date.year.to_s }
.uniq.sort
@current_year = @years.include?(params[:year]) ? params[:year] : "All Years"
if @current_year == "All Years"
work_query = work_query.select("works.revised_at as date, works.word_count as word_count")
else
next_year = @current_year.to_i + 1
start_date = DateTime.parse("01/01/#{@current_year}")
end_date = DateTime.parse("01/01/#{next_year}")
work_query = work_query
.joins(:chapters)
.where("chapters.posted = 1 AND chapters.published_at >= ? AND chapters.published_at < ?", start_date, end_date)
.select("CONVERT(MAX(chapters.published_at), datetime) as date, SUM(chapters.word_count) as word_count")
.group(:id, :fandom)
end
works = work_query.all.sort_by { |w| @dir == "ASC" ? (stat_element(w, @sort) || 0) : (0 - (stat_element(w, @sort) || 0).to_i) }
# on the off-chance a new user decides to look at their stats and have no works
render "no_stats" and return if works.blank?
# group by fandom or flat view
if params[:flat_view]
@works = {ts("All Fandoms") => works.uniq}
else
@works = works.group_by(&:fandom)
end
# gather totals for all works
@totals = {}
(sort_options - ["date"]).each do |value|
# the inject is used to collect the sum in the "result" variable as we iterate over all the works
@totals[value.split(".")[0].to_sym] = works.uniq.inject(0) { |result, work| result + (stat_element(work, value) || 0) } # sum the works
end
@totals[:user_subscriptions] = Subscription.where(subscribable_id: @user.id, subscribable_type: 'User').count
# graph top 5 works
@chart_data = GoogleVisualr::DataTable.new
@chart_data.new_column('string', 'Title')
chart_col = @sort == "date" ? "hits" : @sort
chart_col_title = chart_col.split(".")[0].titleize == "Comments" ? ts("Comment Threads") : chart_col.split(".")[0].titleize
if @sort == "date" && @dir == "ASC"
chart_title = ts("Oldest")
elsif @sort == "date" && @dir == "DESC"
chart_title = ts("Most Recent")
elsif @dir == "ASC"
chart_title = ts("Bottom Five By #{chart_col_title}")
else
chart_title = ts("Top Five By #{chart_col_title}")
end
@chart_data.new_column('number', chart_col_title)
# Add Rows and Values
@chart_data.add_rows(works.uniq[0..4].map { |w| [w.title, stat_element(w, chart_col)] })
# image version of bar chart
# opts from here: http://code.google.com/apis/chart/image/docs/gallery/bar_charts.html
@image_chart = GoogleVisualr::Image::BarChart.new(@chart_data, {isVertical: true}).uri({
chtt: chart_title,
chs: "800x350",
chbh: "a",
chxt: "x",
chm: "N,000000,0,-1,11"
})
options = {
colors: ["#993333"],
title: chart_title,
vAxis: {
viewWindow: { min: 0 }
}
}
@chart = GoogleVisualr::Interactive::ColumnChart.new(@chart_data, options)
end
private
def stat_element(work, element)
case element.downcase
when "date"
work.date
when "hits"
work.hits
when "kudos.count"
work.kudos.count
when "comment_thread_count"
work.comment_thread_count
when "bookmarks.count"
work.bookmarks.count
when "subscriptions.count"
work.subscriptions.count
when "word_count"
work.word_count
end
end
end