Change Anniversary badge to be multiple grant, once per year

This commit is contained in:
Robin Ward 2017-04-28 12:20:05 -04:00
parent bb82e0a041
commit f05f1a24d3
7 changed files with 203 additions and 20 deletions

View File

@ -0,0 +1,17 @@
require_dependency 'jobs/scheduled/grant_anniversary_badges'
module Jobs
class RetroGrantAnniversary < Jobs::Onceoff
def execute_onceoff(args)
return unless SiteSetting.enable_badges
# Fill in the years of anniversary badges we missed
(2..3).each do |year|
Jobs::GrantAnniversaryBadges.new.execute(start_date: year.years.ago)
end
end
end
end

View File

@ -0,0 +1,44 @@
module Jobs
class GrantAnniversaryBadges < Jobs::Scheduled
every 1.day
def execute(args)
return unless SiteSetting.enable_badges?
start_date = args[:start_date] || 1.year.ago
end_date = start_date + 1.year
fmt_end_date = end_date.iso8601(6)
fmt_start_date = start_date.iso8601(6)
results = User.exec_sql <<~SQL
SELECT u.id AS user_id
FROM users AS u
INNER JOIN posts AS p ON p.user_id = u.id
INNER JOIN topics AS t ON p.topic_id = t.id
LEFT OUTER JOIN user_badges AS ub ON ub.user_id = u.id AND
ub.badge_id = #{Badge::Anniversary} AND
ub.granted_at BETWEEN '#{fmt_start_date}' AND '#{fmt_end_date}'
WHERE u.active AND
NOT u.blocked AND
NOT p.hidden AND
p.deleted_at IS NULL AND
t.visible AND
t.archetype <> 'private_message' AND
p.created_at BETWEEN '#{fmt_start_date}' AND '#{fmt_end_date}' AND
u.created_at <= '#{fmt_start_date}'
GROUP BY u.id
HAVING COUNT(p.id) > 0 AND COUNT(ub.id) = 0
SQL
badge = Badge.find(Badge::Anniversary)
user_ids = results.map {|r| r['user_id'].to_i }
User.where(id: user_ids).each do |user|
BadgeGranter.grant(badge, user, created_at: end_date)
end
end
end
end

View File

@ -34,7 +34,7 @@ class Badge < ActiveRecord::Base
NiceShare = 21
GoodShare = 22
GreatShare = 23
OneYearAnniversary = 24
Anniversary = 24
Promoter = 25
Campaigner = 26

View File

@ -37,6 +37,7 @@ class BadgeGranter
post_id: @post_id,
seq: seq)
return unless SiteSetting.enable_badges
if @granted_by != Discourse.system_user
StaffActionLogger.new(@granted_by).log_badge_grant(user_badge)
end

View File

@ -256,15 +256,16 @@ end
end
Badge.seed do |b|
b.id = Badge::OneYearAnniversary
b.id = Badge::Anniversary
b.name = "Anniversary"
b.default_icon = "fa-clock-o"
b.badge_type_id = BadgeType::Silver
b.query = BadgeQueries::OneYearAnniversary
b.default_badge_grouping_id = BadgeGrouping::Community
b.query = nil
b.trigger = Badge::Trigger::None
b.auto_revoke = false
b.system = true
b.multiple_grant = true
end
[

View File

@ -117,23 +117,6 @@ SQL
(:backfill OR u.id IN (:user_ids) )
SQL
# member for a year + has posted at least once during that year
OneYearAnniversary = <<-SQL
SELECT u.id AS user_id, MIN(u.created_at + interval '1 year') AS granted_at
FROM users u
JOIN posts p ON p.user_id = u.id
WHERE u.id > 0
AND u.active
AND NOT u.blocked
AND u.created_at + interval '1 year' < now()
AND p.deleted_at IS NULL
AND NOT p.hidden
AND p.created_at + interval '1 year' > now()
AND (:backfill OR u.id IN (:user_ids))
GROUP BY u.id
HAVING COUNT(p.id) > 0
SQL
FirstMention = <<-SQL
SELECT acting_user_id AS user_id, min(target_post_id) AS post_id, min(p.created_at) AS granted_at
FROM user_actions

View File

@ -0,0 +1,137 @@
require 'rails_helper'
require_dependency 'jobs/scheduled/grant_anniversary_badges'
describe Jobs::GrantAnniversaryBadges do
let(:granter) { described_class.new }
it "doesn't award to a user who is less than a year old" do
user = Fabricate(:user, created_at: 1.month.ago)
Fabricate(:post, user: user, created_at: 1.week.ago)
granter.execute({})
badge = user.user_badges.where(badge_id: Badge::Anniversary)
expect(badge).to be_blank
end
it "doesn't award to an inactive user" do
user = Fabricate(:user, created_at: 400.days.ago, active: false)
Fabricate(:post, user: user, created_at: 1.week.ago)
granter.execute({})
badge = user.user_badges.where(badge_id: Badge::Anniversary)
expect(badge).to be_blank
end
it "doesn't award to a blocked user" do
user = Fabricate(:user, created_at: 400.days.ago, blocked: true)
Fabricate(:post, user: user, created_at: 1.week.ago)
granter.execute({})
badge = user.user_badges.where(badge_id: Badge::Anniversary)
expect(badge).to be_blank
end
it "doesn't award when a post is deleted" do
user = Fabricate(:user, created_at: 400.days.ago)
Fabricate(:post, user: user, created_at: 1.week.ago, deleted_at: 1.day.ago)
granter.execute({})
badge = user.user_badges.where(badge_id: Badge::Anniversary)
expect(badge).to be_blank
end
it "doesn't award when a post is hidden" do
user = Fabricate(:user, created_at: 400.days.ago)
Fabricate(:post, user: user, created_at: 1.week.ago, hidden: true)
granter.execute({})
badge = user.user_badges.where(badge_id: Badge::Anniversary)
expect(badge).to be_blank
end
it "doesn't award to PMs" do
user = Fabricate(:user, created_at: 400.days.ago)
Fabricate(:private_message_post, user: user, created_at: 1.week.ago)
granter.execute({})
badge = user.user_badges.where(badge_id: Badge::Anniversary)
expect(badge).to be_blank
end
it "doesn't award to a user without a post" do
user = Fabricate(:user, created_at: 1.month.ago)
granter.execute({})
badge = user.user_badges.where(badge_id: Badge::Anniversary)
expect(badge).to be_blank
end
it "doesn't award when badges are disabled" do
SiteSetting.enable_badges = false
user = Fabricate(:user, created_at: 400.days.ago)
Fabricate(:post, user: user, created_at: 1.week.ago)
granter.execute({})
badge = user.user_badges.where(badge_id: Badge::Anniversary)
expect(badge.count).to eq(0)
end
it "awards the badge to a user with a post active for a year" do
user = Fabricate(:user, created_at: 400.days.ago)
Fabricate(:post, user: user, created_at: 1.week.ago)
granter.execute({})
badge = user.user_badges.where(badge_id: Badge::Anniversary)
expect(badge.count).to eq(1)
end
context "repeated grants" do
it "won't award twice in the same year" do
user = Fabricate(:user, created_at: 400.days.ago)
Fabricate(:post, user: user, created_at: 1.week.ago)
granter.execute({})
granter.execute({})
badge = user.user_badges.where(badge_id: Badge::Anniversary)
expect(badge.count).to eq(1)
end
it "will award again if a year has passed" do
user = Fabricate(:user, created_at: 800.days.ago)
Fabricate(:post, user: user, created_at: 450.days.ago)
Timecop.freeze(400.days.ago) do
granter.execute({})
end
badge = user.user_badges.where(badge_id: Badge::Anniversary)
expect(badge.count).to eq(1)
Fabricate(:post, user: user, created_at: 50.days.ago)
granter.execute({})
badge = user.user_badges.where(badge_id: Badge::Anniversary)
expect(badge.count).to eq(2)
end
it "supports date ranges" do
user = Fabricate(:user, created_at: 3.years.ago)
Fabricate(:post, user: user, created_at: 750.days.ago)
granter.execute(start_date: 800.days.ago)
badge = user.user_badges.where(badge_id: Badge::Anniversary)
expect(badge.count).to eq(1)
Fabricate(:post, user: user, created_at: 50.days.ago)
granter.execute(start_date: 800.days.ago)
badge = user.user_badges.where(badge_id: Badge::Anniversary)
expect(badge.count).to eq(1)
granter.execute(start_date: 60.days.ago)
badge = user.user_badges.where(badge_id: Badge::Anniversary)
expect(badge.count).to eq(2)
end
end
end