# frozen_string_literal: true class IncomingLinksReport attr_accessor :type, :data, :y_titles, :start_date, :end_date, :limit, :category_id, :include_subcategories def initialize(type) @type = type @y_titles = {} @data = nil @category_id = nil @include_subcategories = false end def as_json(_options = nil) { type: self.type, title: I18n.t("reports.#{self.type}.title"), xaxis: I18n.t("reports.#{self.type}.xaxis"), ytitles: self.y_titles, data: self.data, start_date: start_date, end_date: end_date, } end def self.find(type, _opts = {}) report_method = :"report_#{type}" return nil unless respond_to?(report_method) # Load the report report = IncomingLinksReport.new(type) report.start_date = _opts[:start_date] || 30.days.ago report.end_date = _opts[:end_date] || Time.now.end_of_day report.limit = _opts[:limit].to_i if _opts[:limit] report.category_id = _opts[:category_id] if _opts[:category_id] report.include_subcategories = _opts[:include_subcategories] if _opts[:include_subcategories] public_send(report_method, report) report end # Return top 10 users who brought traffic to the site within the last 30 days def self.report_top_referrers(report) report.y_titles[:num_clicks] = I18n.t("reports.#{report.type}.num_clicks") report.y_titles[:num_topics] = I18n.t("reports.#{report.type}.num_topics") 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, ) user_id_lookup = User .where(username: num_clicks.keys) .select(:id, :username, :uploaded_avatar_id) .inject({}) do |sum, v| sum[v.username] = { id: v.id, user_avatar_template: User.avatar_template(v.username, v.uploaded_avatar_id), } sum end report.data = [] num_clicks.each_key do |username| 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], } end report.data = report.data.sort_by { |x| x[:num_clicks] }.reverse[0, 10] end def self.per_user(start_date:, end_date:, category_id:, include_subcategories:) public_incoming_links(category_id: category_id, include_subcategories: include_subcategories) .where( "incoming_links.created_at > ? AND incoming_links.created_at < ? AND incoming_links.user_id IS NOT NULL", start_date, end_date, ) .joins(:user) .group("users.username") end 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 end 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") end # Return top 10 domains that brought traffic to the site within the last 30 days def self.report_top_traffic_sources(report) report.y_titles[:num_clicks] = I18n.t("reports.#{report.type}.num_clicks") report.y_titles[:num_topics] = I18n.t("reports.#{report.type}.num_topics") report.y_titles[:num_users] = I18n.t("reports.#{report.type}.num_users") 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, ) report.data = [] num_clicks.each_key do |domain| report.data << { domain: domain, num_clicks: num_clicks[domain], num_topics: num_topics[domain], } end report.data = report.data.sort_by { |x| x[:num_clicks] }.reverse[0, 10] end 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) .where( "incoming_links.created_at > ? AND incoming_links.created_at < ?", start_date, end_date, ) .joins(incoming_referer: :incoming_domain) .group("incoming_domains.name") .order("count_all DESC") .limit(limit) .count end def self.per_domain(domains, options = {}) public_incoming_links( category_id: options[:category_id], include_subcategories: options[:include_subcategories], ) .joins(incoming_referer: :incoming_domain) .where("incoming_links.created_at > ? AND incoming_domains.name IN (?)", 30.days.ago, domains) .group("incoming_domains.name") end def self.topic_count_per_domain(domains, options = {}) # COUNT(DISTINCT) is slow per_domain(domains, options).count("DISTINCT posts.topic_id") end def self.report_top_referred_topics(report) report.y_titles[:num_clicks] = I18n.t("reports.#{report.type}.labels.num_clicks") 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, ) num_clicks = num_clicks.to_a.sort_by { |x| x[1] }.last(report.limit || 10).reverse report.data = [] topics = Topic.select("id, slug, title").where("id in (?)", num_clicks.map { |z| z[0] }) if report.category_id topics = topics.where( category_id: ( if report.include_subcategories Category.subcategory_ids(report.category_id) else report.category_id end ), ) end num_clicks.each do |topic_id, num_clicks_element| topic = topics.find { |t| t.id == topic_id } if topic report.data << { topic_id: topic_id, topic_title: topic.title, topic_url: topic.relative_url, num_clicks: num_clicks_element, } end end report.data end 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) .where( "incoming_links.created_at > ? AND incoming_links.created_at < ? AND topic_id IS NOT NULL", start_date, end_date, ) .group("topic_id") .count end def self.public_incoming_links(category_id: nil, include_subcategories: nil) links = IncomingLink.joins(post: :topic).where("topics.archetype = ?", Archetype.default) 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 end end