mirror of
https://github.com/discourse/discourse.git
synced 2025-02-25 18:55:32 -06:00
New badge, Rookie of the Month, for two new high quality users.
This commit is contained in:
@@ -1,5 +1,4 @@
|
|||||||
module Jobs
|
module Jobs
|
||||||
|
|
||||||
class CalculateAvgTime < Jobs::Scheduled
|
class CalculateAvgTime < Jobs::Scheduled
|
||||||
every 1.day
|
every 1.day
|
||||||
|
|
||||||
|
|||||||
63
app/jobs/scheduled/grant_rookie_badges.rb
Normal file
63
app/jobs/scheduled/grant_rookie_badges.rb
Normal file
@@ -0,0 +1,63 @@
|
|||||||
|
require 'badge_granter'
|
||||||
|
|
||||||
|
module Jobs
|
||||||
|
class GrantRookieBadges < Jobs::Scheduled
|
||||||
|
every 1.month
|
||||||
|
|
||||||
|
MAX_AWARDED = 2
|
||||||
|
|
||||||
|
def execute(args)
|
||||||
|
badge = Badge.find(Badge::RookieOfTheMonth)
|
||||||
|
scores.each do |user_id, score|
|
||||||
|
# Don't bother awarding to users who haven't received any likes
|
||||||
|
if score > 0.0
|
||||||
|
BadgeGranter.grant(badge, User.find(user_id))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def scores
|
||||||
|
scores = {}
|
||||||
|
|
||||||
|
# Find recent accounts and come up with a score based on how many likes they
|
||||||
|
# received, based on how much they posted and how old the accounts of the people
|
||||||
|
# who voted on them are.
|
||||||
|
sql = <<~SQL
|
||||||
|
SELECT u.id,
|
||||||
|
SUM(CASE
|
||||||
|
WHEN pa.id IS NOT NULL THEN
|
||||||
|
CASE
|
||||||
|
WHEN liked_by.created_at > (CURRENT_TIMESTAMP - '1 week'::INTERVAL) THEN 0.1
|
||||||
|
WHEN liked_by.created_at > (CURRENT_TIMESTAMP - '1 month'::INTERVAL) THEN 0.5
|
||||||
|
ELSE 1.0
|
||||||
|
END
|
||||||
|
ELSE 0
|
||||||
|
END) / COUNT(DISTINCT p.id) AS score
|
||||||
|
FROM users AS u
|
||||||
|
INNER JOIN user_stats AS us ON u.id = us.user_id
|
||||||
|
LEFT OUTER JOIN posts AS p ON p.user_id = u.id
|
||||||
|
LEFT OUTER JOIN post_actions AS pa ON
|
||||||
|
pa.post_id = p.id AND pa.post_action_type_id = :like
|
||||||
|
LEFT OUTER JOIN users AS liked_by ON liked_by.id = pa.user_id
|
||||||
|
WHERE u.active AND
|
||||||
|
u.id > 0 AND
|
||||||
|
NOT(u.admin) AND
|
||||||
|
NOT(u.moderator) AND
|
||||||
|
u.created_at >= CURRENT_TIMESTAMP - '1 month'::INTERVAL
|
||||||
|
GROUP BY u.id
|
||||||
|
HAVING COUNT(p.id) > 0
|
||||||
|
ORDER BY score DESC
|
||||||
|
LIMIT :max_awarded
|
||||||
|
SQL
|
||||||
|
|
||||||
|
User.exec_sql(sql, {
|
||||||
|
like: PostActionType.types[:like],
|
||||||
|
max_awarded: MAX_AWARDED
|
||||||
|
}).each do |row|
|
||||||
|
scores[row['id'].to_i] = row['score'].to_f
|
||||||
|
end
|
||||||
|
scores
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -56,6 +56,8 @@ class Badge < ActiveRecord::Base
|
|||||||
GivesBack = 32
|
GivesBack = 32
|
||||||
Empathetic = 39
|
Empathetic = 39
|
||||||
|
|
||||||
|
RookieOfTheMonth = 44
|
||||||
|
|
||||||
# other consts
|
# other consts
|
||||||
AutobiographerMinBioLength = 10
|
AutobiographerMinBioLength = 10
|
||||||
|
|
||||||
|
|||||||
@@ -3290,6 +3290,11 @@ en:
|
|||||||
description: Replied to a Post via email
|
description: Replied to a Post via email
|
||||||
long_description: |
|
long_description: |
|
||||||
This badge is granted the first time you reply to a post via email :e-mail:.
|
This badge is granted the first time you reply to a post via email :e-mail:.
|
||||||
|
rookie_of_the_month:
|
||||||
|
name: "Rookie of the Month"
|
||||||
|
description: Contributed a lot of value in their first month
|
||||||
|
long_description: |
|
||||||
|
This badge is granted to two high quality users who joined in the last month. The quality is determined by how many of their posts were liked and by whom.
|
||||||
|
|
||||||
admin_login:
|
admin_login:
|
||||||
success: "Email Sent"
|
success: "Email Sent"
|
||||||
|
|||||||
@@ -401,6 +401,20 @@ Badge.seed do |b|
|
|||||||
b.system = true
|
b.system = true
|
||||||
end
|
end
|
||||||
|
|
||||||
|
Badge.seed do |b|
|
||||||
|
b.id = Badge::RookieOfTheMonth
|
||||||
|
b.name = "Rookie of the Month"
|
||||||
|
b.badge_type_id = BadgeType::Bronze
|
||||||
|
b.multiple_grant = false
|
||||||
|
b.target_posts = false
|
||||||
|
b.show_posts = false
|
||||||
|
b.query = nil
|
||||||
|
b.badge_grouping_id = BadgeGrouping::GettingStarted
|
||||||
|
b.default_badge_grouping_id = BadgeGrouping::GettingStarted
|
||||||
|
b.trigger = Badge::Trigger::None
|
||||||
|
b.system = true
|
||||||
|
end
|
||||||
|
|
||||||
Badge.where("NOT system AND id < 100").each do |badge|
|
Badge.where("NOT system AND id < 100").each do |badge|
|
||||||
new_id = [Badge.maximum(:id) + 1, 100].max
|
new_id = [Badge.maximum(:id) + 1, 100].max
|
||||||
old_id = badge.id
|
old_id = badge.id
|
||||||
|
|||||||
68
spec/jobs/grant_rookie_badges_spec.rb
Normal file
68
spec/jobs/grant_rookie_badges_spec.rb
Normal file
@@ -0,0 +1,68 @@
|
|||||||
|
require 'rails_helper'
|
||||||
|
require_dependency 'jobs/scheduled/grant_rookie_badges'
|
||||||
|
|
||||||
|
describe Jobs::GrantRookieBadges do
|
||||||
|
|
||||||
|
let(:granter) { described_class.new }
|
||||||
|
|
||||||
|
it "runs correctly" do
|
||||||
|
user = Fabricate(:user, created_at: 1.week.ago)
|
||||||
|
p = Fabricate(:post, user: user)
|
||||||
|
|
||||||
|
old_user = Fabricate(:user, created_at: 6.months.ago)
|
||||||
|
PostAction.act(old_user, p, PostActionType.types[:like])
|
||||||
|
|
||||||
|
granter.execute({})
|
||||||
|
|
||||||
|
badge = user.user_badges.where(badge_id: Badge::RookieOfTheMonth)
|
||||||
|
expect(badge).to be_present
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '.scores' do
|
||||||
|
|
||||||
|
it "doesn't award it to accounts over a month old" do
|
||||||
|
user = Fabricate(:user, created_at: 2.months.ago)
|
||||||
|
Fabricate(:post, user: user)
|
||||||
|
expect(granter.scores.keys).not_to include(user.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "returns active accounts created in the last month" do
|
||||||
|
user = Fabricate(:user, created_at: 1.week.ago)
|
||||||
|
Fabricate(:post, user: user)
|
||||||
|
expect(granter.scores.keys).to include(user.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "likes from older accounts are scored higher" do
|
||||||
|
user = Fabricate(:user, created_at: 1.week.ago)
|
||||||
|
p = Fabricate(:post, user: user)
|
||||||
|
|
||||||
|
new_user = Fabricate(:user, created_at: 2.days.ago)
|
||||||
|
med_user = Fabricate(:user, created_at: 3.weeks.ago)
|
||||||
|
old_user = Fabricate(:user, created_at: 6.months.ago)
|
||||||
|
|
||||||
|
PostAction.act(new_user, p, PostActionType.types[:like])
|
||||||
|
PostAction.act(med_user, p, PostActionType.types[:like])
|
||||||
|
PostAction.act(old_user, p, PostActionType.types[:like])
|
||||||
|
expect(granter.scores[user.id]).to eq(1.6)
|
||||||
|
|
||||||
|
# It goes down the more they post
|
||||||
|
Fabricate(:post, user: user)
|
||||||
|
expect(granter.scores[user.id]).to eq(0.8)
|
||||||
|
end
|
||||||
|
|
||||||
|
it "is limited to two accounts" do
|
||||||
|
u1 = Fabricate(:user, created_at: 1.week.ago)
|
||||||
|
u2 = Fabricate(:user, created_at: 2.weeks.ago)
|
||||||
|
u3 = Fabricate(:user, created_at: 3.weeks.ago)
|
||||||
|
|
||||||
|
Fabricate(:post, user: u1)
|
||||||
|
Fabricate(:post, user: u2)
|
||||||
|
Fabricate(:post, user: u3)
|
||||||
|
|
||||||
|
expect(granter.scores.keys.size).to eq(2)
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
|
|
||||||
|
end
|
||||||
Reference in New Issue
Block a user