class AnalyticsService def initialize(user_or_org, start_date: "", end_date: "", article_id: nil) @user_or_org = user_or_org @article_id = article_id @start_date = Time.zone.parse(start_date.to_s)&.beginning_of_day @end_date = Time.zone.parse(end_date.to_s)&.end_of_day || Time.current.end_of_day load_data end def totals total_views = article_data.sum(:page_views_count) logged_in_page_view_data = page_view_data.where.not(user_id: nil) average_read_time_in_seconds = average_read_time(logged_in_page_view_data) { comments: { total: comment_data.size }, reactions: { total: reaction_data.size, like: reaction_data.count { |rxn| rxn.category == "like" }, readinglist: reaction_data.count { |rxn| rxn.category == "readinglist" }, unicorn: reaction_data.count { |rxn| rxn.category == "unicorn" } }, follows: { total: follow_data.size }, page_views: { total: total_views, average_read_time_in_seconds: average_read_time_in_seconds, total_read_time_in_seconds: average_read_time_in_seconds * total_views } } end def stats_grouped_by_day result = {} (start_date.to_date..end_date.to_date).each do |date| result[date.to_s(:iso)] = cached_data_for_date(date) end result end private attr_reader :user_or_org, :start_date, :end_date, :article_data, :reaction_data, :comment_data, :follow_data, :page_view_data def load_data @article_data = Article.published.where("#{user_or_org.class.name.downcase}_id" => user_or_org.id) if @article_id @article_data = @article_data.where(id: @article_id) raise UnauthorizedError if @article_data.blank? article_ids = @article_id else article_ids = @article_data.pluck(:id) end if @start_date && @end_date @reaction_data = Reaction.where(reactable_id: article_ids, reactable_type: "Article"). where(created_at: @start_date..@end_date). where("points > 0") @comment_data = Comment.where(commentable_id: article_ids, commentable_type: "Article"). where(created_at: @start_date..@end_date). where("score > 0") @page_view_data = PageView.where(article_id: article_ids).where(created_at: @start_date..@end_date) else @reaction_data = Reaction.where(reactable_id: article_ids, reactable_type: "Article").where("points > 0") @comment_data = Comment.where(commentable_id: article_ids, commentable_type: "Article").where("score > 0") @page_view_data = PageView.where(article_id: article_ids) end @follow_data = Follow.where(followable_type: user_or_org.class.name, followable_id: user_or_org.id) end def average_read_time(page_view_data) page_view_data.size.positive? ? page_view_data.sum(:time_tracked_in_seconds) / page_view_data.size : 0 end def cached_data_for_date(date) expiration_date = if date == Time.current.to_date 30.minutes else 7.days end Rails.cache.fetch("analytics-for-date-#{date}-#{user_or_org.class.name}-#{user_or_org.id}", expires_in: expiration_date) do reaction_data_of_date = reaction_data.where("date(created_at) = ?", date) logged_in_page_view_data = page_view_data.where("date(created_at) = ?", date).where.not(user_id: nil) average_read_time_in_seconds = average_read_time(logged_in_page_view_data) total_views = page_view_data.where("date(created_at) = ?", date).sum(:counts_for_number_of_views) { comments: { total: comment_data.where("date(created_at) = ?", date).size }, reactions: { total: reaction_data_of_date.size, like: reaction_data_of_date.where("category = ?", "like").size, readinglist: reaction_data_of_date.where("category = ?", "readinglist").size, unicorn: reaction_data_of_date.where("category = ?", "unicorn").size }, page_views: { total: total_views, total_read_time_in_seconds: average_read_time_in_seconds * total_views, average_read_time_in_seconds: average_read_time_in_seconds }, follows: { total: follow_data.where("date(created_at) = ?", date).size } } end end end