Wellness badge query+service refactor (#17592)

* Query +service slight refactor

* 33 week comment
This commit is contained in:
Fernando Valverde 2022-05-09 16:44:50 -06:00 committed by GitHub
parent 6d6287d93f
commit bf4d34fe9c
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
4 changed files with 69 additions and 53 deletions

View file

@ -2,8 +2,8 @@
# [ # [
# { # {
# "user_id"=>11, # "user_id"=>11,
# "serialized_weeks_ago"=>"1,16,20", # "serialized_weeks_ago"=>"0,1,16,20",
# "serialized_comment_counts"=>"2,2,1" # "serialized_comment_counts"=>"2,2,1,3"
# }, # },
# ... # ...
# ] # ]
@ -33,19 +33,19 @@ module Comments
SELECT user_id, SELECT user_id,
COUNT(user_id) AS number_of_comments_with_positive_reaction, COUNT(user_id) AS number_of_comments_with_positive_reaction,
/* Get the number of weeks, since today for posts */ /* Get the number of weeks, since today for posts */
(trunc((extract(epoch FROM (current_timestamp- created_at))) / 604800) + 1) AS weeks_ago trunc((extract(epoch FROM (current_timestamp- created_at))) / 604800) AS weeks_ago
FROM comments FROM comments
INNER JOIN INNER JOIN
( (
SELECT DISTINCT reactable_id SELECT DISTINCT reactable_id
FROM reactions FROM reactions
WHERE reactable_type = 'Comment' WHERE reactable_type = 'Comment'
AND created_at > (now() - interval '224' day) AND created_at > (now() - interval '231' day)
EXCEPT EXCEPT
SELECT DISTINCT reactable_id SELECT DISTINCT reactable_id
FROM reactions FROM reactions
WHERE reactable_type = 'Comment' WHERE reactable_type = 'Comment'
AND created_at > (now() - interval '224' day) AND created_at > (now() - interval '231' day)
AND category IN ('thumbsdown', 'vomit')) AS negative_reactions AND category IN ('thumbsdown', 'vomit')) AS negative_reactions
ON comments.id = negative_reactions.reactable_id ON comments.id = negative_reactions.reactable_id
INNER JOIN INNER JOIN
@ -53,13 +53,14 @@ module Comments
SELECT count(id) AS number_of_comments, SELECT count(id) AS number_of_comments,
user_id AS comment_counts_user_id user_id AS comment_counts_user_id
FROM comments FROM comments
WHERE created_at >= (now() - interval '7' day) /* This interval filters week 1 (what we care about) */
WHERE created_at >= (now() - interval '14' day)
AND created_at <= (now() - interval '7' day)
GROUP BY user_id) AS comment_counts GROUP BY user_id) AS comment_counts
ON comments.user_id = comment_counts_user_id ON comments.user_id = comment_counts_user_id
AND comment_counts.number_of_comments > 1 AND comment_counts.number_of_comments > 1
/* Dont select anything older than 224 days ago, or 32 weeks ago */ /* Dont select anything older than 231 days ago, or 33 weeks ago */
WHERE created_at > (now() - interval '224' day) WHERE created_at > (now() - interval '231' day)
AND created_at < (now() - interval '3' day)
GROUP BY user_id, weeks_ago) AS user_comment_counts_by_week GROUP BY user_id, weeks_ago) AS user_comment_counts_by_week
GROUP BY user_id GROUP BY user_id
SQL SQL

View file

@ -13,13 +13,16 @@ module Badges
# `weeks_ago` can have values like the following: # `weeks_ago` can have values like the following:
# - [1,2,10,11,12] # - [1,2,10,11,12]
# - [5] # - [0,5]
# - [1,2,3,4,5,6,7,8] # - [0,1,2,3,4,5,6,7,8]
# - [1,4,17] # - [1,4,17]
# We only care for active streak (starting at 1) so we need to filter # We only care for active streak (starting at 1) so we need to filter
# these to check how far back the (continuous) streak goes # these to check how far back the (continuous) streak goes
week_streak = 0 week_streak = 0
weeks_ago.each_with_index do |week, index| weeks_ago.each_with_index do |week, index|
# Week 0 are comments that exist but aren't at least 1 week old yet
next if week.zero?
# Must have 2 or more non-flagged comments posted on that week # Must have 2 or more non-flagged comments posted on that week
next unless comment_counts[index] > 1 next unless comment_counts[index] > 1

View file

@ -15,38 +15,43 @@ RSpec.describe Comments::CommunityWellnessQuery, type: :query do
# don't meet the query criteria: min 2 comments in a week within last 32 weeks # don't meet the query criteria: min 2 comments in a week within last 32 weeks
before do before do
# User 3 - only one comment # User 3 - only one comment
create_comment_time_ago(user3.id, 5.days.ago, commentable: articles.sample) create_comment_time_ago(user3.id, 8.days.ago, commentable: articles.sample)
# User 4 - has 3 comments but doesn't meet criteria bc comments are too old # User 4 - has 3 comments but doesn't meet criteria bc comments are too old
create_comment_time_ago(user4.id, 226.days.ago, commentable: articles.sample) create_comment_time_ago(user4.id, 233.days.ago, commentable: articles.sample)
create_comment_time_ago(user4.id, 227.days.ago, commentable: articles.sample) create_comment_time_ago(user4.id, 237.days.ago, commentable: articles.sample)
create_comment_time_ago(user4.id, 227.days.ago, commentable: articles.sample) create_comment_time_ago(user4.id, 239.days.ago, commentable: articles.sample)
end end
context "when multiple users match criteria" do context "when multiple users match criteria" do
before do before do
# User 1 - week 1 # User 1 - week 0
create_comment_time_ago(user1.id, 5.days.ago, commentable: articles.sample) create_comment_time_ago(user1.id, 5.days.ago, commentable: articles.sample)
create_comment_time_ago(user1.id, 6.days.ago, commentable: articles.sample) create_comment_time_ago(user1.id, 6.days.ago, commentable: articles.sample)
# User 1 - week 2 # User 1 - week 1
create_comment_time_ago(user1.id, 8.days.ago, commentable: articles.sample) create_comment_time_ago(user1.id, 8.days.ago, commentable: articles.sample)
create_comment_time_ago(user1.id, 11.days.ago, commentable: articles.sample) create_comment_time_ago(user1.id, 11.days.ago, commentable: articles.sample)
# User 2 - week 1
create_comment_time_ago(user2.id, 4.days.ago, commentable: articles.sample)
create_comment_time_ago(user2.id, 6.days.ago, commentable: articles.sample)
# User 1 - week 2 # User 1 - week 2
create_comment_time_ago(user1.id, 15.days.ago, commentable: articles.sample)
create_comment_time_ago(user1.id, 15.days.ago, commentable: articles.sample)
# User 2 - week 0
create_comment_time_ago(user2.id, 1.day.ago, commentable: articles.sample)
# User 2 - week 1
create_comment_time_ago(user2.id, 9.days.ago, commentable: articles.sample) create_comment_time_ago(user2.id, 9.days.ago, commentable: articles.sample)
create_comment_time_ago(user2.id, 13.days.ago, commentable: articles.sample) create_comment_time_ago(user2.id, 13.days.ago, commentable: articles.sample)
# User 1 - week 3 # User 2 - week 2
create_comment_time_ago(user2.id, 17.days.ago, commentable: articles.sample)
create_comment_time_ago(user2.id, 17.days.ago, commentable: articles.sample) create_comment_time_ago(user2.id, 17.days.ago, commentable: articles.sample)
create_comment_time_ago(user2.id, 18.days.ago, commentable: articles.sample) create_comment_time_ago(user2.id, 18.days.ago, commentable: articles.sample)
create_comment_time_ago(user2.id, 18.days.ago, commentable: articles.sample)
# User 2 - week 3
create_comment_time_ago(user2.id, 22.days.ago, commentable: articles.sample)
create_comment_time_ago(user2.id, 23.days.ago, commentable: articles.sample)
create_comment_time_ago(user2.id, 23.days.ago, commentable: articles.sample)
# User 5 - has 2 comments in the first week but too early to award because # User 5 - week 1
# comments must be 3 days old or more to give mods time to flag before award create_comment_time_ago(user5.id, 8.days.ago, commentable: articles.sample)
create_comment_time_ago(user5.id, 2.days.ago, commentable: articles.sample) create_comment_time_ago(user5.id, 10.days.ago, commentable: articles.sample)
create_comment_time_ago(user5.id, 6.days.ago, commentable: articles.sample)
end end
it "returns the correct data structure (array of hashes w/ correct keys)" do it "returns the correct data structure (array of hashes w/ correct keys)" do
@ -69,40 +74,46 @@ RSpec.describe Comments::CommunityWellnessQuery, type: :query do
expect(result_user_ids).to contain_exactly(user1.id, user2.id, user5.id) expect(result_user_ids).to contain_exactly(user1.id, user2.id, user5.id)
index1 = result.index { |hash| hash["user_id"] == user1.id } index1 = result.index { |hash| hash["user_id"] == user1.id }
expect(result[index1]["serialized_weeks_ago"]).to eq("1,2") expect(result[index1]["serialized_weeks_ago"]).to eq("0,1,2")
expect(result[index1]["serialized_comment_counts"]).to eq("2,2") expect(result[index1]["serialized_comment_counts"]).to eq("2,2,2")
index2 = result.index { |hash| hash["user_id"] == user2.id } index2 = result.index { |hash| hash["user_id"] == user2.id }
expect(result[index2]["serialized_weeks_ago"]).to eq("1,2,3") expect(result[index2]["serialized_weeks_ago"]).to eq("0,1,2,3")
expect(result[index2]["serialized_comment_counts"]).to eq("2,2,3") expect(result[index2]["serialized_comment_counts"]).to eq("1,2,3,3")
# user5 will still appear in the query because it has 2 comments in the # user5 will still appear in the query despite not having any comments in
# week but the count will reflect only those that are 3+ days old # week 0, because we start counting at week 1 to award the badge
index5 = result.index { |hash| hash["user_id"] == user5.id } index5 = result.index { |hash| hash["user_id"] == user5.id }
expect(result[index5]["serialized_weeks_ago"]).to eq("1") expect(result[index5]["serialized_weeks_ago"]).to eq("1")
expect(result[index5]["serialized_comment_counts"]).to eq("1") expect(result[index5]["serialized_comment_counts"]).to eq("2")
end end
end end
context "when users match criteria but mod reaction reduces their comment counts" do context "when users match criteria but mod reaction reduces their comment counts" do
before do before do
# User 1 - week 1 # User 1 - week 0
create_comment_time_ago(user1.id, 5.days.ago, commentable: articles.sample, flagged_by: mod) create_comment_time_ago(user1.id, 5.days.ago, commentable: articles.sample)
create_comment_time_ago(user1.id, 6.days.ago, commentable: articles.sample) create_comment_time_ago(user1.id, 6.days.ago, commentable: articles.sample)
# User 1 - week 2 # User 1 - week 1
create_comment_time_ago(user1.id, 8.days.ago, commentable: articles.sample) create_comment_time_ago(user1.id, 8.days.ago, commentable: articles.sample, flagged_by: mod)
create_comment_time_ago(user1.id, 11.days.ago, commentable: articles.sample) create_comment_time_ago(user1.id, 11.days.ago, commentable: articles.sample)
# User 2 - week 1
create_comment_time_ago(user2.id, 4.days.ago, commentable: articles.sample)
create_comment_time_ago(user2.id, 6.days.ago, commentable: articles.sample)
# User 1 - week 2 # User 1 - week 2
create_comment_time_ago(user1.id, 15.days.ago, commentable: articles.sample)
create_comment_time_ago(user1.id, 15.days.ago, commentable: articles.sample)
# User 2 - week 0
create_comment_time_ago(user2.id, 1.day.ago, commentable: articles.sample)
# User 2 - week 1
create_comment_time_ago(user2.id, 9.days.ago, commentable: articles.sample) create_comment_time_ago(user2.id, 9.days.ago, commentable: articles.sample)
create_comment_time_ago(user2.id, 13.days.ago, commentable: articles.sample, flagged_by: mod) create_comment_time_ago(user2.id, 13.days.ago, commentable: articles.sample)
# User 1 - week 3 # User 2 - week 2
create_comment_time_ago(user2.id, 17.days.ago, commentable: articles.sample)
create_comment_time_ago(user2.id, 17.days.ago, commentable: articles.sample) create_comment_time_ago(user2.id, 17.days.ago, commentable: articles.sample)
create_comment_time_ago(user2.id, 18.days.ago, commentable: articles.sample) create_comment_time_ago(user2.id, 18.days.ago, commentable: articles.sample)
create_comment_time_ago(user2.id, 18.days.ago, commentable: articles.sample)
# User 2 - week 3
create_comment_time_ago(user2.id, 22.days.ago, commentable: articles.sample)
create_comment_time_ago(user2.id, 23.days.ago, commentable: articles.sample)
create_comment_time_ago(user2.id, 23.days.ago, commentable: articles.sample, flagged_by: mod)
end end
it "matches the correct comment count for each week in result hash" do it "matches the correct comment count for each week in result hash" do
@ -112,16 +123,17 @@ RSpec.describe Comments::CommunityWellnessQuery, type: :query do
# Result includes both users because they have > 1 comment per week # Result includes both users because they have > 1 comment per week
expect(result_user_ids).to contain_exactly(user1.id, user2.id) expect(result_user_ids).to contain_exactly(user1.id, user2.id)
# user1 must have `1` in first week comment count because one of their 2 # user1 must have `1` in first week (1) comment count because one out of
# comments is flagged by a moderator # their two comments is flagged by a moderator
index1 = result.index { |hash| hash["user_id"] == user1.id } index1 = result.index { |hash| hash["user_id"] == user1.id }
expect(result[index1]["serialized_weeks_ago"]).to eq("1,2") expect(result[index1]["serialized_weeks_ago"]).to eq("0,1,2")
expect(result[index1]["serialized_comment_counts"]).to eq("1,2") expect(result[index1]["serialized_comment_counts"]).to eq("2,1,2")
# Second # user1 must have `2` in third week (3) comment count because one out of
# their three comments is flagged by a moderator
index2 = result.index { |hash| hash["user_id"] == user2.id } index2 = result.index { |hash| hash["user_id"] == user2.id }
expect(result[index2]["serialized_weeks_ago"]).to eq("1,2,3") expect(result[index2]["serialized_weeks_ago"]).to eq("0,1,2,3")
expect(result[index2]["serialized_comment_counts"]).to eq("2,1,3") expect(result[index2]["serialized_comment_counts"]).to eq("1,2,3,2")
end end
end end
end end

View file

@ -21,7 +21,7 @@ RSpec.describe Badges::AwardCommunityWellness, type: :service do
# Create 2 comments per-week to be tested, i.e. week 8 would create 2 # Create 2 comments per-week to be tested, i.e. week 8 would create 2
# comments on each of the 8 weeks (calculated on days_ago per week) # comments on each of the 8 weeks (calculated on days_ago per week)
reward_weeks[index].times do |week| reward_weeks[index].times do |week|
days_ago = (5 + (week * 7)).days.ago days_ago = (8 + (week * 7)).days.ago
create_comment_time_ago(users[index].id, days_ago, commentable: articles.sample) create_comment_time_ago(users[index].id, days_ago, commentable: articles.sample)
create_comment_time_ago(users[index].id, days_ago, commentable: articles.sample) create_comment_time_ago(users[index].id, days_ago, commentable: articles.sample)
end end