2019-05-02 17:17:27 -05:00
|
|
|
# frozen_string_literal: true
|
|
|
|
|
2013-05-01 17:12:02 -05:00
|
|
|
class IncomingLinksReport
|
|
|
|
|
2020-04-22 03:52:50 -05:00
|
|
|
attr_accessor :type, :data, :y_titles, :start_date, :end_date, :limit, :category_id, :include_subcategories
|
2013-05-01 17:12:02 -05:00
|
|
|
|
|
|
|
def initialize(type)
|
|
|
|
@type = type
|
|
|
|
@y_titles = {}
|
|
|
|
@data = nil
|
2018-08-09 19:50:05 -05:00
|
|
|
@category_id = nil
|
2020-04-22 03:52:50 -05:00
|
|
|
@include_subcategories = false
|
2013-05-01 17:12:02 -05:00
|
|
|
end
|
|
|
|
|
2014-08-20 13:14:19 -05:00
|
|
|
def as_json(_options = nil)
|
2013-05-01 17:12:02 -05:00
|
|
|
{
|
|
|
|
type: self.type,
|
|
|
|
title: I18n.t("reports.#{self.type}.title"),
|
|
|
|
xaxis: I18n.t("reports.#{self.type}.xaxis"),
|
|
|
|
ytitles: self.y_titles,
|
2018-05-15 00:08:23 -05:00
|
|
|
data: self.data,
|
2018-07-19 13:33:11 -05:00
|
|
|
start_date: start_date,
|
|
|
|
end_date: end_date
|
2013-05-01 17:12:02 -05:00
|
|
|
}
|
|
|
|
end
|
|
|
|
|
2014-08-20 13:14:19 -05:00
|
|
|
def self.find(type, _opts = {})
|
2013-05-01 17:12:02 -05:00
|
|
|
report_method = :"report_#{type}"
|
|
|
|
return nil unless respond_to?(report_method)
|
|
|
|
|
|
|
|
# Load the report
|
|
|
|
report = IncomingLinksReport.new(type)
|
2018-05-15 00:08:23 -05:00
|
|
|
|
|
|
|
report.start_date = _opts[:start_date] || 30.days.ago
|
2018-07-19 13:33:11 -05:00
|
|
|
report.end_date = _opts[:end_date] || Time.now.end_of_day
|
2018-05-15 00:08:23 -05:00
|
|
|
report.limit = _opts[:limit].to_i if _opts[:limit]
|
2018-08-09 19:50:05 -05:00
|
|
|
report.category_id = _opts[:category_id] if _opts[:category_id]
|
2020-04-22 03:52:50 -05:00
|
|
|
report.include_subcategories = _opts[:include_subcategories] if _opts[:include_subcategories]
|
2018-05-15 00:08:23 -05:00
|
|
|
|
2019-05-06 20:57:55 -05:00
|
|
|
public_send(report_method, report)
|
2013-05-01 17:12:02 -05:00
|
|
|
report
|
|
|
|
end
|
|
|
|
|
|
|
|
# Return top 10 users who brought traffic to the site within the last 30 days
|
|
|
|
def self.report_top_referrers(report)
|
2017-07-27 20:20:09 -05:00
|
|
|
report.y_titles[:num_clicks] = I18n.t("reports.#{report.type}.num_clicks")
|
2013-05-01 17:12:02 -05:00
|
|
|
report.y_titles[:num_topics] = I18n.t("reports.#{report.type}.num_topics")
|
|
|
|
|
2020-04-22 03:52:50 -05:00
|
|
|
num_clicks = link_count_per_user(start_date: report.start_date, end_date: report.end_date, category_id: report.category_id, include_subcategories: report.include_subcategories)
|
|
|
|
num_topics = topic_count_per_user(start_date: report.start_date, end_date: report.end_date, category_id: report.category_id, include_subcategories: report.include_subcategories)
|
2018-11-19 05:20:05 -06:00
|
|
|
user_id_lookup = User
|
|
|
|
.where(username: num_clicks.keys)
|
|
|
|
.select(:id, :username, :uploaded_avatar_id)
|
|
|
|
.inject({}) { |sum, v|
|
|
|
|
sum[v.username] = {
|
|
|
|
id: v.id,
|
|
|
|
user_avatar_template: User.avatar_template(v.username, v.uploaded_avatar_id)
|
|
|
|
}
|
|
|
|
sum
|
|
|
|
}
|
|
|
|
|
2013-05-01 17:12:02 -05:00
|
|
|
report.data = []
|
2015-04-18 06:53:53 -05:00
|
|
|
num_clicks.each_key do |username|
|
2018-11-19 05:20:05 -06:00
|
|
|
report.data << {
|
|
|
|
username: username,
|
|
|
|
user_id: user_id_lookup[username][:id],
|
|
|
|
user_avatar_template: user_id_lookup[username][:user_avatar_template],
|
|
|
|
num_clicks: num_clicks[username],
|
|
|
|
num_topics: num_topics[username]
|
|
|
|
}
|
2013-05-01 17:12:02 -05:00
|
|
|
end
|
2017-07-27 20:20:09 -05:00
|
|
|
report.data = report.data.sort_by { |x| x[:num_clicks] }.reverse[0, 10]
|
2013-05-01 17:12:02 -05:00
|
|
|
end
|
|
|
|
|
2020-04-22 03:52:50 -05:00
|
|
|
def self.per_user(start_date:, end_date:, category_id:, include_subcategories:)
|
|
|
|
public_incoming_links(category_id: category_id, include_subcategories: include_subcategories)
|
2018-07-19 13:33:11 -05:00
|
|
|
.where('incoming_links.created_at > ? AND incoming_links.created_at < ? AND incoming_links.user_id IS NOT NULL', start_date, end_date)
|
2017-07-27 20:20:09 -05:00
|
|
|
.joins(:user)
|
|
|
|
.group('users.username')
|
2013-05-01 17:12:02 -05:00
|
|
|
end
|
|
|
|
|
2020-04-22 03:52:50 -05:00
|
|
|
def self.link_count_per_user(start_date:, end_date:, category_id:, include_subcategories:)
|
|
|
|
per_user(start_date: start_date, end_date: end_date, category_id: category_id, include_subcategories: include_subcategories).count
|
2013-05-01 17:12:02 -05:00
|
|
|
end
|
|
|
|
|
2020-04-22 03:52:50 -05:00
|
|
|
def self.topic_count_per_user(start_date:, end_date:, category_id:, include_subcategories:)
|
|
|
|
per_user(start_date: start_date, end_date: end_date, category_id: category_id, include_subcategories: include_subcategories).joins(:post).count("DISTINCT posts.topic_id")
|
2013-05-01 17:12:02 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
# Return top 10 domains that brought traffic to the site within the last 30 days
|
|
|
|
def self.report_top_traffic_sources(report)
|
2017-07-27 20:20:09 -05:00
|
|
|
report.y_titles[:num_clicks] = I18n.t("reports.#{report.type}.num_clicks")
|
2013-05-01 17:12:02 -05:00
|
|
|
report.y_titles[:num_topics] = I18n.t("reports.#{report.type}.num_topics")
|
|
|
|
report.y_titles[:num_users] = I18n.t("reports.#{report.type}.num_users")
|
|
|
|
|
2020-04-22 03:52:50 -05:00
|
|
|
num_clicks = link_count_per_domain(start_date: report.start_date, end_date: report.end_date, category_id: report.category_id, include_subcategories: report.include_subcategories)
|
|
|
|
num_topics = topic_count_per_domain(num_clicks.keys, category_id: report.category_id, include_subcategories: report.include_subcategories)
|
2013-05-01 17:12:02 -05:00
|
|
|
report.data = []
|
2015-04-18 06:53:53 -05:00
|
|
|
num_clicks.each_key do |domain|
|
2017-07-27 20:20:09 -05:00
|
|
|
report.data << { domain: domain, num_clicks: num_clicks[domain], num_topics: num_topics[domain] }
|
2013-05-01 17:12:02 -05:00
|
|
|
end
|
2017-07-27 20:20:09 -05:00
|
|
|
report.data = report.data.sort_by { |x| x[:num_clicks] }.reverse[0, 10]
|
2013-05-01 17:12:02 -05:00
|
|
|
end
|
|
|
|
|
2020-04-22 03:52:50 -05:00
|
|
|
def self.link_count_per_domain(limit: 10, start_date:, end_date:, category_id:, include_subcategories:)
|
|
|
|
public_incoming_links(category_id: category_id, include_subcategories: include_subcategories)
|
2018-07-19 13:33:11 -05:00
|
|
|
.where('incoming_links.created_at > ? AND incoming_links.created_at < ?', start_date, end_date)
|
2017-07-27 20:20:09 -05:00
|
|
|
.joins(incoming_referer: :incoming_domain)
|
|
|
|
.group('incoming_domains.name')
|
|
|
|
.order('count_all DESC')
|
2018-05-16 09:55:54 -05:00
|
|
|
.limit(limit)
|
|
|
|
.count
|
2013-05-01 17:12:02 -05:00
|
|
|
end
|
|
|
|
|
2018-08-09 19:50:05 -05:00
|
|
|
def self.per_domain(domains, options = {})
|
2020-04-22 03:52:50 -05:00
|
|
|
public_incoming_links(category_id: options[:category_id], include_subcategories: options[:include_subcategories])
|
2017-07-27 20:20:09 -05:00
|
|
|
.joins(incoming_referer: :incoming_domain)
|
|
|
|
.where('incoming_links.created_at > ? AND incoming_domains.name IN (?)', 30.days.ago, domains)
|
|
|
|
.group('incoming_domains.name')
|
2013-05-01 17:12:02 -05:00
|
|
|
end
|
|
|
|
|
2018-08-09 19:50:05 -05:00
|
|
|
def self.topic_count_per_domain(domains, options = {})
|
2013-07-31 13:57:31 -05:00
|
|
|
# COUNT(DISTINCT) is slow
|
2018-08-09 19:50:05 -05:00
|
|
|
per_domain(domains, options).count("DISTINCT posts.topic_id")
|
2013-05-01 17:12:02 -05:00
|
|
|
end
|
|
|
|
|
|
|
|
def self.report_top_referred_topics(report)
|
2018-11-09 10:18:19 -06:00
|
|
|
report.y_titles[:num_clicks] = I18n.t("reports.#{report.type}.labels.num_clicks")
|
2020-04-22 03:52:50 -05:00
|
|
|
num_clicks = link_count_per_topic(start_date: report.start_date, end_date: report.end_date, category_id: report.category_id, include_subcategories: report.include_subcategories)
|
2018-05-15 00:08:23 -05:00
|
|
|
num_clicks = num_clicks.to_a.sort_by { |x| x[1] }.last(report.limit || 10).reverse
|
2013-05-01 17:12:02 -05:00
|
|
|
report.data = []
|
2017-07-27 20:20:09 -05:00
|
|
|
topics = Topic.select('id, slug, title').where('id in (?)', num_clicks.map { |z| z[0] })
|
2020-04-22 03:52:50 -05:00
|
|
|
if report.category_id
|
|
|
|
topics = topics.where(category_id: report.include_subcategories ? Category.subcategory_ids(report.category_id) : report.category_id)
|
|
|
|
end
|
2013-06-10 00:08:10 -05:00
|
|
|
num_clicks.each do |topic_id, num_clicks_element|
|
2017-07-27 20:20:09 -05:00
|
|
|
topic = topics.find { |t| t.id == topic_id }
|
2013-05-01 18:10:31 -05:00
|
|
|
if topic
|
2018-01-15 16:28:35 -06:00
|
|
|
report.data << { topic_id: topic_id, topic_title: topic.title, topic_url: topic.relative_url, num_clicks: num_clicks_element }
|
2013-05-01 18:10:31 -05:00
|
|
|
end
|
2013-05-01 17:12:02 -05:00
|
|
|
end
|
2013-05-01 18:10:31 -05:00
|
|
|
report.data
|
2013-05-01 17:12:02 -05:00
|
|
|
end
|
|
|
|
|
2020-04-22 03:52:50 -05:00
|
|
|
def self.link_count_per_topic(start_date:, end_date:, category_id:, include_subcategories:)
|
|
|
|
public_incoming_links(category_id: category_id, include_subcategories: include_subcategories)
|
2018-07-19 13:33:11 -05:00
|
|
|
.where('incoming_links.created_at > ? AND incoming_links.created_at < ? AND topic_id IS NOT NULL', start_date, end_date)
|
2017-07-27 20:20:09 -05:00
|
|
|
.group('topic_id')
|
|
|
|
.count
|
2013-05-01 17:12:02 -05:00
|
|
|
end
|
2018-05-16 09:55:54 -05:00
|
|
|
|
2020-04-22 03:52:50 -05:00
|
|
|
def self.public_incoming_links(category_id: nil, include_subcategories: nil)
|
|
|
|
links = IncomingLink
|
2018-05-16 09:55:54 -05:00
|
|
|
.joins(post: :topic)
|
|
|
|
.where("topics.archetype = ?", Archetype.default)
|
2020-04-22 03:52:50 -05:00
|
|
|
|
|
|
|
if category_id
|
|
|
|
if include_subcategories
|
|
|
|
links = links.where("topics.category_id IN (?)", Category.subcategory_ids(category_id))
|
|
|
|
else
|
|
|
|
links = links.where("topics.category_id = ?", category_id)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
links
|
2018-05-16 09:55:54 -05:00
|
|
|
end
|
2013-08-25 16:18:11 -05:00
|
|
|
end
|