From 564eed0b6b212ddbb102b9830b1fb8ef998b48c6 Mon Sep 17 00:00:00 2001 From: mudith-perera Date: Fri, 8 May 2026 14:30:21 +1000 Subject: [PATCH] feat(peer-progress): Peer anonymized data fetch --- app/api/projects_api.rb | 40 +++++++++++++++++++++++++++++++++++ test/api/projects_api_test.rb | 32 ++++++++++++++++++++++++++++ 2 files changed, 72 insertions(+) diff --git a/app/api/projects_api.rb b/app/api/projects_api.rb index dc3cbafc62..cf74ab9189 100644 --- a/app/api/projects_api.rb +++ b/app/api/projects_api.rb @@ -34,6 +34,46 @@ class ProjectsApi < Grape::API end end + desc 'Get anonymized peer progress for a project unit' + params do + requires :id, type: Integer, desc: 'The id of the project to get peer progress for' + end + get '/projects/:id/peer_progress' do + project = Project.eager_load(:unit, :user).find(params[:id]) + + unless authorise? current_user, project, :get + error!({ error: "Couldn't find Project with id=#{params[:id]}" }, 403) + end + + peers = project.unit.active_projects.includes(:user).order('projects.id ASC').map.with_index(1) do |peer_project, index| + task_stats = begin + peer_project.task_stats.present? ? JSON.parse(peer_project.task_stats) : {} + rescue StandardError + {} + end + progress = task_stats['order_scale'].to_f.round + progress = 0 if progress.negative? + progress = 100 if progress > 100 + + { + alias: "Peer #{index.to_s.rjust(2, '0')}", + progress: progress, + band_label: if progress >= 85 + 'Leading' + elsif progress >= 75 + 'On Track' + elsif progress >= 65 + 'Building' + else + 'Needs Support' + end, + is_current_student: peer_project.id == project.id + } + end + + present peers, with: Grape::Presenters::Presenter + end + desc 'Update a project' params do optional :trigger, type: String, desc: 'The update trigger' diff --git a/test/api/projects_api_test.rb b/test/api/projects_api_test.rb index 6d4ab3087c..f7ba2d6dff 100644 --- a/test/api/projects_api_test.rb +++ b/test/api/projects_api_test.rb @@ -112,6 +112,38 @@ def test_projects_works_with_inactive_units end end + def test_get_project_peer_progress_response_is_correct + unit = FactoryBot.create(:unit) + project = unit.active_projects.first + + add_auth_header_for(user: project.student) + + get "/api/projects/#{project.id}/peer_progress" + assert_equal 200, last_response.status, last_response_body + assert_equal unit.active_projects.count, last_response_body.count + + current_student_entries = last_response_body.select { |entry| entry['is_current_student'] } + assert_equal 1, current_student_entries.count + + last_response_body.each_with_index do |entry, index| + assert_json_limit_keys_to_exactly %w(alias progress band_label is_current_student), entry + assert_equal "Peer #{(index + 1).to_s.rjust(2, '0')}", entry['alias'] + assert entry['progress'].between?(0, 100), entry.inspect + assert %w(Leading On\ Track Building Needs\ Support).include?(entry['band_label']), entry.inspect + end + end + + def test_get_project_peer_progress_requires_project_access + unit = FactoryBot.create(:unit) + project = unit.active_projects.first + other_user = FactoryBot.create(:user, :student, enrol_in: 1) + + add_auth_header_for(user: other_user) + + get "/api/projects/#{project.id}/peer_progress" + assert_equal 403, last_response.status + end + def test_submitted_grade_cant_change_after_submission user = FactoryBot.create(:user, :student, enrol_in: 1) project = user.projects.first