Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Badges for 48in24 #7207

Open
wants to merge 10 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ def status(exercise)
completed_exercises = completions[exercise[:slug]].to_a
return :in_progress if completed_exercises.blank?

num_completions_in_2024 = completed_exercises.count { |(_, year)| year == 2024 }
num_completions_in_2024 = completed_exercises.count { |(_, date)| date[0..3] == '2024' || date == '2025-01-01' || date == '2023-12-31' }
glennj marked this conversation as resolved.
Show resolved Hide resolved
return :in_progress if num_completions_in_2024.zero?
return :bronze if num_completions_in_2024 < 3

Expand All @@ -86,7 +86,7 @@ def completions
user.solutions.completed.
joins(exercise: :track).
where(exercise: { slug: EXERCISES.pluck(:slug) }).
pluck('exercise.slug', 'tracks.slug', 'YEAR(completed_at)').
pluck('exercise.slug', 'tracks.slug', "DATE_FORMAT(completed_at, '%Y-%m-%d')").
glennj marked this conversation as resolved.
Show resolved Hide resolved
group_by(&:first).
transform_values { |entries| entries.map { |entry| entry[1..] } }
end
Expand Down
15 changes: 15 additions & 0 deletions app/models/badges/larisa_latynina.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module Badges
class LarisaLatyninaBadge < Badge
seed "Larisa Latynina",
:rare,
'larisa-latynina',
'Earned 48 medals in the #48in24 challenge'

def award_to?(user)
def exercises = User::Challenges::FeaturedExercisesProgress48In24.(user)
exercises.none? {|e| e.status == :in_progress}
end

def send_email_on_acquisition? = true
end
end
16 changes: 16 additions & 0 deletions app/models/badges/paavo_nurmi.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
module Badges
class PaavoNurmiBadge < Badge
seed "Paavo Nurmi",
:ultimate,
'paavo-nurmi',
'Earned 48 gold or silver medals in the #48in24 challenge'

def award_to?(user)
def exercises = User::Challenges::FeaturedExercisesProgress48In24.(user)
exercises.none? {|e| e.status == :in_progress} &&
exercises.none? {|e| e.status == :bronze}
end

def send_email_on_acquisition? = true
end
end
15 changes: 15 additions & 0 deletions app/models/badges/participant_in_48_in_24_badge.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module Badges
class ParticipantIn48In24Badge < Badge
seed "#48in24 Participant",
:common,
'48in24',
'Participated in the #48in24 challenge and achieved a medal'

def award_to?(user)
def exercises = User::Challenges::FeaturedExercisesProgress48In24.(user)
exercises.any? {|e| e.status != :in_progress}
end

def send_email_on_acquisition? = true
end
end
15 changes: 15 additions & 0 deletions app/models/badges/usain_bolt.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module Badges
class UsainBoltBadge < Badge
seed "Usain Bolt",
:legendary,
'usain-bolt',
'Earned 48 gold medals in the #48in24 challenge'

def award_to?(user)
def exercises = User::Challenges::FeaturedExercisesProgress48In24.(user)
exercises.all? {|e| e.status == :gold}
end

def send_email_on_acquisition? = true
end
end
100 changes: 100 additions & 0 deletions test/models/badges/larisa_latynina_badge_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
require "test_helper"

class Badges::LarisaLatyninaBadgeTest < ActiveSupport::TestCase
test "attributes" do
badge = create :larisa_latynina_badge
assert_equal "Larisa Latynina", badge.name
assert_equal :rare, badge.rarity
assert_equal :'larisa-latynina', badge.icon
assert_equal 'Earned 48 medals in the #48in24 challenge', badge.description
assert badge.send_email_on_acquisition?
assert_nil badge.notification_key
end

test "award_to?" do
badge = create :larisa_latynina_badge
exercises = User::Challenges::FeaturedExercisesProgress48In24.EXERCISES
week1 = exercises.select {|e| e[:week] == 1}
tracks = exercises.map {|e| e[:featured_tracks]}.flatten.uniq
map {|track_slug| [track_slug.to_sym, create(:track, slug: track_slug)]}
.to_h
user = create :user

# No solutions
refute badge.award_to?(user.reload)

create :user_challenge, user:, challenge_id: '48in24'
refute badge.award_to?(user.reload)

# One bronze all year
exercise = create(:practice_exercise, track: track[:csharp], slug: week1[:slug])
create(:practice_solution, :published, user:, track: track[:csharp], exercise:,
published_at: Time.utc(2024, SecureRandom.rand(1..12), SecureRandom.rand(1..28)))

refute badge.award_to?(user.reload)

# One silver medal: csharp+tcl+wren are never the featured tracks for an exercise
[:tcl, :wren].each do |track_slug|
exercise = create(:practice_exercise, track: track[track_slug], slug: week1[:slug])
create(:practice_solution, :published, user:, track: track[track_slug], exercise:,
published_at: Time.utc(2024, SecureRandom.rand(1..12), SecureRandom.rand(1..28)))
end

refute badge.award_to?(user.reload)

# One gold medal
week1[:featured_tracks].each do |track_slug|
create(:practice_solution, :published, user:, track: track[track_slug.to_sym], exercise:,
published_at: Time.utc(2024, SecureRandom.rand(1..12), SecureRandom.rand(1..28)))
end

refute badge.award_to?(user.reload)

# in addition to week 1 gold, add 46 bronze medals: 47 medals does not qualify
exercises.select {|e| !(e[:week] == 1 || e[:week] == 48)}.each do |e|
exercise = create(:practice_exercise, track: track[:csharp], slug: e[:slug])
create(:practice_solution, :published, user:, track: track[:csharp], exercise:,
published_at: Time.utc(2024, SecureRandom.rand(1..12), SecureRandom.rand(1..28)))
end

refute badge.award_to?(user.reload)

# add week 48 solution, not in 2024
week48 = exercises.select {|e| e[:week] == 48}
exercise = create(:practice_exercise, track: track[:csharp], slug: week48[:slug])
solution = create(:practice_solution, :published, user:, track: track[:csharp], exercise:,
published_at: Time.utc(2023, SecureRandom.rand(1..12), SecureRandom.rand(1..28)))

refute badge.award_to?(user.reload)

# add an iteration to week 48 to qualify
iteration = create(:iteration, solution:)
iteration.update(created_at: Time.utc(2024, 1, 1, 0, 0, 0)

assert badge.award_to?(user.reload)

# 48 gold or silver medals
exercises.select {|e| !(e[:week] == 1}.each do |e|
[:tcl, :wren].each do |t|
exercise = create(:practice_exercise, track: track[t], slug: e[:slug])
create(:practice_solution, :published, user:, track: track[t], exercise:,
published_at: Time.utc(2024, SecureRandom.rand(1..12), SecureRandom.rand(1..28)))
end
end

assert badge.award_to?(user.reload)

# 48 gold medals
exercises.select {|e| !(e[:week] == 1}.each do |e|
e[:featured_tracks].map(&:to_sym).each do |t|
next if [:csharp, :tcl, :wren].contains(t) # already have these

exercise = create(:practice_exercise, track: track[t], slug: e[:slug])
create(:practice_solution, :published, user:, track: track[t], exercise:,
published_at: Time.utc(2024, SecureRandom.rand(1..12), SecureRandom.rand(1..28)))
end
end

assert badge.award_to?(user.reload)
end
end
100 changes: 100 additions & 0 deletions test/models/badges/paavo_nurmi_badge_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
require "test_helper"

class Badges::PaavoNurmiBadgeTest < ActiveSupport::TestCase
test "attributes" do
badge = create :paavo_nurmi_badge
assert_equal "Paavo Nurmi", badge.name
assert_equal :ultimate, badge.rarity
assert_equal :'paavo-nurmi', badge.icon
assert_equal 'Earned 48 gold or silver medals in the #48in24 challenge', badge.description
assert badge.send_email_on_acquisition?
assert_nil badge.notification_key
end

test "award_to?" do
badge = create :paavo_nurmi_badge
exercises = User::Challenges::FeaturedExercisesProgress48In24.EXERCISES
week1 = exercises.select {|e| e[:week] == 1}
tracks = exercises.map {|e| e[:featured_tracks]}.flatten.uniq
map {|track_slug| [track_slug.to_sym, create(:track, slug: track_slug)]}
.to_h
user = create :user

# No solutions
refute badge.award_to?(user.reload)

create :user_challenge, user:, challenge_id: '48in24'
refute badge.award_to?(user.reload)

# One bronze all year
exercise = create(:practice_exercise, track: track[:csharp], slug: week1[:slug])
create(:practice_solution, :published, user:, track: track[:csharp], exercise:,
published_at: Time.utc(2024, SecureRandom.rand(1..12), SecureRandom.rand(1..28)))

refute badge.award_to?(user.reload)

# One silver medal: csharp+tcl+wren are never the featured tracks for an exercise
[:tcl, :wren].each do |track_slug|
exercise = create(:practice_exercise, track: track[track_slug], slug: week1[:slug])
create(:practice_solution, :published, user:, track: track[track_slug], exercise:,
published_at: Time.utc(2024, SecureRandom.rand(1..12), SecureRandom.rand(1..28)))
end

refute badge.award_to?(user.reload)

# One gold medal
week1[:featured_tracks].each do |track_slug|
create(:practice_solution, :published, user:, track: track[track_slug.to_sym], exercise:,
published_at: Time.utc(2024, SecureRandom.rand(1..12), SecureRandom.rand(1..28)))
end

refute badge.award_to?(user.reload)

# in addition to week 1 gold, add 46 bronze medals: 47 medals does not qualify
exercises.select {|e| !(e[:week] == 1 || e[:week] == 48)}.each do |e|
exercise = create(:practice_exercise, track: track[:csharp], slug: e[:slug])
create(:practice_solution, :published, user:, track: track[:csharp], exercise:,
published_at: Time.utc(2024, SecureRandom.rand(1..12), SecureRandom.rand(1..28)))
end

refute badge.award_to?(user.reload)

# add week 48 solution, not in 2024
week48 = exercises.select {|e| e[:week] == 48}
exercise = create(:practice_exercise, track: track[:csharp], slug: week48[:slug])
solution = create(:practice_solution, :published, user:, track: track[:csharp], exercise:,
published_at: Time.utc(2023, SecureRandom.rand(1..12), SecureRandom.rand(1..28)))

refute badge.award_to?(user.reload)

# add an iteration to week 48
iteration = create(:iteration, solution:)
iteration.update(created_at: Time.utc(2024, 1, 1, 0, 0, 0)

refute badge.award_to?(user.reload)

# 48 gold or silver medals
exercises.select {|e| !(e[:week] == 1}.each do |e|
[:tcl, :wren].each do |t|
exercise = create(:practice_exercise, track: track[t], slug: e[:slug])
create(:practice_solution, :published, user:, track: track[t], exercise:,
published_at: Time.utc(2024, SecureRandom.rand(1..12), SecureRandom.rand(1..28)))
end
end

assert badge.award_to?(user.reload)

# 48 gold medals
exercises.select {|e| !(e[:week] == 1}.each do |e|
e[:featured_tracks].map(&:to_sym).each do |t|
next if [:csharp, :tcl, :wren].contains(t) # already have these

exercise = create(:practice_exercise, track: track[t], slug: e[:slug])
create(:practice_solution, :published, user:, track: track[t], exercise:,
published_at: Time.utc(2024, SecureRandom.rand(1..12), SecureRandom.rand(1..28)))
end
end

assert badge.award_to?(user.reload)
end
end
66 changes: 66 additions & 0 deletions test/models/badges/participant_in_48_in_24_badge_test.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
require "test_helper"

class Badge::ParticipantIn12In23BadgeTest < ActiveSupport::TestCase
test "attributes" do
badge = create :participant_in_48_in_24_badge
assert_equal "#48in24 Participant", badge.name
assert_equal :common, badge.rarity
assert_equal :'48in24', badge.icon
assert_equal 'Participated in the #48in24 challenge and achieved a medal', badge.description
assert badge.send_email_on_acquisition?
assert_nil badge.notification_key
end

test "award_to?" do
badge = create :participant_in_48_in_24_badge
exercises = User::Challenges::FeaturedExercisesProgress48In24.EXERCISES
week1_slug = 'leap'
user = create :user

# No solutions
refute badge.award_to?(user.reload)

create :user_challenge, user:, challenge_id: '48in24'
refute badge.award_to?(user.reload)

# One exercise before 2024 does not qualify
exercise = create(:practice_exercise, track: track[:csharp], slug: week1_slug)
create(:practice_solution, :published, user:, track: track[:csharp], exercise:,
published_at: Time.utc(2023, 12, 30))
refute badge.award_to?(user.reload)

# One exercise after 2024 does not qualify
exercise = create(:practice_exercise, track: track[:tcl], slug: week1_slug)
create(:practice_solution, :published, user:, track: track[:tcl], exercise:,
published_at: Time.utc(2025, 1, 2))
refute badge.award_to?(user.reload)

# One exercise in 2024 for a non-featured exercise does not qualify
exercise = create(:practice_exercise, track: track[:wren], slug: 'hello-world')
create(:practice_solution, :published, user:, track: track[:wren], exercise:,
published_at: Time.utc(2024, SecureRandom.rand(1..12), SecureRandom.rand(1..28)))
refute badge.award_to?(user.reload)

# One exercise in 2024
exercise = create(:practice_exercise, track: track[:wren], slug: week1_slug)
create(:practice_solution, :published, user:, track: track[:wren], exercise:,
published_at: Time.utc(2024, SecureRandom.rand(1..12), SecureRandom.rand(1..28)))
assert badge.award_to?(user.reload)

# One exercise on Dec 31, 2023
user2 = create :user
create :user_challenge, user: user2, challenge_id: '48in24'
exercise = create(:practice_exercise, track: track[:csharp], slug: week1_slug)
create(:practice_solution, :published, user: user2, track: track[:csharp], exercise:,
published_at: Time.utc(2023, 12, 31))
assert badge.award_to?(user2.reload)

# One exercise on Jan 1, 2025
user3 = create :user
create :user_challenge, user: user3, challenge_id: '48in24'
exercise = create(:practice_exercise, track: track[:csharp], slug: week1_slug)
create(:practice_solution, :published, user: user3, track: track[:csharp], exercise:,
published_at: Time.utc(2025, 1, 1))
assert badge.award_to?(user3.reload)
end
end
Loading
Loading