-
-
Notifications
You must be signed in to change notification settings - Fork 132
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
Add track trophies! #5749
Add track trophies! #5749
Conversation
41ecf56
to
5296b29
Compare
app/commands/git/sync_track.rb
Outdated
@@ -53,6 +53,7 @@ def call | |||
) | |||
|
|||
Git::SyncTrackDocs.(track, force_sync:) | |||
Track::Trophy::ReseedVariableTrophies.() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This command will reseed any trophies, which allowed tracks depend on some state of the track.
For example, the trophy to complete learning mode only applies to tracks with learning mode enabled, but that value might change.
What we do here is after syncing the track (meaning: its state might have changed), we'll reseed the variable trophies (like the aforementioned one), updating the track slugs dynamically.
Track::Trophies::Shared::CompletedFiveHardExercisesTrophy, | ||
Track::Trophies::Shared::CompletedLearningModeTrophy |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Both these trophies are dependent on the state of the track
app/jobs/award_trophy_job.rb
Outdated
def self.worth_queuing?(track, category, slug, context:) | ||
# General trophies apply to _all_ tracks, so there won't be any track filtering. | ||
# If the context is also empty, there is no possibility of the trophy not being | ||
# worth queuing | ||
return true if category == :general && context.empty? | ||
|
||
args = { track: } | ||
args[context.class.base_class.name.demodulize.underscore] = context if context.present? | ||
|
||
badge = "Track::Trophies::#{category.to_s.camelize}::#{slug.to_s.camelize}Trophy".safe_constantize | ||
badge.worth_queuing?(**args.symbolize_keys) | ||
end |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I've re-used and tweaked the logic from AwardBadgeJob
a bit
app/views/tracks/show.html.haml
Outdated
@@ -3,6 +3,6 @@ | |||
#page-track-show | |||
= render ViewComponents::Track::Header.new(@track, :overview) | |||
|
|||
= render "tracks/show/summary_article", track: @track, user_track: @user_track, exercise_statuses: @exercise_statuses, recent_solutions: @recent_solutions, sample_learnt_concepts: @sample_learnt_concepts, sample_mastered_concepts: @sample_mastered_concepts, last_8_weeks_counts: @last_8_weeks_counts | |||
= render "tracks/show/summary_article", track: @track, user_track: @user_track, exercise_statuses: @exercise_statuses, recent_solutions: @recent_solutions, sample_learnt_concepts: @sample_learnt_concepts, sample_mastered_concepts: @sample_mastered_concepts, last_8_weeks_counts: @last_8_weeks_counts, forum_threads: @forum_threads, trophies: @track.trophies |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There were some linting errors that this fixes
db/migrate/20230804084334_add_trophies_mailer_communication_preference.rb
Show resolved
Hide resolved
box-shadow: 0px 4px 8px 0px rgba(var(--shadowColorMain), 0.2); | ||
} | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@dem4ron Can you rename both of these animations to be trophy-...
please.
<div className="c-alert--danger">Failed to reveal trophy</div> | ||
)} | ||
</button> | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think the logic here is a bit different to what is in my original code.
There are three states:
revealed
: The trophy gets theacquired
class. It's a button. Clicking on it opens the modalunrevealed
: The trophy gets therevealable
class. It's a button. Clicking on it opens the modal and reveals it.not_earned
: The trophy gets thenot-acquired
class. It's a div. Hovering opens a tooltip.
app/models/track/trophies/general/completed_all_exercises_trophy.rb
Outdated
Show resolved
Hide resolved
app/views/mailers/notifications_mailer/acquired_trophy.html.haml
Outdated
Show resolved
Hide resolved
Co-authored-by: Jeremy Walker <jez.walker@gmail.com>
974b1b7
to
919dc81
Compare
We can backport the trophies using: Track::Trophy::Reseed.()
trophies = %i[
completed_all_exercises
completed_fifty_percent_of_exercises
completed_five_hard_exercises
completed_learning_mode
completed_twenty_exercises
iterated_twenty_exercises
].map { |slug| Track::Trophy.lookup!(slug) }
Track.active.find_each do |track|
File.write('/tmp/trophies.txt', "#{Time.now} [#{track.slug}] start\n", mode: 'a+')
track_trophies = trophies.select { |trophy| trophy.enabled_for_track?(track) }
num_processed = 0
User.where(id: UserTrack.select(:user_id).where(track:)).find_each do |user|
next if user.id == User::GHOST_USER_ID
track_trophies.each do |trophy|
next unless trophy.award?(user, track)
begin
UserTrack::AcquiredTrophy.create(user:, track:, trophy:)
rescue ActiveRecord::RecordNotUnique
# Ignore existing trophies
end
end
num_processed += 1
File.write('/tmp/trophies.txt', "#{Time.now} [#{track.slug}] processed #{num_processed}/#{track.num_students} students\n", mode: 'a+') if num_processed % 1000 == 0
end
File.write('/tmp/trophies.txt', "#{Time.now} [#{track.slug}] done\n\n", mode: 'a+')
end
mentored_trophy = Track::Trophy.lookup!(:mentored)
Mentor::Discussion.finished.includes(request: [:student, :track]).find_each do |discussion|
user = discussion.request.student
track = discussion.request.track
begin
UserTrack::AcquiredTrophy.create(user:, track:, trophy: mentored_trophy)
rescue ActiveRecord::RecordNotUnique
# Ignore existing trophies
end
end |
76df09d
to
d8558ec
Compare
Backport progress:
|
Backporting Mentor::Discussion.finished.includes(request: [:student, :track]).find_each do |discussion|
user = discussion.request.student
track = discussion.request.track
begin
UserTrack::AcquiredTrophy.create(user:, track:, trophy: mentored_trophy)
rescue ActiveRecord::RecordNotUnique
# Ignore existing trophies
end
end |
Backporting created_at = Time.current
trophy = Track::Trophy.lookup!(:iterated_twenty_exercises)
Track.active.find_each do |track|
user_ids = Solution.joins(:exercise).
where(exercise: { track: }).
where('num_iterations >= 2').
group(:user_id).
count.
filter_map { |user_id, count| user_id if count >= 20 }
User.where(id: user_ids).find_each do |user|
UserTrack::AcquiredTrophy.create(user:, track:, trophy:, created_at:)
rescue ActiveRecord::RecordNotUnique
# Ignore existing trophies
end
end |
Backporting created_at = Time.current
trophy = Track::Trophy.lookup!(:completed_twenty_exercises)
Track.active.find_each do |track|
user_ids = Solution.where.not(completed_at: nil).joins(:exercise).
where(exercise: { track: }).
group(:user_id).
count.
filter_map { |user_id, count| user_id if count >= 20 }
User.where(id: user_ids).find_each do |user|
UserTrack::AcquiredTrophy.create(user:, track:, trophy:. created_at:)
rescue ActiveRecord::RecordNotUnique
# Ignore existing trophies
end
end |
Backporting created_at = Time.current
trophy = Track::Trophy.lookup!(:completed_fifty_percent_of_exercises)
Track.active.find_each do |track|
halfway_there = (track.exercises.where(status: %i[beta active]).count / 2).ceil
user_ids = Solution.where.not(completed_at: nil).joins(:exercise).
where(exercise: { track: }).
group(:user_id).
count.
filter_map { |user_id, count| user_id if count >= halfway_there }
User.where(id: user_ids).find_each do |user|
UserTrack::AcquiredTrophy.create(user:, track:, trophy:, created_at:)
rescue ActiveRecord::RecordNotUnique
# Ignore existing trophies
end
end |
Backporting created_at = Time.current
trophy = Track::Trophy.lookup!(:completed_all_exercises)
Track.active.find_each do |track|
num_exercises = track.exercises.where(status: %i[beta active]).count
user_ids = Solution.where.not(completed_at: nil).joins(:exercise).
where(exercise: { track: }).
group(:user_id).
count.
filter_map { |user_id, count| user_id if count >= num_exercises }
User.where(id: user_ids).find_each do |user|
UserTrack::AcquiredTrophy.create(user:, track:, trophy:, created_at:)
rescue ActiveRecord::RecordNotUnique
# Ignore existing trophies
end
end |
Backporting created_at = Time.current
trophy = Track::Trophy.lookup!(:completed_learning_mode)
Track.active.find_each do |track|
next unless track.course?
num_concept_exercises = track.concept_exercises.where(status: %i[beta active]).count
exercise_ids = track.concept_exercises.where(status: %i[beta active]).pluck(:id)
user_ids = ConceptSolution.where.not(completed_at: nil).joins(:exercise).
where(exercise: exercise_ids).
group(:user_id).
count.
filter_map { |user_id, count| user_id if count >= num_concept_exercises }
user_ids.each do |user_id|
user_track = UserTrack.find_by(track_id: track.id, user_id: user_id)
next unless user_track
next unless user_track.num_completed_concept_exercises >= num_concept_exercises
UserTrack::AcquiredTrophy.create(user: user_track.user, track:, trophy:, created_at:)
rescue ActiveRecord::RecordNotUnique
# Ignore existing trophies
end
end |
Backport created_at = Time.current
trophy = Track::Trophy.lookup!(:completed_five_hard_exercises)
Track.active.find_each do |track|
exercise_ids = track.exercises.where('difficulty >= 8').pluck(:id)
user_ids = Solution.where.not(completed_at: nil).joins(:exercise).
where(exercise: exercise_ids).
group(:user_id).
count.
filter_map { |user_id, count| user_id if count >= 5 }
User.where(id: user_ids).find_each do |user|
UserTrack::AcquiredTrophy.create(user:, track:, trophy:. created_at:)
rescue ActiveRecord::RecordNotUnique
# Ignore existing trophies
end
end |
Modal here probably needs some tweaking cc @iHiD
Screen.Recording.2023-08-15.at.13.27.48.mov
Error revealing:
Screen.Recording.2023-08-15.at.13.22.17.mov