diff --git a/mod/assign/lang/en/assign.php b/mod/assign/lang/en/assign.php index 57563937fd9..6939ac3edf1 100644 --- a/mod/assign/lang/en/assign.php +++ b/mod/assign/lang/en/assign.php @@ -90,9 +90,12 @@ $string['attempthistory'] = 'Previous attempts'; $string['attemptnumber'] = 'Attempt number'; $string['attemptsettings'] = 'Attempt settings'; $string['attemptreopenmethod'] = 'Grant attempts'; +$string['attemptreopenmethod_automatic'] = 'Automatically'; +$string['attemptreopenmethod_automatic_help'] = 'After each attempt, the next is granted automatically.'; $string['attemptreopenmethod_help'] = 'This setting controls how students are granted attempts for this assignment. For each attempt, the grade and feedback are saved, and can be viewed by the teacher and the student. The available options are: * Manually - After each attempt, you can grant the next one through the Submissions page or the Grader page. +* Automatically - After each attempt, the next is granted automatically. * Automatically until pass - After each attempt, the next will be granted automatically, until the student achieves the passing grade.'; $string['attemptreopenmethod_manual'] = 'Manually'; $string['attemptreopenmethod_manual_help'] = 'After each attempt, you can grant the next one through the Submissions page or the Grader page.'; diff --git a/mod/assign/locallib.php b/mod/assign/locallib.php index 4e9c81d9cf6..fbb8935a37c 100644 --- a/mod/assign/locallib.php +++ b/mod/assign/locallib.php @@ -53,6 +53,7 @@ define('ASSIGN_MARKER_FILTER_NO_MARKER', -1); */ define('ASSIGN_ATTEMPT_REOPEN_METHOD_NONE', 'none'); define('ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL', 'manual'); +define('ASSIGN_ATTEMPT_REOPEN_METHOD_AUTOMATIC', 'automatic'); define('ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS', 'untilpass'); // Special value means allow unlimited attempts. @@ -8699,21 +8700,27 @@ class assign { $submission->attemptnumber >= ($instance->maxattempts - 1) && $instance->maxattempts != ASSIGN_UNLIMITED_ATTEMPTS; $shouldreopen = false; - if ($instance->attemptreopenmethod == ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS) { - // Check the gradetopass from the gradebook. - $gradeitem = $this->get_grade_item(); - if ($gradeitem) { - $gradegrade = grade_grade::fetch(array('userid' => $userid, 'itemid' => $gradeitem->id)); + switch ($instance->attemptreopenmethod) { + case ASSIGN_ATTEMPT_REOPEN_METHOD_AUTOMATIC: + $shouldreopen = true; + break; + case ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS: + // Check the gradetopass from the gradebook. + $gradeitem = $this->get_grade_item(); + if ($gradeitem) { + $gradegrade = grade_grade::fetch(['userid' => $userid, 'itemid' => $gradeitem->id]); - // Do not reopen if is_passed returns null, e.g. if there is no pass criterion set. - if ($gradegrade && ($gradegrade->is_passed() === false)) { + // Do not reopen if is_passed returns null, e.g. if there is no pass criterion set. + if ($gradegrade && ($gradegrade->is_passed() === false)) { + $shouldreopen = true; + } + } + break; + case ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL: + if (!empty($addattempt)) { $shouldreopen = true; } - } - } - if ($instance->attemptreopenmethod == ASSIGN_ATTEMPT_REOPEN_METHOD_MANUAL && - !empty($addattempt)) { - $shouldreopen = true; + break; } if ($shouldreopen && !$maxattemptsreached) { $this->add_attempt($userid); diff --git a/mod/assign/mod_form.php b/mod/assign/mod_form.php index 1140f3b2d11..475f929639e 100644 --- a/mod/assign/mod_form.php +++ b/mod/assign/mod_form.php @@ -150,6 +150,11 @@ class mod_assign_mod_form extends moodleform_mod { name: get_string('attemptreopenmethod_manual', 'mod_assign'), definition: ['description' => get_string('attemptreopenmethod_manual_help', 'mod_assign')] ); + $choice->add_option( + value: ASSIGN_ATTEMPT_REOPEN_METHOD_AUTOMATIC, + name: get_string('attemptreopenmethod_automatic', 'mod_assign'), + definition: ['description' => get_string('attemptreopenmethod_automatic_help', 'mod_assign')] + ); $choice->add_option( value: ASSIGN_ATTEMPT_REOPEN_METHOD_UNTILPASS, name: get_string('attemptreopenmethod_untilpass', 'mod_assign'), diff --git a/mod/assign/tests/locallib_test.php b/mod/assign/tests/locallib_test.php index 1808f48b74d..c9e7a6865fe 100644 --- a/mod/assign/tests/locallib_test.php +++ b/mod/assign/tests/locallib_test.php @@ -2758,6 +2758,66 @@ class locallib_test extends \advanced_testcase { $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign'))); } + /** + * Test reopen behavior when in "Automatic" mode. + * + * @coversNothing + */ + public function test_attempt_reopen_method_automatic(): void { + global $PAGE; + + $this->resetAfterTest(); + $course = $this->getDataGenerator()->create_course(); + $student = $this->getDataGenerator()->create_and_enrol($course, 'student'); + $teacher = $this->getDataGenerator()->create_and_enrol($course, 'teacher'); + + $assign = $this->create_instance($course, [ + 'attemptreopenmethod' => ASSIGN_ATTEMPT_REOPEN_METHOD_AUTOMATIC, + 'maxattempts' => 3, + 'submissiondrafts' => 1, + 'assignsubmission_onlinetext_enabled' => 1, + ]); + $PAGE->set_url(new \moodle_url('/mod/assign/view.php', ['id' => $assign->get_course_module()->id])); + + // Set grade to pass to 80. + $gradeitem = $assign->get_grade_item(); + $gradeitem->gradepass = '80.0'; + $gradeitem->update(); + + // Student should be able to see an add submission button. + $this->setUser($student); + $output = $assign->view_submission_action_bar($assign->get_instance(), $student); + $this->assertNotEquals(false, strpos($output, get_string('addsubmission', 'assign'))); + + // Add a submission as a student. + $this->add_submission($student, $assign); + $this->submit_for_grading($student, $assign); + + // Verify the student cannot make a new attempt. + $output = $assign->view_student_summary($student, true); + $this->assertEquals(false, strpos($output, get_string('addnewattempt', 'assign'))); + + // Mark the submission as non-passing. + $this->mark_submission($teacher, $assign, $student, 50.0); + + // Check the student now has a button for Add a new attempt. + $this->setUser($student); + $output = $assign->view_submission_action_bar($assign->get_instance(), $student); + $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign'))); + + // Add a second submission. + $this->add_submission($student, $assign); + $this->submit_for_grading($student, $assign); + + // Mark the submission as passing. + $this->mark_submission($teacher, $assign, $student, 80.0, [], 1); + + // Check the student now has a button for Add a new attempt. + $this->setUser($student); + $output = $assign->view_submission_action_bar($assign->get_instance(), $student); + $this->assertNotEquals(false, strpos($output, get_string('addnewattempt', 'assign'))); + } + /** * Test student visibility for each stage of the marking workflow. */ diff --git a/mod/assign/version.php b/mod/assign/version.php index 2a15deefeee..acd834fe27e 100644 --- a/mod/assign/version.php +++ b/mod/assign/version.php @@ -25,5 +25,5 @@ defined('MOODLE_INTERNAL') || die(); $plugin->component = 'mod_assign'; // Full name of the plugin (used for diagnostics). -$plugin->version = 2024042202; // The current module version (Date: YYYYMMDDXX). +$plugin->version = 2024053100; // The current module version (Date: YYYYMMDDXX). $plugin->requires = 2024041600; // Requires this Moodle version.