Skip to content

Commit

Permalink
FIX: Topic.time_to_first_response should include sub-sub-categories (#…
Browse files Browse the repository at this point in the history
  • Loading branch information
udan11 committed Apr 4, 2020
1 parent a8002ba commit b9d411a
Show file tree
Hide file tree
Showing 4 changed files with 37 additions and 27 deletions.
19 changes: 19 additions & 0 deletions app/models/category.rb
Expand Up @@ -153,6 +153,25 @@ def reset_topic_ids_cache
Category.reset_topic_ids_cache
end

def self.subcategory_ids(category_id)
sql = <<~SQL
WITH RECURSIVE subcategories AS (
SELECT :category_id id, 1 depth
UNION
SELECT categories.id, (subcategories.depth + 1) depth
FROM categories
JOIN subcategories ON subcategories.id = categories.parent_category_id
WHERE subcategories.depth < :max_category_nesting
)
SELECT id FROM subcategories
SQL
DB.query_single(
sql,
category_id: category_id,
max_category_nesting: SiteSetting.max_category_nesting
)
end

def self.scoped_to_permissions(guardian, permission_types)
if guardian.try(:is_admin?)
all
Expand Down
11 changes: 3 additions & 8 deletions app/models/topic.rb
Expand Up @@ -189,11 +189,6 @@ def recover!(recovered_by = nil)
where("topics.category_id IS NULL OR topics.category_id IN (SELECT id FROM categories WHERE #{condition[0]})", condition[1])
}

IN_CATEGORY_AND_SUBCATEGORIES_SQL = <<~SQL
t.category_id = :category_id
OR t.category_id IN (SELECT id FROM categories WHERE categories.parent_category_id = :category_id)
SQL

scope :in_category_and_subcategories, lambda { |category_id|
where("topics.category_id = ? OR topics.category_id IN (SELECT id FROM categories WHERE categories.parent_category_id = ?)",
category_id,
Expand Down Expand Up @@ -1300,7 +1295,7 @@ def self.time_to_first_response(sql, opts = nil)
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(IN_CATEGORY_AND_SUBCATEGORIES_SQL, category_id: opts[:category_id]) if opts[:category_id]
builder.where("t.category_id IN (:subcategory_ids)", subcategory_ids: Category.subcategory_ids(opts[:category_id])) if opts[:category_id]
builder.where("t.archetype <> '#{Archetype.private_message}'")
builder.where("t.deleted_at IS NULL")
builder.where("p.deleted_at IS NULL")
Expand Down Expand Up @@ -1339,7 +1334,7 @@ def self.with_no_response_per_day(start_date, end_date, category_id = 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(IN_CATEGORY_AND_SUBCATEGORIES_SQL, category_id: category_id) if category_id
builder.where("t.category_id IN (:subcategory_ids)", subcategory_ids: Category.subcategory_ids(category_id)) if category_id
builder.where("t.archetype <> '#{Archetype.private_message}'")
builder.where("t.deleted_at IS NULL")
builder.query_hash
Expand All @@ -1359,7 +1354,7 @@ def self.with_no_response_per_day(start_date, end_date, category_id = nil)

def self.with_no_response_total(opts = {})
builder = DB.build(WITH_NO_RESPONSE_TOTAL_SQL)
builder.where(IN_CATEGORY_AND_SUBCATEGORIES_SQL, category_id: opts[:category_id]) if opts[:category_id]
builder.where("t.category_id IN (:subcategory_ids)", subcategory_ids: Category.subcategory_ids(opts[:category_id])) if opts[:category_id]
builder.where("t.archetype <> '#{Archetype.private_message}'")
builder.where("t.deleted_at IS NULL")
builder.query_single.first.to_i
Expand Down
20 changes: 1 addition & 19 deletions lib/topic_query.rb
Expand Up @@ -1054,25 +1054,7 @@ def suggested_ordering(result, options)

def subcategory_ids(category_id)
@subcategory_ids ||= {}
@subcategory_ids[category_id] ||=
begin
sql = <<~SQL
WITH RECURSIVE subcategories AS (
SELECT :category_id id, 1 depth
UNION
SELECT categories.id, (subcategories.depth + 1) depth
FROM categories
JOIN subcategories ON subcategories.id = categories.parent_category_id
WHERE subcategories.depth < :max_category_nesting
)
SELECT id FROM subcategories
SQL
DB.query_single(
sql,
category_id: category_id,
max_category_nesting: SiteSetting.max_category_nesting
)
end
@subcategory_ids[category_id] ||= Category.subcategory_ids(category_id)
end

def sanitize_sql_array(input)
Expand Down
14 changes: 14 additions & 0 deletions spec/models/topic_spec.rb
Expand Up @@ -2348,6 +2348,20 @@ def set_state!(group, user, state)
expect(Topic.time_to_first_response_total).to eq(1)
end

it "should have results if there's a topic with replies" do
SiteSetting.max_category_nesting = 3

category = Fabricate(:category_with_definition)
subcategory = Fabricate(:category_with_definition, parent_category_id: category.id)
subsubcategory = Fabricate(:category_with_definition, parent_category_id: subcategory.id)

topic = Fabricate(:topic, category: subsubcategory, created_at: 3.hours.ago)
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)
end

it "should only count regular posts as the first response" do
topic = Fabricate(:topic, created_at: 5.hours.ago)
Fabricate(:post, topic: topic, user: topic.user, post_number: 1, created_at: 5.hours.ago)
Expand Down

0 comments on commit b9d411a

Please sign in to comment.