docbrown/app/services/analytics_service.rb
rhymes 5e9867ec56 Simplify AnalyticsService code (#2308)
* Add basic tests for Analytics service

* User/Organization are always required

* Add some data to the test

* Use queries and date times

* Fix AnalyticsService interface in the controller

* s/HTTP_API_KEY/api-key

* Defend against nils and support datetime objects

* Update app/services/analytics_service.rb

Co-Authored-By: rhymes <rhymesete@gmail.com>

* Use SQL sum, not Rails sum

* Group average_read_time

* Use SQL, not Rails

* Use ISO date

* Fix specs
2019-04-04 16:07:57 -04:00

97 lines
3.7 KiB
Ruby

class AnalyticsService
def initialize(user_or_org, start_date: "", end_date: "")
@user_or_org = user_or_org
@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|
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)
result[date.to_s(:iso)] = {
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
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.where("#{user_or_org.class.name.downcase}_id" => user_or_org.id, published: true)
article_ids = @article_data.pluck(:id)
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
end