From ed2d15848518722d67fdb853feb86cf48476ba86 Mon Sep 17 00:00:00 2001 From: Jeremy Walker Date: Mon, 30 Oct 2023 19:52:27 +0900 Subject: [PATCH] Fix reputation fallback (#6422) --- .../solution/search_via_representations.rb | 25 ++-- .../search_via_representations_test.rb | 130 +++++------------- 2 files changed, 43 insertions(+), 112 deletions(-) diff --git a/app/commands/solution/search_via_representations.rb b/app/commands/solution/search_via_representations.rb index 380a20d43d..5e1a165ba3 100644 --- a/app/commands/solution/search_via_representations.rb +++ b/app/commands/solution/search_via_representations.rb @@ -116,45 +116,44 @@ class Fallback initialize_with :exercise, :page, :per, :order, :criteria, :tags def call - @solutions = Solution.joins(:published_exercise_representation).where(exercise:) + @representations = exercise.representations.where('num_published_solutions > 0') sort! filter! paginate! - @solutions + @representations = @representations.includes(:prestigious_solution).page(page).per(per) + + Kaminari.paginate_array(@representations.map(&:prestigious_solution), total_count: @representations.total_count). + page(page).per(per) end private attr_reader :solutions def filter! - # By grouping, we force MySQL to return just one result per group - @solutions = @solutions.group(:published_exercise_representation_id) - # We can't filter on criteria as code is not stored in the database - - @solutions = @solutions.joins(:tags).where(tags: { tag: tags }) if tags.present? + @representations = @representations.joins(prestigious_solution: :tags).where(tags: { tag: tags }) if tags.present? end def sort! case order when :newest - @solutions = @solutions.order(id: :desc) + @representations = @representations.joins(:prestigious_solution).order('solutions.id': :desc) when :oldest - @solutions = @solutions.order(id: :asc) + @representations = @representations.joins(:prestigious_solution).order('solutions.id': :asc) when :fewest_loc - @solutions = @solutions.order(num_loc: :desc) + @representations = @representations.joins(:prestigious_solution).order('solutions.num_loc': :asc) when :highest_reputation # This is not track-specific reputation, but it's fine for the fallback - @solutions = @solutions.joins(:user).order(reputation: :desc) + @representations = @representations.joins(prestigious_solution: :user).order('users.reputation': :desc) else # :most_popular - @solutions = @solutions.order(num_published_solutions: :desc, id: :asc) + @representations = @representations.order(num_published_solutions: :desc, id: :asc) end end def paginate! - @solutions = @solutions. + @representations = @representations. page(page). per(per) end diff --git a/test/commands/solution/search_via_representations_test.rb b/test/commands/solution/search_via_representations_test.rb index b85eae587e..f3944398ad 100644 --- a/test/commands/solution/search_via_representations_test.rb +++ b/test/commands/solution/search_via_representations_test.rb @@ -629,131 +629,63 @@ class Solution::SearchViaRepresentationsTest < ActiveSupport::TestCase test "fallback: sort: most popular" do exercise = create :practice_exercise - exercise_representation_1 = create(:exercise_representation, exercise:) - exercise_representation_2 = create(:exercise_representation, exercise:) - - solution_1 = create :concept_solution, exercise:, published_at: 2.days.ago, - git_important_files_hash: exercise.git_important_files_hash, - published_iteration_head_tests_status: :passed, - published_exercise_representation: exercise_representation_1 - submission = create :submission, solution: solution_1, tests_status: :passed - create :submission_representation, submission:, ast: exercise_representation_1.ast - create(:submission_file, submission:) - create(:iteration, solution: solution_1, submission:) - - solution_2 = create :concept_solution, exercise:, published_at: 2.days.ago, - git_important_files_hash: exercise.git_important_files_hash, - published_iteration_head_tests_status: :passed, - published_exercise_representation: exercise_representation_2 - submission = create :submission, solution: solution_2, tests_status: :passed - create :submission_representation, submission:, ast: exercise_representation_2.ast - create(:submission_file, submission:) - create(:iteration, solution: solution_2, submission:) - - solution_3 = create :concept_solution, exercise:, published_at: 2.days.ago, - git_important_files_hash: exercise.git_important_files_hash, - published_iteration_head_tests_status: :passed, - published_exercise_representation: exercise_representation_2 - submission = create :submission, solution: solution_3, tests_status: :passed - create :submission_representation, submission:, ast: exercise_representation_2.ast - create(:submission_file, submission:) - create(:iteration, solution: solution_3, submission:) - exercise_representation_1.update(num_published_solutions: 1) - exercise_representation_2.update(num_published_solutions: 2) + solution_1 = create :concept_solution, exercise:, published_at: 2.days.ago + solution_2 = create :concept_solution, exercise:, published_at: 2.days.ago + create(:exercise_representation, exercise:, num_published_solutions: 1, prestigious_solution: solution_1) + create(:exercise_representation, exercise:, num_published_solutions: 2, prestigious_solution: solution_2) assert_equal [solution_2, solution_1], Solution::SearchViaRepresentations::Fallback.(exercise, 1, 24, :most_popular, nil, []) end test "fallback: sort: oldest" do exercise = create :practice_exercise - exercise_representation_1 = create(:exercise_representation, exercise:) - exercise_representation_2 = create(:exercise_representation, exercise:) - solutions = [ - exercise_representation_1, - exercise_representation_2, - exercise_representation_2 - ].map do |representation| - create_solution(exercise:, representation:) - end + solution_1 = create(:concept_solution, exercise:) + solution_2 = create(:concept_solution, exercise:) + create(:exercise_representation, exercise:, num_published_solutions: 1, prestigious_solution: solution_1) + create(:exercise_representation, exercise:, num_published_solutions: 1, prestigious_solution: solution_2) - assert_equal [solutions[0], solutions[1]], Solution::SearchViaRepresentations::Fallback.(exercise, 1, 24, :oldest, nil, []) + assert_equal [solution_1, solution_2], Solution::SearchViaRepresentations::Fallback.(exercise, 1, 24, :oldest, nil, []) end test "fallback: sort: newest" do exercise = create :practice_exercise - exercise_representation_1 = create(:exercise_representation, exercise:) - exercise_representation_2 = create(:exercise_representation, exercise:) - solutions = [ - exercise_representation_1, - exercise_representation_2, - exercise_representation_2 - ].map do |representation| - create_solution(exercise:, representation:) - end + solution_1 = create(:concept_solution, exercise:) + solution_2 = create(:concept_solution, exercise:) + create(:exercise_representation, exercise:, num_published_solutions: 1, prestigious_solution: solution_1) + create(:exercise_representation, exercise:, num_published_solutions: 1, prestigious_solution: solution_2) - assert_equal [solutions[1], solutions[0]], Solution::SearchViaRepresentations::Fallback.(exercise, 1, 24, :newest, nil, []) + assert_equal [solution_2, solution_1], Solution::SearchViaRepresentations::Fallback.(exercise, 1, 24, :newest, nil, []) end test "fallback: sort: num_loc" do exercise = create :practice_exercise - exercise_representation_1 = create(:exercise_representation, exercise:) - exercise_representation_2 = create(:exercise_representation, exercise:) - solutions = [ - [exercise_representation_1, 20], - [exercise_representation_2, 50], - [exercise_representation_2, 10] - ].map do |(representation, num_loc)| - create_solution(exercise:, representation:, num_loc:) - end + solution_1 = create :concept_solution, exercise:, num_loc: 5 + solution_2 = create :concept_solution, exercise:, num_loc: 2 + solution_3 = create :concept_solution, exercise:, num_loc: 7 + create(:exercise_representation, exercise:, num_published_solutions: 1, prestigious_solution: solution_1) + create(:exercise_representation, exercise:, num_published_solutions: 1, prestigious_solution: solution_2) + create(:exercise_representation, exercise:, num_published_solutions: 1, prestigious_solution: solution_3) - assert_equal [solutions[1], solutions[0]], Solution::SearchViaRepresentations::Fallback.(exercise, 1, 24, :fewest_loc, nil, []) + assert_equal [solution_2, solution_1, solution_3].map(&:id), + Solution::SearchViaRepresentations::Fallback.(exercise, 1, 24, :fewest_loc, nil, []).map(&:id) end test "fallback: sort: highest_reputation" do - user = create :user, handle: 'john', reputation: 15 - other_user = create :user, handle: 'jane', reputation: 50 - another_user = create :user, handle: 'june', reputation: 30 - ruby = create :track, title: "Ruby" - - exercise = create :practice_exercise, track: ruby - exercise_representation_1 = create(:exercise_representation, exercise:) - exercise_representation_2 = create(:exercise_representation, exercise:) - - solution_1 = create :concept_solution, exercise:, published_at: 2.days.ago, user:, - git_important_files_hash: exercise.git_important_files_hash, - published_iteration_head_tests_status: :passed, - published_exercise_representation: exercise_representation_1, - num_loc: 20 - submission = create :submission, solution: solution_1, tests_status: :passed - create :submission_representation, submission:, ast: exercise_representation_1.ast - create :submission_file, submission:, filename: "main.rb", content: "def my_main; end" - create(:iteration, solution: solution_1, submission:) - - solution_2 = create :concept_solution, exercise:, published_at: 2.days.ago, user: other_user, - git_important_files_hash: exercise.git_important_files_hash, - published_iteration_head_tests_status: :passed, - published_exercise_representation: exercise_representation_2, - num_loc: 50 - submission = create :submission, solution: solution_2, tests_status: :passed - create :submission_representation, submission:, ast: exercise_representation_2.ast - create :submission_file, submission:, filename: "main.rb", content: "def your_main; end" - create(:iteration, solution: solution_2, submission:) + exercise = create :practice_exercise - solution_3 = create :concept_solution, exercise:, published_at: 2.days.ago, user: another_user, - git_important_files_hash: exercise.git_important_files_hash, - published_iteration_head_tests_status: :passed, - published_exercise_representation: exercise_representation_2, - num_loc: 10 - submission = create :submission, solution: solution_3, tests_status: :passed - create :submission_representation, submission:, ast: exercise_representation_2.ast - create :submission_file, submission:, filename: "main.rb", content: "def another_main; end" - create(:iteration, solution: solution_3, submission:) + solution_1 = create :concept_solution, exercise:, user: create(:user, reputation: 5) + solution_2 = create :concept_solution, exercise:, user: create(:user, reputation: 3) + solution_3 = create :concept_solution, exercise:, user: create(:user, reputation: 7) + create(:exercise_representation, exercise:, num_published_solutions: 1, prestigious_solution: solution_1) + create(:exercise_representation, exercise:, num_published_solutions: 1, prestigious_solution: solution_2) + create(:exercise_representation, exercise:, num_published_solutions: 1, prestigious_solution: solution_3) - assert_equal [solution_2, solution_1], Solution::SearchViaRepresentations::Fallback.(exercise, 1, 24, :highest_reputation, nil, []) + assert_equal [solution_3, solution_1, solution_2].map(&:id), + Solution::SearchViaRepresentations::Fallback.(exercise, 1, 24, :highest_reputation, nil, []).map(&:id) end private