diff --git a/mod/quiz/classes/external/create_grade_item_per_section.php b/mod/quiz/classes/external/create_grade_item_per_section.php index 630e8eee101..657827b1647 100644 --- a/mod/quiz/classes/external/create_grade_item_per_section.php +++ b/mod/quiz/classes/external/create_grade_item_per_section.php @@ -85,6 +85,16 @@ class create_grade_item_per_section extends external_api { $gradeitemsids = []; foreach ($structure->get_sections() as $section) { + // Only create a grade item for sections that contain at least one real question (not description). + $hasrealquestion = false; + foreach ($structure->get_slots_in_section($section->id) as $slot) { + $hasrealquestion = $hasrealquestion || $structure->is_real_question($slot); + } + if (!$hasrealquestion) { + continue; + } + + // Grade item required. Create it. $gradeitem = new stdClass(); $gradeitem->quizid = $quizid; $gradeitem->name = $section->heading; @@ -93,7 +103,9 @@ class create_grade_item_per_section extends external_api { } foreach ($structure->get_slots() as $slot) { - $structure->update_slot_grade_item($slot, $gradeitemsids[$slot->section->id]); + if ($structure->is_real_question($slot->slot)) { + $structure->update_slot_grade_item($slot, $gradeitemsids[$slot->section->id]); + } } $transaction->allow_commit(); diff --git a/mod/quiz/classes/structure.php b/mod/quiz/classes/structure.php index 15a72d372d1..cd451d5ffa7 100644 --- a/mod/quiz/classes/structure.php +++ b/mod/quiz/classes/structure.php @@ -1185,6 +1185,10 @@ class structure { $gradeitemid = null; } + if ($gradeitemid !== null && !$this->is_real_question($slot->slot)) { + throw new coding_exception('Cannot set a grade item for a question that is ungraded.'); + } + if ($slot->quizgradeitemid !== null) { // Object $slot likely comes from the database, which means int may be // represented as a string, which breaks the next test, so fix up. diff --git a/mod/quiz/tests/external/grade_items_test.php b/mod/quiz/tests/external/grade_items_test.php index 4a31eb1275c..60d704f7096 100644 --- a/mod/quiz/tests/external/grade_items_test.php +++ b/mod/quiz/tests/external/grade_items_test.php @@ -249,6 +249,52 @@ final class grade_items_test extends externallib_advanced_testcase { $this->assertEquals($gradeitems[1]->id, $structure->get_slot_by_number(3)->quizgradeitemid); } + public function test_create_grade_item_per_section_with_descriptions(): void { + global $SITE; + $this->resetAfterTest(); + $this->setAdminUser(); + + // Create a quiz with no grade items yet, but two sections. + /** @var \mod_quiz_generator $quizgenerator */ + $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz'); + $quiz = $quizgenerator->create_instance(['course' => $SITE->id]); + + // Create three questions. + /** @var core_question_generator $questiongenerator */ + $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); + $cat = $questiongenerator->create_question_category(); + $desc1 = $questiongenerator->create_question('description', null, ['category' => $cat->id]); + $desc2 = $questiongenerator->create_question('description', null, ['category' => $cat->id]); + $saq1 = $questiongenerator->create_question('shortanswer', null, ['category' => $cat->id]); + + // Add them to the quiz. + quiz_add_quiz_question($desc1->id, $quiz, 1); + quiz_add_quiz_question($desc2->id, $quiz, 2); + quiz_add_quiz_question($saq1->id, $quiz, 2, 7); + + // Create two sections. + $quizobj = quiz_settings::create($quiz->id); + $structure = $quizobj->get_structure(); + $defaultsection = array_values($structure->get_sections())[0]; + $structure->set_section_heading($defaultsection->id, 'Introduction'); + $structure->add_section_heading(2, 'The question'); + + // Call the method we are testing. + create_grade_item_per_section::execute($quizobj->get_quizid()); + + // Verify. + $structure = $quizobj->get_structure(); + + $gradeitems = array_values($structure->get_grade_items()); + $this->assertCount(1, $gradeitems); + $this->assertEquals('The question', $gradeitems[0]->name); + $this->assertEquals(1, $gradeitems[0]->sortorder); + + $this->assertNull($structure->get_slot_by_number(1)->quizgradeitemid); + $this->assertNull($structure->get_slot_by_number(2)->quizgradeitemid); + $this->assertEquals($gradeitems[0]->id, $structure->get_slot_by_number(3)->quizgradeitemid); + } + public function test_create_grade_item_per_section_service_checks_permissions(): void { global $SITE; $this->resetAfterTest(); diff --git a/mod/quiz/tests/structure_test.php b/mod/quiz/tests/structure_test.php index afa9d33cf90..ae5bee166bb 100644 --- a/mod/quiz/tests/structure_test.php +++ b/mod/quiz/tests/structure_test.php @@ -16,6 +16,8 @@ namespace mod_quiz; +use core\exception\coding_exception; + defined('MOODLE_INTERNAL') || die(); global $CFG; @@ -939,6 +941,20 @@ class structure_test extends \advanced_testcase { $this->assertFalse($structure->update_slot_grade_item($slot, null)); } + public function test_cannot_set_nonnull_slot_grade_item_for_description(): void { + $quizobj = $this->create_test_quiz([ + ['Info', 1, 'description'], + ]); + /** @var \mod_quiz_generator $quizgenerator */ + $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz'); + $gradeitem = $quizgenerator->create_grade_item( + ['quizid' => $quizobj->get_quizid(), 'name' => 'Awesomeness!']); + $structure = structure::create_for_quiz($quizobj); + + $this->expectException(coding_exception::class); + $structure->update_slot_grade_item($structure->get_slot_by_number(1), $gradeitem->id); + } + /** * Test for can_add_random_questions. */