diff --git a/app/commands/track/trophy/reseed_variable.rb b/app/commands/track/trophy/reseed_variable.rb
index 5e408c014b..1a2e2cf847 100644
--- a/app/commands/track/trophy/reseed_variable.rb
+++ b/app/commands/track/trophy/reseed_variable.rb
@@ -9,6 +9,7 @@ def call
VARIABLE_TROPHIES = [
Track::Trophies::CompletedFiveHardExercisesTrophy,
- Track::Trophies::CompletedLearningModeTrophy
+ Track::Trophies::CompletedLearningModeTrophy,
+ Track::Trophies::ReadFiveApproachesTrophy
].freeze
end
diff --git a/app/commands/user_track/viewed_exercise_approach/create.rb b/app/commands/user_track/viewed_exercise_approach/create.rb
index 2a88d3f3f0..b4f374d705 100644
--- a/app/commands/user_track/viewed_exercise_approach/create.rb
+++ b/app/commands/user_track/viewed_exercise_approach/create.rb
@@ -4,6 +4,8 @@ class UserTrack::ViewedExerciseApproach::Create
initialize_with :user, :track, :approach
def call
- ::UserTrack::ViewedExerciseApproach.create_or_find_by!(user:, track:, approach:)
+ ::UserTrack::ViewedExerciseApproach.create_or_find_by!(user:, track:, approach:).tap do
+ AwardTrophyJob.perform_later(user, track, :read_five_approaches)
+ end
end
end
diff --git a/app/images/graphics/trophy-read-five-approaches.svg b/app/images/graphics/trophy-read-five-approaches.svg
new file mode 100644
index 0000000000..58ad7af259
--- /dev/null
+++ b/app/images/graphics/trophy-read-five-approaches.svg
@@ -0,0 +1 @@
+
diff --git a/app/models/track/trophies/read_five_approaches_trophy.rb b/app/models/track/trophies/read_five_approaches_trophy.rb
new file mode 100644
index 0000000000..93325086ae
--- /dev/null
+++ b/app/models/track/trophies/read_five_approaches_trophy.rb
@@ -0,0 +1,38 @@
+class Track::Trophies::ReadFiveApproachesTrophy < Track::Trophy
+ def self.valid_track_slugs
+ Track.active.where(id:
+ Exercise::Approach.joins(:exercise).
+ where('exercises.track_id = tracks.id').
+ having("count(*) >= ?", NUM_APPROACHES).
+ select(:track_id)).pluck(:slug)
+ end
+
+ def name(_) = "Digging Deeper"
+ def icon = 'trophy-read-five-approaches'
+ def order = 7
+
+ # rubocop:disable Layout/LineLength
+ def criteria(track)
+ "Awarded for reading %i %s approaches" % {
+ num_approaches: NUM_APPROACHES,
+ track_title: track.title
+ }
+ end
+
+ def success_message(track)
+ "Congratulations on reading %i approaches in %s. Learning how to approach an exercise in different ways is a fantastic way to master a language!" % {
+ num_approaches: NUM_APPROACHES,
+ track_title: track.title
+ }
+ end
+ # rubocop:enable Layout/LineLength
+
+ def award?(user, track)
+ UserTrack::ViewedExerciseApproach.
+ where(user:, track:).
+ count >= NUM_APPROACHES
+ end
+
+ NUM_APPROACHES = 5
+ private_constant :NUM_APPROACHES
+end
diff --git a/test/commands/track/trophy/reseed_variable_test.rb b/test/commands/track/trophy/reseed_variable_test.rb
index 57cf4c6fb3..9dcff8eb38 100644
--- a/test/commands/track/trophy/reseed_variable_test.rb
+++ b/test/commands/track/trophy/reseed_variable_test.rb
@@ -6,13 +6,14 @@ class Track::Trophy::ReseedVariableTest < ActiveSupport::TestCase
create :iterated_twenty_exercises_trophy
create :completed_five_hard_exercises_trophy
create :completed_learning_mode_trophy
+ create :read_five_approaches_trophy
Track::Trophies::IteratedTwentyExercisesTrophy.any_instance.expects(:reseed!).never
Track::Trophies::MentoredTrophy.any_instance.expects(:reseed!).never
- Track::Trophy::ReseedVariable::VARIABLE_TROPHIES.each do |trophy|
- trophy.any_instance.expects(:reseed!).once
- end
+ Track::Trophies::CompletedFiveHardExercisesTrophy.any_instance.expects(:reseed!).once
+ Track::Trophies::CompletedLearningModeTrophy.any_instance.expects(:reseed!).once
+ Track::Trophies::ReadFiveApproachesTrophy.any_instance.expects(:reseed!).once
Track::Trophy::ReseedVariable.()
end
diff --git a/test/commands/user_track/viewed_exercise_approach/create_test.rb b/test/commands/user_track/viewed_exercise_approach/create_test.rb
index 3d350add9e..af65415c3e 100644
--- a/test/commands/user_track/viewed_exercise_approach/create_test.rb
+++ b/test/commands/user_track/viewed_exercise_approach/create_test.rb
@@ -20,4 +20,26 @@ class UserTrack::ViewedExerciseApproach::CreateTest < ActiveSupport::TestCase
assert_equal track, actual.track
assert_equal approach, actual.approach
end
+
+ test "awards read five approaches trophy when now having read five" do
+ user = create :user
+ track = create :track
+ exercise = create(:practice_exercise, track:)
+
+ perform_enqueued_jobs do
+ create_list(:exercise_approach, 4, exercise:) do |approach|
+ create(:user_track_viewed_exercise_approach, user:, track:, approach:)
+ end
+ end
+
+ refute_includes user.reload.trophies.map(&:class), Track::Trophies::ReadFiveApproachesTrophy
+
+ approach = create(:exercise_approach, exercise:)
+
+ perform_enqueued_jobs do
+ UserTrack::ViewedExerciseApproach::Create.(user, track, approach)
+ end
+
+ assert_includes user.reload.trophies.map(&:class), Track::Trophies::ReadFiveApproachesTrophy
+ end
end
diff --git a/test/factories/track/trophies.rb b/test/factories/track/trophies.rb
index 3af024da97..1338c4d9ee 100644
--- a/test/factories/track/trophies.rb
+++ b/test/factories/track/trophies.rb
@@ -6,7 +6,7 @@
completed_all_exercises completed_fifty_percent_of_exercises
completed_twenty_exercises mentored iterated_twenty_exercises
read_fifty_community_solutions completed_five_hard_exercises
- completed_learning_mode functional
+ completed_learning_mode functional read_five_approaches
].each do |type|
factory "#{type}_trophy", class: "Track::Trophies::#{type.to_s.camelize}Trophy" do
end
diff --git a/test/models/track/trophies/read_fifty_community_solutions_test.rb b/test/models/track/trophies/read_fifty_community_solutions_trophy_test.rb
similarity index 100%
rename from test/models/track/trophies/read_fifty_community_solutions_test.rb
rename to test/models/track/trophies/read_fifty_community_solutions_trophy_test.rb
diff --git a/test/models/track/trophies/read_five_approaches_trophy_test.rb b/test/models/track/trophies/read_five_approaches_trophy_test.rb
new file mode 100644
index 0000000000..91084db75e
--- /dev/null
+++ b/test/models/track/trophies/read_five_approaches_trophy_test.rb
@@ -0,0 +1,66 @@
+require "test_helper"
+
+class Track::Trophies::ReadFiveApproachesTrophyTest < ActiveSupport::TestCase
+ test "award?" do
+ user = create :user
+ other_user = create :user
+ track = create :track
+ other_track = create :track, slug: 'kotlin'
+ trophy = create :read_five_approaches_trophy
+
+ exercise = create(:practice_exercise, :random_slug, track:)
+ other_exercise = create(:practice_exercise, :random_slug, track: other_track)
+
+ # Don't award trophy if no approaches have been read
+ refute trophy.award?(user, track)
+
+ # Don't award trophy if less than five approaches
+ create_list(:exercise_approach, 4, :random, exercise:) do |approach|
+ create(:user_track_viewed_exercise_approach, user:, track:, approach:)
+ end
+ refute trophy.award?(user, track)
+
+ # Don't award trophy if other user has read five approaches in the track
+ create_list(:exercise_approach, 6, :random, exercise:) do |approach|
+ create(:user_track_viewed_exercise_approach, user: other_user, track:, approach:)
+ end
+ refute trophy.award?(user, track)
+
+ # Don't count viewed approaches for other track
+ create_list(:exercise_approach, 7, :random, exercise: other_exercise) do |approach|
+ create(:user_track_viewed_exercise_approach, user:, track: other_track, approach:)
+ end
+ refute trophy.award?(user, track)
+
+ # Award trophy when five approaches solutions in the track have been read
+ create(:exercise_approach, exercise:) do |approach|
+ create(:user_track_viewed_exercise_approach, user:, track:, approach:)
+ end
+ assert trophy.award?(user, track)
+ end
+
+ test "reseed! sets valid_track_slugs to tracks with at least five approaches" do
+ trophy = create :read_five_approaches_trophy
+
+ # Track is not valid if it doesn't have any approaches
+ track = create :track, :random_slug
+ trophy.reseed!
+ assert_empty trophy.valid_track_slugs
+
+ # Four approaches is not enough
+ create_list(:exercise_approach, 4, exercise: create(:practice_exercise, :random_slug, track:))
+ trophy.reseed!
+ assert_empty trophy.valid_track_slugs
+
+ # Five approaches is enough
+ create(:exercise_approach, exercise: create(:practice_exercise, :random_slug, track:))
+ trophy.reseed!
+ assert_equal [track.slug], trophy.valid_track_slugs
+
+ # Include all tracks with five approaches
+ other_track = create :track, :random_slug
+ create_list(:exercise_approach, 6, exercise: create(:practice_exercise, :random_slug, track: other_track))
+ trophy.reseed!
+ assert_equal [track.slug, other_track.slug].sort, trophy.valid_track_slugs.sort
+ end
+end