FEATURE: Make report filters reusable (#9444)
This commit also adds 'include subcategories' report filter
This commit is contained in:
parent
a511bea4cc
commit
e733701887
|
@ -7,7 +7,6 @@ import Component from "@ember/component";
|
|||
import ReportLoader from "discourse/lib/reports-loader";
|
||||
import { exportEntity } from "discourse/lib/export-csv";
|
||||
import { outputExportResult } from "discourse/lib/export-result";
|
||||
import { isNumeric } from "discourse/lib/utilities";
|
||||
import Report, { SCHEMA_VERSION } from "admin/models/report";
|
||||
import ENV from "discourse-common/config/environment";
|
||||
|
||||
|
@ -167,10 +166,9 @@ export default Component.extend({
|
|||
ENV.environment === "test" ? "end" : endDate.replace(/-/g, ""),
|
||||
"[:prev_period]",
|
||||
this.get("reportOptions.table.limit"),
|
||||
// Convert all filter values to strings to ensure unique serialization
|
||||
customFilters
|
||||
? JSON.stringify(customFilters, (key, value) =>
|
||||
isNumeric(value) ? value.toString() : value
|
||||
)
|
||||
? JSON.stringify(customFilters, (k, v) => (k ? `${v}` : v))
|
||||
: null,
|
||||
SCHEMA_VERSION
|
||||
]
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
import { action } from "@ember/object";
|
||||
import FilterComponent from "admin/components/report-filters/filter";
|
||||
|
||||
export default FilterComponent.extend({
|
||||
checked: false,
|
||||
|
||||
didReceiveAttrs() {
|
||||
this._super(...arguments);
|
||||
this.set("checked", !!this.filter.default);
|
||||
},
|
||||
|
||||
@action
|
||||
onChange() {
|
||||
this.applyFilter(this.filter.id, !this.checked || undefined);
|
||||
}
|
||||
});
|
|
@ -1,16 +1,12 @@
|
|||
import { action } from "@ember/object";
|
||||
import { readOnly } from "@ember/object/computed";
|
||||
import FilterComponent from "admin/components/report-filters/filter";
|
||||
|
||||
export default FilterComponent.extend({
|
||||
classNames: ["category-filter"],
|
||||
|
||||
layoutName: "admin/templates/components/report-filters/category",
|
||||
|
||||
category: readOnly("filter.default"),
|
||||
|
||||
actions: {
|
||||
onChange(categoryId) {
|
||||
this.applyFilter(this.get("filter.id"), categoryId || undefined);
|
||||
}
|
||||
@action
|
||||
onChange(categoryId) {
|
||||
this.applyFilter(this.filter.id, categoryId || undefined);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,7 +0,0 @@
|
|||
import FilterComponent from "admin/components/report-filters/filter";
|
||||
|
||||
export default FilterComponent.extend({
|
||||
classNames: ["file-extension-filter"],
|
||||
|
||||
layoutName: "admin/templates/components/report-filters/file-extension"
|
||||
});
|
|
@ -1,8 +1,9 @@
|
|||
import Component from "@ember/component";
|
||||
import { action } from "@ember/object";
|
||||
|
||||
export default Component.extend({
|
||||
actions: {
|
||||
onChange(value) {
|
||||
this.applyFilter(this.get("filter.id"), value);
|
||||
}
|
||||
@action
|
||||
onChange(value) {
|
||||
this.applyFilter(this.filter.id, value);
|
||||
}
|
||||
});
|
||||
|
|
|
@ -1,20 +1,18 @@
|
|||
import { computed } from "@ember/object";
|
||||
import FilterComponent from "admin/components/report-filters/filter";
|
||||
import discourseComputed from "discourse-common/utils/decorators";
|
||||
|
||||
export default FilterComponent.extend({
|
||||
classNames: ["group-filter"],
|
||||
|
||||
layoutName: "admin/templates/components/report-filters/group",
|
||||
|
||||
@discourseComputed()
|
||||
groupOptions() {
|
||||
@computed
|
||||
get groupOptions() {
|
||||
return (this.site.groups || []).map(group => {
|
||||
return { name: group["name"], value: group["id"] };
|
||||
});
|
||||
},
|
||||
|
||||
@discourseComputed("filter.default")
|
||||
groupId(filterDefault) {
|
||||
return filterDefault ? parseInt(filterDefault, 10) : null;
|
||||
@computed("filter.default")
|
||||
get groupId() {
|
||||
return this.filter.default ? parseInt(this.filter.default, 10) : null;
|
||||
}
|
||||
});
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
import FilterComponent from "admin/components/report-filters/filter";
|
||||
|
||||
export default FilterComponent.extend();
|
|
@ -3,7 +3,7 @@ import { ajax } from "discourse/lib/ajax";
|
|||
|
||||
export default DiscourseRoute.extend({
|
||||
model() {
|
||||
return ajax("/admin/reports").then(json => json);
|
||||
return ajax("/admin/reports");
|
||||
},
|
||||
|
||||
setupController(controller, model) {
|
||||
|
|
|
@ -165,7 +165,8 @@
|
|||
|
||||
<div class="input">
|
||||
{{component
|
||||
(concat "report-filters/" filter.id)
|
||||
(concat "report-filters/" filter.type)
|
||||
model=model
|
||||
filter=filter
|
||||
applyFilter=(action "applyFilter")}}
|
||||
</div>
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
{{input
|
||||
type="checkbox"
|
||||
checked=checked
|
||||
click=(action "onChange")
|
||||
}}
|
|
@ -1,7 +1,5 @@
|
|||
{{search-advanced-category-chooser
|
||||
value=category
|
||||
onChange=(action "onChange")
|
||||
options=(hash
|
||||
filterable=true
|
||||
)
|
||||
options=(hash filterable=true)
|
||||
}}
|
||||
|
|
|
@ -2,13 +2,14 @@
|
|||
|
||||
class IncomingLinksReport
|
||||
|
||||
attr_accessor :type, :data, :y_titles, :start_date, :end_date, :limit, :category_id
|
||||
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)
|
||||
|
@ -34,6 +35,7 @@ class IncomingLinksReport
|
|||
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
|
||||
|
@ -44,8 +46,8 @@ class IncomingLinksReport
|
|||
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)
|
||||
num_topics = topic_count_per_user(start_date: report.start_date, end_date: report.end_date, category_id: report.category_id)
|
||||
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)
|
||||
|
@ -70,19 +72,19 @@ class IncomingLinksReport
|
|||
report.data = report.data.sort_by { |x| x[:num_clicks] }.reverse[0, 10]
|
||||
end
|
||||
|
||||
def self.per_user(start_date:, end_date:, category_id:)
|
||||
public_incoming_links(category_id: category_id)
|
||||
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:)
|
||||
per_user(start_date: start_date, end_date: end_date, category_id: category_id).count
|
||||
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:)
|
||||
per_user(start_date: start_date, end_date: end_date, category_id: category_id).joins(:post).count("DISTINCT posts.topic_id")
|
||||
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
|
||||
|
@ -91,8 +93,8 @@ class IncomingLinksReport
|
|||
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)
|
||||
num_topics = topic_count_per_domain(num_clicks.keys, category_id: report.category_id)
|
||||
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] }
|
||||
|
@ -100,8 +102,8 @@ class IncomingLinksReport
|
|||
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:)
|
||||
public_incoming_links(category_id: category_id)
|
||||
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')
|
||||
|
@ -111,7 +113,7 @@ class IncomingLinksReport
|
|||
end
|
||||
|
||||
def self.per_domain(domains, options = {})
|
||||
public_incoming_links(category_id: options[:category_id])
|
||||
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')
|
||||
|
@ -124,11 +126,13 @@ class IncomingLinksReport
|
|||
|
||||
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)
|
||||
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] })
|
||||
topics = topics.in_category_and_subcategories(report.category_id) if report.category_id
|
||||
if report.category_id
|
||||
topics = topics.where(category_id: report.include_subcategories ? Category.subcategory_ids(report.category_id) : report.category_id)
|
||||
end
|
||||
num_clicks.each do |topic_id, num_clicks_element|
|
||||
topic = topics.find { |t| t.id == topic_id }
|
||||
if topic
|
||||
|
@ -138,17 +142,26 @@ class IncomingLinksReport
|
|||
report.data
|
||||
end
|
||||
|
||||
def self.link_count_per_topic(start_date:, end_date:, category_id:)
|
||||
public_incoming_links(category_id: category_id)
|
||||
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)
|
||||
IncomingLink
|
||||
def self.public_incoming_links(category_id: nil, include_subcategories: nil)
|
||||
links = IncomingLink
|
||||
.joins(post: :topic)
|
||||
.where("topics.archetype = ?", Archetype.default)
|
||||
.merge(Topic.in_category_and_subcategories(category_id))
|
||||
|
||||
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
|
||||
|
|
|
@ -765,10 +765,19 @@ class Post < ActiveRecord::Base
|
|||
DiscourseEvent.trigger(:after_trigger_post_process, self)
|
||||
end
|
||||
|
||||
def self.public_posts_count_per_day(start_date, end_date, category_id = nil)
|
||||
result = public_posts.where('posts.created_at >= ? AND posts.created_at <= ?', start_date, end_date)
|
||||
def self.public_posts_count_per_day(start_date, end_date, category_id = nil, include_subcategories = false)
|
||||
result = public_posts
|
||||
.where('posts.created_at >= ? AND posts.created_at <= ?', start_date, end_date)
|
||||
.where(post_type: Post.types[:regular])
|
||||
result = result.where('topics.category_id IN (?)', Category.subcategory_ids(category_id.to_i)) if category_id
|
||||
|
||||
if category_id
|
||||
if include_subcategories
|
||||
result = result.where('topics.category_id IN (?)', Category.subcategory_ids(category_id))
|
||||
else
|
||||
result = result.where('topics.category_id = ?', category_id)
|
||||
end
|
||||
end
|
||||
|
||||
result
|
||||
.group('date(posts.created_at)')
|
||||
.order('date(posts.created_at)')
|
||||
|
|
|
@ -67,7 +67,13 @@ class PostAction < ActiveRecord::Base
|
|||
result = unscoped.where(post_action_type_id: post_action_type)
|
||||
result = result.where('post_actions.created_at >= ?', opts[:start_date] || (opts[:since_days_ago] || 30).days.ago)
|
||||
result = result.where('post_actions.created_at <= ?', opts[:end_date]) if opts[:end_date]
|
||||
result = result.joins(post: :topic).merge(Topic.in_category_and_subcategories(opts[:category_id])) if opts[:category_id]
|
||||
if opts[:category_id]
|
||||
if opts[:include_subcategories]
|
||||
result = result.joins(post: :topic).where('topics.category_id IN (?)', Category.subcategory_ids(opts[:category_id]))
|
||||
else
|
||||
result = result.joins(post: :topic).where('topics.category_id = ?', opts[:category_id])
|
||||
end
|
||||
end
|
||||
result.group('date(post_actions.created_at)')
|
||||
.order('date(post_actions.created_at)')
|
||||
.count
|
||||
|
|
|
@ -50,8 +50,8 @@ class Report
|
|||
end
|
||||
|
||||
def self.cache_key(report)
|
||||
(+"reports:") <<
|
||||
[
|
||||
"reports",
|
||||
report.type,
|
||||
report.start_date.to_date.strftime("%Y%m%d"),
|
||||
report.end_date.to_date.strftime("%Y%m%d"),
|
||||
|
@ -63,14 +63,30 @@ class Report
|
|||
end
|
||||
|
||||
def add_filter(name, options = {})
|
||||
default_filter = { allow_any: false, choices: [], default: nil }
|
||||
available_filters[name] = default_filter.merge(options)
|
||||
if options[:type].blank?
|
||||
options[:type] = name
|
||||
Discourse.deprecate("#{name} filter should define a `:type` option. Temporarily setting type to #{name}.")
|
||||
end
|
||||
|
||||
available_filters[name] = options
|
||||
end
|
||||
|
||||
def remove_filter(name)
|
||||
available_filters.delete(name)
|
||||
end
|
||||
|
||||
def add_category_filter
|
||||
category_id = filters[:category].to_i if filters[:category].present?
|
||||
add_filter('category', type: 'category', default: category_id)
|
||||
return if category_id.blank?
|
||||
|
||||
include_subcategories = filters[:'include-subcategories']
|
||||
include_subcategories = !!ActiveRecord::Type::Boolean.new.cast(include_subcategories)
|
||||
add_filter('include-subcategories', type: 'bool', default: include_subcategories)
|
||||
|
||||
[category_id, include_subcategories]
|
||||
end
|
||||
|
||||
def self.clear_cache(type = nil)
|
||||
pattern = type ? "reports:#{type}:*" : "reports:*"
|
||||
|
||||
|
@ -285,15 +301,22 @@ class Report
|
|||
end
|
||||
|
||||
def self.post_action_report(report, post_action_type)
|
||||
category_filter = report.filters.dig(:category)
|
||||
report.add_filter('category', default: category_filter)
|
||||
category_id, include_subcategories = report.add_category_filter
|
||||
|
||||
report.data = []
|
||||
PostAction.count_per_day_for_type(post_action_type, category_id: category_filter, start_date: report.start_date, end_date: report.end_date).each do |date, count|
|
||||
PostAction.count_per_day_for_type(post_action_type, category_id: category_id, include_subcategories: include_subcategories, start_date: report.start_date, end_date: report.end_date).each do |date, count|
|
||||
report.data << { x: date, y: count }
|
||||
end
|
||||
|
||||
countable = PostAction.unscoped.where(post_action_type_id: post_action_type)
|
||||
countable = countable.joins(post: :topic).merge(Topic.in_category_and_subcategories(category_filter)) if category_filter
|
||||
if category_id
|
||||
if include_subcategories
|
||||
countable = countable.joins(post: :topic).where('topics.category_id IN (?)', Category.subcategory_ids(category_id))
|
||||
else
|
||||
countable = countable.joins(post: :topic).where('topics.category_id = ?', category_id)
|
||||
end
|
||||
end
|
||||
|
||||
add_counts report, countable, 'post_actions.created_at'
|
||||
end
|
||||
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Report.add_report('flags') do |report|
|
||||
category_filter = report.filters.dig(:category)
|
||||
report.add_filter('category', default: category_filter)
|
||||
category_id, include_subcategories = report.add_category_filter
|
||||
|
||||
report.icon = 'flag'
|
||||
report.higher_is_better = false
|
||||
|
@ -13,13 +12,18 @@ Report.add_report('flags') do |report|
|
|||
:count_by_date,
|
||||
report.start_date,
|
||||
report.end_date,
|
||||
category_filter
|
||||
category_id,
|
||||
include_subcategories
|
||||
)
|
||||
|
||||
countable = ReviewableFlaggedPost.scores_with_topics
|
||||
|
||||
if category_filter
|
||||
countable.merge!(Topic.in_category_and_subcategories(category_filter))
|
||||
if category_id
|
||||
if include_subcategories
|
||||
countable = countable.where('topics.category_id IN (?)', Category.subcategory_ids(category_id))
|
||||
else
|
||||
countable = countable.where('topics.category_id = ?', category_id)
|
||||
end
|
||||
end
|
||||
|
||||
add_counts report, countable, 'reviewable_scores.created_at'
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Report.add_report('post_edits') do |report|
|
||||
category_filter = report.filters.dig(:category)
|
||||
report.add_filter('category', default: category_filter)
|
||||
category_id, include_subcategories = report.add_category_filter
|
||||
|
||||
report.modes = [:table]
|
||||
|
||||
|
@ -77,13 +76,13 @@ Report.add_report('post_edits') do |report|
|
|||
/*limit*/
|
||||
SQL
|
||||
|
||||
if category_filter
|
||||
if category_id
|
||||
builder.join "topics t ON t.id = p.topic_id"
|
||||
builder.where("t.category_id = :category_id
|
||||
OR t.category_id IN (
|
||||
SELECT id FROM categories
|
||||
WHERE categories.parent_category_id = :category_id
|
||||
)", category_id: category_filter)
|
||||
if include_subcategories
|
||||
builder.where("t.category_id IN (?)", Category.subcategory_ids(category_id))
|
||||
else
|
||||
builder.where("t.category_id = ?", category_id)
|
||||
end
|
||||
end
|
||||
|
||||
builder.where("editor.id > 0 AND editor.id != author.id")
|
||||
|
|
|
@ -3,14 +3,18 @@
|
|||
Report.add_report('posts') do |report|
|
||||
report.modes = [:table, :chart]
|
||||
|
||||
category_filter = report.filters.dig(:category)
|
||||
report.add_filter('category', default: category_filter)
|
||||
category_id, include_subcategories = report.add_category_filter
|
||||
|
||||
basic_report_about report, Post, :public_posts_count_per_day, report.start_date, report.end_date, category_filter
|
||||
basic_report_about report, Post, :public_posts_count_per_day, report.start_date, report.end_date, category_id, include_subcategories
|
||||
|
||||
countable = Post.public_posts.where(post_type: Post.types[:regular])
|
||||
if category_filter
|
||||
countable = countable.joins(:topic).merge(Topic.in_category_and_subcategories(category_filter))
|
||||
if category_id
|
||||
if include_subcategories
|
||||
countable = countable.joins(:topic).where('topics.category_id IN (?)', Category.subcategory_ids(category_id))
|
||||
else
|
||||
countable = countable.joins(:topic).where('topics.category_id = ?', category_id)
|
||||
end
|
||||
end
|
||||
|
||||
add_counts report, countable, 'posts.created_at'
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Report.add_report('profile_views') do |report|
|
||||
group_filter = report.filters.dig(:group)
|
||||
report.add_filter('group', default: group_filter)
|
||||
report.add_filter('group', type: 'group', default: group_filter)
|
||||
|
||||
start_date = report.start_date
|
||||
end_date = report.end_date
|
||||
|
|
|
@ -4,7 +4,7 @@ Report.add_report('signups') do |report|
|
|||
report.icon = 'user-plus'
|
||||
|
||||
group_filter = report.filters.dig(:group)
|
||||
report.add_filter('group', default: group_filter)
|
||||
report.add_filter('group', type: 'group', default: group_filter)
|
||||
|
||||
if group_filter
|
||||
basic_report_about report, User.real, :count_by_signup_date, report.start_date, report.end_date, group_filter
|
||||
|
|
|
@ -2,17 +2,17 @@
|
|||
|
||||
Report.add_report('time_to_first_response') do |report|
|
||||
category_filter = report.filters.dig(:category)
|
||||
report.add_filter('category', default: category_filter)
|
||||
category_id, include_subcategories = report.add_category_filter
|
||||
|
||||
report.icon = 'reply'
|
||||
report.higher_is_better = false
|
||||
report.data = []
|
||||
|
||||
Topic.time_to_first_response_per_day(report.start_date, report.end_date, category_id: category_filter).each do |r|
|
||||
Topic.time_to_first_response_per_day(report.start_date, report.end_date, category_id: category_id, include_subcategories: include_subcategories).each do |r|
|
||||
report.data << { x: r['date'], y: r['hours'].to_f.round(2) }
|
||||
end
|
||||
|
||||
report.total = Topic.time_to_first_response_total(category_id: category_filter)
|
||||
report.total = Topic.time_to_first_response_total(category_id: category_id, include_subcategories: include_subcategories)
|
||||
|
||||
report.prev30Days = Topic.time_to_first_response_total(start_date: report.start_date - 30.days, end_date: report.start_date, category_id: category_filter)
|
||||
report.prev30Days = Topic.time_to_first_response_total(start_date: report.start_date - 30.days, end_date: report.start_date, category_id: category_id, include_subcategories: include_subcategories)
|
||||
end
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Report.add_report('top_referred_topics') do |report|
|
||||
category_filter = report.filters.dig(:category)
|
||||
report.add_filter('category', default: category_filter)
|
||||
category_id, include_subcategories = report.add_category_filter
|
||||
|
||||
report.modes = [:table]
|
||||
|
||||
|
@ -26,7 +25,8 @@ Report.add_report('top_referred_topics') do |report|
|
|||
end_date: report.end_date,
|
||||
start_date: report.start_date,
|
||||
limit: report.limit || 8,
|
||||
category_id: category_filter
|
||||
category_id: category_id,
|
||||
include_subcategories: include_subcategories
|
||||
}
|
||||
result = nil
|
||||
result = IncomingLinksReport.find(:top_referred_topics, options)
|
||||
|
|
|
@ -1,8 +1,7 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Report.add_report('top_traffic_sources') do |report|
|
||||
category_filter = report.filters.dig(:category)
|
||||
report.add_filter('category', default: category_filter)
|
||||
category_id, include_subcategories = report.add_category_filter
|
||||
|
||||
report.modes = [:table]
|
||||
|
||||
|
@ -27,7 +26,8 @@ Report.add_report('top_traffic_sources') do |report|
|
|||
end_date: report.end_date,
|
||||
start_date: report.start_date,
|
||||
limit: report.limit || 8,
|
||||
category_id: category_filter
|
||||
category_id: category_id,
|
||||
include_subcategories: include_subcategories
|
||||
}
|
||||
|
||||
result = IncomingLinksReport.find(:top_traffic_sources, options)
|
||||
|
|
|
@ -5,10 +5,9 @@ Report.add_report('top_uploads') do |report|
|
|||
|
||||
extension_filter = report.filters.dig(:"file-extension")
|
||||
report.add_filter('file-extension',
|
||||
type: 'list',
|
||||
default: extension_filter || 'any',
|
||||
choices: (
|
||||
SiteSetting.authorized_extensions.split('|') + Array(extension_filter)
|
||||
).uniq
|
||||
choices: (SiteSetting.authorized_extensions.split('|') + Array(extension_filter)).uniq
|
||||
)
|
||||
|
||||
report.labels = [
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Report.add_report('topics') do |report|
|
||||
category_filter = report.filters.dig(:category)
|
||||
report.add_filter('category', default: category_filter)
|
||||
category_id, include_subcategories = report.add_category_filter
|
||||
|
||||
basic_report_about report, Topic, :listable_count_per_day, report.start_date, report.end_date, category_filter
|
||||
basic_report_about report, Topic, :listable_count_per_day, report.start_date, report.end_date, category_id, include_subcategories
|
||||
|
||||
countable = Topic.listable_topics
|
||||
if category_filter
|
||||
countable = countable.in_category_and_subcategories(category_filter)
|
||||
end
|
||||
countable = countable.where(category_id: include_subcategories ? Category.subcategory_ids(category_id) : category_id) if category_id
|
||||
|
||||
add_counts report, countable, 'topics.created_at'
|
||||
end
|
||||
|
|
|
@ -1,15 +1,14 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
Report.add_report('topics_with_no_response') do |report|
|
||||
category_filter = report.filters.dig(:category)
|
||||
report.add_filter('category', default: category_filter)
|
||||
category_id, include_subcategories = report.add_category_filter
|
||||
|
||||
report.data = []
|
||||
Topic.with_no_response_per_day(report.start_date, report.end_date, category_filter).each do |r|
|
||||
Topic.with_no_response_per_day(report.start_date, report.end_date, category_id, include_subcategories).each do |r|
|
||||
report.data << { x: r['date'], y: r['count'].to_i }
|
||||
end
|
||||
|
||||
report.total = Topic.with_no_response_total(category_id: category_filter)
|
||||
report.total = Topic.with_no_response_total(category_id: category_id, include_subcategories: include_subcategories)
|
||||
|
||||
report.prev30Days = Topic.with_no_response_total(start_date: report.start_date - 30.days, end_date: report.start_date, category_id: category_filter)
|
||||
report.prev30Days = Topic.with_no_response_total(start_date: report.start_date - 30.days, end_date: report.start_date, category_id: category_id, include_subcategories: include_subcategories)
|
||||
end
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
Report.add_report('visits') do |report|
|
||||
group_filter = report.filters.dig(:group)
|
||||
report.add_filter('group', default: group_filter)
|
||||
report.add_filter('group', type: 'group', default: group_filter)
|
||||
|
||||
report.icon = 'user'
|
||||
|
||||
|
|
|
@ -534,10 +534,18 @@ class Reviewable < ActiveRecord::Base
|
|||
ReviewableScore.joins(reviewable: :topic).where("reviewables.type = ?", name)
|
||||
end
|
||||
|
||||
def self.count_by_date(start_date, end_date, category_id = nil)
|
||||
scores_with_topics
|
||||
.where('reviewable_scores.created_at BETWEEN ? AND ?', start_date, end_date)
|
||||
.where("topics.category_id = COALESCE(?, topics.category_id)", category_id)
|
||||
def self.count_by_date(start_date, end_date, category_id = nil, include_subcategories = false)
|
||||
query = scores_with_topics.where('reviewable_scores.created_at BETWEEN ? AND ?', start_date, end_date)
|
||||
|
||||
if category_id
|
||||
if include_subcategories
|
||||
query = query.where("topics.category_id IN (?)", Category.subcategory_ids(category_id))
|
||||
else
|
||||
query = query.where("topics.category_id = ?", category_id)
|
||||
end
|
||||
end
|
||||
|
||||
query
|
||||
.group("date(reviewable_scores.created_at)")
|
||||
.order('date(reviewable_scores.created_at)')
|
||||
.count
|
||||
|
|
|
@ -454,10 +454,10 @@ class Topic < ActiveRecord::Base
|
|||
((Time.zone.now - created_at) / 1.minute).round
|
||||
end
|
||||
|
||||
def self.listable_count_per_day(start_date, end_date, category_id = nil)
|
||||
def self.listable_count_per_day(start_date, end_date, category_id = nil, include_subcategories = false)
|
||||
result = listable_topics.where("topics.created_at >= ? AND topics.created_at <= ?", start_date, end_date)
|
||||
result = result.group('date(topics.created_at)').order('date(topics.created_at)')
|
||||
result = result.where(category_id: category_id) if category_id
|
||||
result = result.where(category_id: include_subcategories ? Category.subcategory_ids(category_id) : category_id) if category_id
|
||||
result.count
|
||||
end
|
||||
|
||||
|
@ -1294,7 +1294,13 @@ class Topic < ActiveRecord::Base
|
|||
builder = DB.build(sql)
|
||||
builder.where("t.created_at >= :start_date", start_date: opts[:start_date]) if opts[:start_date]
|
||||
builder.where("t.created_at < :end_date", end_date: opts[:end_date]) if opts[:end_date]
|
||||
builder.where("t.category_id IN (:subcategory_ids)", subcategory_ids: Category.subcategory_ids(opts[:category_id].to_i)) if opts[:category_id]
|
||||
if opts[:category_id]
|
||||
if opts[:include_subcategories]
|
||||
builder.where("t.category_id IN (?)", Category.subcategory_ids(opts[:category_id]))
|
||||
else
|
||||
builder.where("t.category_id = ?", opts[:category_id])
|
||||
end
|
||||
end
|
||||
builder.where("t.archetype <> '#{Archetype.private_message}'")
|
||||
builder.where("t.deleted_at IS NULL")
|
||||
builder.where("p.deleted_at IS NULL")
|
||||
|
@ -1329,11 +1335,17 @@ class Topic < ActiveRecord::Base
|
|||
ORDER BY tt.created_at
|
||||
SQL
|
||||
|
||||
def self.with_no_response_per_day(start_date, end_date, category_id = nil)
|
||||
def self.with_no_response_per_day(start_date, end_date, category_id = nil, include_subcategories = nil)
|
||||
builder = DB.build(WITH_NO_RESPONSE_SQL)
|
||||
builder.where("t.created_at >= :start_date", start_date: start_date) if start_date
|
||||
builder.where("t.created_at < :end_date", end_date: end_date) if end_date
|
||||
builder.where("t.category_id IN (:subcategory_ids)", subcategory_ids: Category.subcategory_ids(category_id.to_i)) if category_id
|
||||
if category_id
|
||||
if include_subcategories
|
||||
builder.where("t.category_id IN (?)", Category.subcategory_ids(category_id))
|
||||
else
|
||||
builder.where("t.category_id = ?", category_id)
|
||||
end
|
||||
end
|
||||
builder.where("t.archetype <> '#{Archetype.private_message}'")
|
||||
builder.where("t.deleted_at IS NULL")
|
||||
builder.query_hash
|
||||
|
@ -1353,7 +1365,13 @@ class Topic < ActiveRecord::Base
|
|||
|
||||
def self.with_no_response_total(opts = {})
|
||||
builder = DB.build(WITH_NO_RESPONSE_TOTAL_SQL)
|
||||
builder.where("t.category_id IN (:subcategory_ids)", subcategory_ids: Category.subcategory_ids(opts[:category_id].to_i)) if opts[:category_id]
|
||||
if opts[:category_id]
|
||||
if opts[:include_subcategories]
|
||||
builder.where("t.category_id IN (?)", Category.subcategory_ids(opts[:category_id]))
|
||||
else
|
||||
builder.where("t.category_id = ?", opts[:category_id])
|
||||
end
|
||||
end
|
||||
builder.where("t.archetype <> '#{Archetype.private_message}'")
|
||||
builder.where("t.deleted_at IS NULL")
|
||||
builder.query_single.first.to_i
|
||||
|
|
|
@ -3411,6 +3411,8 @@ en:
|
|||
label: Group
|
||||
category:
|
||||
label: Category
|
||||
include-subcategories:
|
||||
label: "Include Subcategories"
|
||||
|
||||
commits:
|
||||
latest_changes: "Latest changes: please update often!"
|
||||
|
|
|
@ -771,7 +771,7 @@ describe Report do
|
|||
include_examples 'category filtering'
|
||||
|
||||
context "on subcategories" do
|
||||
let(:report) { Report.find('flags', filters: { category: c0.id }) }
|
||||
let(:report) { Report.find('flags', filters: { category: c0.id, 'include-subcategories': true }) }
|
||||
|
||||
include_examples 'category filtering on subcategories'
|
||||
end
|
||||
|
@ -801,7 +801,7 @@ describe Report do
|
|||
include_examples 'category filtering'
|
||||
|
||||
context "on subcategories" do
|
||||
let(:report) { Report.find('topics', filters: { category: c0.id }) }
|
||||
let(:report) { Report.find('topics', filters: { category: c0.id, 'include-subcategories': true }) }
|
||||
|
||||
include_examples 'category filtering on subcategories'
|
||||
end
|
||||
|
@ -892,7 +892,7 @@ describe Report do
|
|||
include_examples 'category filtering'
|
||||
|
||||
context "on subcategories" do
|
||||
let(:report) { Report.find('posts', filters: { category: c0.id }) }
|
||||
let(:report) { Report.find('posts', filters: { category: c0.id, 'include-subcategories': true }) }
|
||||
|
||||
include_examples 'category filtering on subcategories'
|
||||
end
|
||||
|
@ -924,7 +924,7 @@ describe Report do
|
|||
include_examples 'category filtering'
|
||||
|
||||
context "on subcategories" do
|
||||
let(:report) { Report.find('topics_with_no_response', filters: { category: c0.id }) }
|
||||
let(:report) { Report.find('topics_with_no_response', filters: { category: c0.id, 'include-subcategories': true }) }
|
||||
|
||||
include_examples 'category filtering on subcategories'
|
||||
end
|
||||
|
@ -960,7 +960,7 @@ describe Report do
|
|||
include_examples 'category filtering'
|
||||
|
||||
context "on subcategories" do
|
||||
let(:report) { Report.find('likes', filters: { category: c0.id }) }
|
||||
let(:report) { Report.find('likes', filters: { category: c0.id, 'include-subcategories': true }) }
|
||||
|
||||
include_examples 'category filtering on subcategories'
|
||||
end
|
||||
|
|
|
@ -2359,7 +2359,7 @@ describe Topic do
|
|||
Fabricate(:post, topic: topic, user: topic.user, post_number: 1, created_at: 3.hours.ago)
|
||||
Fabricate(:post, topic: topic, post_number: 2, created_at: 2.hours.ago)
|
||||
|
||||
expect(Topic.time_to_first_response_total(category_id: category.id)).to eq(1)
|
||||
expect(Topic.time_to_first_response_total(category_id: category.id, include_subcategories: true)).to eq(1)
|
||||
end
|
||||
|
||||
it "should only count regular posts as the first response" do
|
||||
|
|
|
@ -61,7 +61,7 @@ let signups = {
|
|||
dates_filtering: true,
|
||||
report_key: 'reports:signups:start:end:[:prev_period]:50:{"group":"88"}:4',
|
||||
available_filters: [
|
||||
{ id: "group", allow_any: false, choices: [], default: "88" }
|
||||
{ id: "group", type: "group", allow_any: false, choices: [], default: "88" }
|
||||
],
|
||||
labels: [
|
||||
{ type: "date", properties: ["x"], title: "Day" },
|
||||
|
|
Loading…
Reference in New Issue