diff --git a/app/api/submission/portfolio_evidence_api.rb b/app/api/submission/portfolio_evidence_api.rb index 660f65872..9a290ebaf 100644 --- a/app/api/submission/portfolio_evidence_api.rb +++ b/app/api/submission/portfolio_evidence_api.rb @@ -18,16 +18,6 @@ def self.logger authenticated? end - TASK_STATES = { - ready_for_feedback: 1, - assess_in_portfolio: 1, - discuss: 2, - rediscuss: 2, - attention_required: 0, - demonstrate: 2, - complete: 3 - }.freeze - desc 'Upload and generate doubtfire-task-specific submission document' params do optional :file0, type: File, desc: 'file 0.' @@ -53,32 +43,8 @@ def self.logger error!({ error: "This task requires a group submission. Ensure you are in a group for the unit's #{task_definition.group_set.name}" }, 403) end - # Check that prerequisite tasks are in the required minimum submitted state - prerequisites = task_definition.task_prerequisites - prerequisites.each do |prerequisite| - prereq_td = prerequisite.prerequisite - minimum_status = TaskStatus.find(prerequisite.task_status_id) - - - prereq_task = project.task_for_task_definition(prereq_td) - - verb = - case minimum_status - when TaskStatus.complete - 'completed' - when TaskStatus.discuss - 'discussed' - when TaskStatus.rediscuss - 'rediscussed' - when TaskStatus.demonstrate - 'demonstrated' - when TaskStatus.ready_for_feedback - 'submitted' - end - - if !prereq_task.ready_or_complete? || TASK_STATES[prereq_task.status] < TASK_STATES[minimum_status.status_key] - error!({ error: "Cannot submit this task until prerequisite '#{prereq_td.abbreviation}' has been #{verb}" }, 409) - end + if (prerequisite_error = task.unmet_prerequisite_error) + error!({ error: prerequisite_error }, 409) end trigger = if params[:trigger] && params[:trigger].tr('"\'', '') == 'need_help' diff --git a/app/api/tasks_api.rb b/app/api/tasks_api.rb index bd5f0552e..b3f71014c 100644 --- a/app/api/tasks_api.rb +++ b/app/api/tasks_api.rb @@ -178,6 +178,11 @@ class TasksApi < Grape::API error!({ error: 'Cannot set this task status to ready to mark without uploading documents.' }, 403) end + if !needs_upload_docs && %w[ready_for_feedback assess_in_portfolio].include?(params[:trigger]) && + (prerequisite_error = task.unmet_prerequisite_error) + error!({ error: prerequisite_error }, 409) + end + if params[:trigger] == 'assess_in_portfolio' && !authorise?(current_user, project, :assess) # Prevent students from upading status if task definition doesn't enable it if !task_definition.assess_in_portfolio_only diff --git a/app/models/task.rb b/app/models/task.rb index 2747b9e09..fda1694e9 100644 --- a/app/models/task.rb +++ b/app/models/task.rb @@ -7,6 +7,16 @@ class Task < ApplicationRecord include ApplicationHelper include GradeHelper + PREREQUISITE_STATUS_LEVELS = { + ready_for_feedback: 1, + assess_in_portfolio: 1, + discuss: 2, + rediscuss: 2, + attention_required: 0, + demonstrate: 2, + complete: 3 + }.freeze + attr_accessor :discussion_confirmed_for_transition # @@ -550,6 +560,29 @@ def ready_or_complete? [:complete, :discuss, :rediscuss, :demonstrate, :ready_for_feedback, :assess_in_portfolio].include? status end + def unmet_prerequisite_error + task_definition.task_prerequisites.each do |prerequisite| + prereq_td = prerequisite.prerequisite + minimum_status = TaskStatus.find(prerequisite.task_status_id) + prereq_task = project.task_for_task_definition(prereq_td) + + next if prereq_task.ready_or_complete? && + PREREQUISITE_STATUS_LEVELS[prereq_task.status] >= PREREQUISITE_STATUS_LEVELS[minimum_status.status_key] + + verb = case minimum_status + when TaskStatus.complete then 'completed' + when TaskStatus.discuss then 'discussed' + when TaskStatus.rediscuss then 'rediscussed' + when TaskStatus.demonstrate then 'demonstrated' + when TaskStatus.ready_for_feedback then 'submitted' + end + + return "Cannot submit this task until prerequisite '#{prereq_td.abbreviation}' has been #{verb}" + end + + nil + end + def submitted_status? [:working_on_it, :not_started, :fix_and_resubmit, :redo, :need_help].exclude? status end diff --git a/test/api/tasks_api_test.rb b/test/api/tasks_api_test.rb index f1f65e2e4..3b605bac0 100644 --- a/test/api/tasks_api_test.rb +++ b/test/api/tasks_api_test.rb @@ -653,6 +653,45 @@ def test_cant_submit_until_prerequisites_submitted unit.destroy end + def test_cant_set_upload_free_task_ready_for_feedback_until_prerequisites_met + unit = FactoryBot.create(:unit, student_count: 1, task_count: 2) + prerequisite_definition, task_definition = unit.task_definitions.first(2) + prerequisite_definition.update!(target_grade: 0) + task_definition.update!(target_grade: 3, upload_requirements: []) + project = unit.active_projects.first + task = project.task_for_task_definition(task_definition) + prerequisite_task = project.task_for_task_definition(prerequisite_definition) + prerequisite = TaskPrerequisite.create!( + task_definition: task_definition, + prerequisite: prerequisite_definition, + task_status_id: TaskStatus.ready_for_feedback.id + ) + + add_auth_header_for(user: project.user) + path = "/api/projects/#{project.id}/task_def_id/#{task_definition.id}" + + put path, { trigger: 'ready_for_feedback' } + assert_equal 409, last_response.status + assert_equal "Cannot submit this task until prerequisite '#{prerequisite_definition.abbreviation}' has been submitted", + last_response_body['error'] + assert_equal TaskStatus.not_started, task.reload.task_status + + prerequisite.update!(task_status_id: TaskStatus.complete.id) + prerequisite_task.update!(task_status: TaskStatus.ready_for_feedback) + + put path, { trigger: 'ready_for_feedback' } + assert_equal 409, last_response.status + assert_equal "Cannot submit this task until prerequisite '#{prerequisite_definition.abbreviation}' has been completed", + last_response_body['error'] + assert_equal TaskStatus.not_started, task.reload.task_status + + prerequisite_task.update!(task_status: TaskStatus.complete) + + put path, { trigger: 'ready_for_feedback' } + assert_equal 200, last_response.status + assert_equal TaskStatus.ready_for_feedback, task.reload.task_status + end + def test_prerequisites_task_status # Create a unit and two task definitions unit = FactoryBot.create(:unit, student_count: 1, task_count: 10)