FEATURE: Make report filters reusable (#9444)

This commit also adds 'include subcategories' report filter
This commit is contained in:
Dan Ungureanu 2020-04-22 11:52:50 +03:00 committed by GitHub
parent a511bea4cc
commit e733701887
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
34 changed files with 221 additions and 130 deletions

View File

@ -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
]

View File

@ -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);
}
});

View File

@ -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);
}
});

View File

@ -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"
});

View File

@ -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);
}
});

View File

@ -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;
}
});

View File

@ -0,0 +1,3 @@
import FilterComponent from "admin/components/report-filters/filter";
export default FilterComponent.extend();

View File

@ -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) {

View File

@ -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>

View File

@ -0,0 +1,5 @@
{{input
type="checkbox"
checked=checked
click=(action "onChange")
}}

View File

@ -1,7 +1,5 @@
{{search-advanced-category-chooser
value=category
onChange=(action "onChange")
options=(hash
filterable=true
)
options=(hash filterable=true)
}}

View File

@ -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

View File

@ -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)')

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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")

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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 = [

View File

@ -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

View File

@ -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

View File

@ -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'

View File

@ -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

View File

@ -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

View File

@ -3411,6 +3411,8 @@ en:
label: Group
category:
label: Category
include-subcategories:
label: "Include Subcategories"
commits:
latest_changes: "Latest changes: please update often!"

View File

@ -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

View File

@ -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

View File

@ -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" },