From 54da83f40a5d33745251a47ed2f11cd5d072224d Mon Sep 17 00:00:00 2001 From: Mihail Geshoski Date: Thu, 26 Sep 2024 23:01:13 +0800 Subject: [PATCH] MDL-83264 mod_quiz: Return the correct name for top categories --- mod/quiz/classes/structure.php | 67 ++++++-- mod/quiz/lang/en/quiz.php | 6 + .../tests/behat/editing_add_random.feature | 151 +++++++++++++++++- 3 files changed, 210 insertions(+), 14 deletions(-) diff --git a/mod/quiz/classes/structure.php b/mod/quiz/classes/structure.php index 4a4f43ffa6848..672db9708645d 100644 --- a/mod/quiz/classes/structure.php +++ b/mod/quiz/classes/structure.php @@ -1716,7 +1716,9 @@ protected function load_random_slot_info(): void { // Loop over all random slots to build arrays of the data we will need. $tagids = []; $questioncategoriesids = []; - $randomcategoriesandtags = []; // An associative array of slotid => ['cat' => catid, 'tag' => [tagid, tagid, ...]]. + // An associative array of slotid. Example structure: + // ['cat' => [values => catid, 'includesubcategories' => true, 'tag' => [tagid, tagid, ...]]. + $randomcategoriesandtags = []; foreach ($allslots as $slotid => $slot) { foreach ($slot->filtercondition as $name => $value) { if ($name !== 'filter') { @@ -1726,8 +1728,9 @@ protected function load_random_slot_info(): void { // Parse the filter condition. foreach ($value as $filteroption => $filtervalue) { if ($filteroption === 'category') { - $randomcategoriesandtags[$slotid]['cat'] = $filtervalue['values']; - $questioncategoriesids[] = $filtervalue['values'][0]; + $randomcategoriesandtags[$slotid]['cat']['values'] = $questioncategoriesids[] = $filtervalue['values'][0]; + $randomcategoriesandtags[$slotid]['cat']['includesubcategories'] = + $filtervalue['filteroptions']['includesubcategories'] ?? false; } if ($filteroption === 'qtagids') { @@ -1752,19 +1755,13 @@ protected function load_random_slot_info(): void { // Get names for all question categories. $categories = $DB->get_records_list('question_categories', 'id', $questioncategoriesids, 'id', 'id, name, contextid, parent'); - $categorynames = []; - foreach ($categories as $id => $category) { - if ($category->name === 'top') { - $categoryname = $DB->get_field('question_categories', 'name', ['parent' => $id]); - } else { - $categoryname = $category->name; - } - $categorynames[$id] = $categoryname; - } // Now, put the data required for each slot into $this->randomslotcategories and $this->randomslottags. foreach ($randomcategoriesandtags as $slotid => $catandtags) { - $this->randomslotcategories[$slotid] = $categorynames[$catandtags['cat'][0]]; + $qcategoryid = $catandtags['cat']['values']; + $qcategory = $categories[$qcategoryid]; + $includesubcategories = $catandtags['cat']['includesubcategories']; + $this->randomslotcategories[$slotid] = $this->get_used_category_description($qcategory, $includesubcategories); if (isset($catandtags['tag'])) { $slottagnames = []; foreach ($catandtags['tag'] as $tagid) { @@ -1774,4 +1771,48 @@ protected function load_random_slot_info(): void { } } } + + /** + * Returns a description of the used question category, taking into account the context and whether subcategories are + * included. + * + * @param stdClass $qcategory The question category object containing category details. + * @param bool $includesubcategories Whether subcategories are included. + * @return string The generated description based on the used category. + * @throws coding_exception If the context level is unsupported. + */ + private function get_used_category_description(stdClass $qcategory, bool $includesubcategories): string { + if ($qcategory->name === 'top') { // This is a "top" question category. + if (!$includesubcategories) { + // Question categories labeled as "top" cannot directly contain questions. If the subcategories that may + // hold questions are excluded, the generated random questions will be invalid. Thus, return a description + // that informs the user about the issues associated with these types of generated random questions. + return get_string('randomfaultynosubcat', 'mod_quiz'); + } + + $context = \context::instance_by_id($qcategory->contextid); + + switch ($context->contextlevel) { + case CONTEXT_MODULE: + return get_string('randommodulewithsubcat', 'mod_quiz'); + + case CONTEXT_COURSE: + return get_string('randomcoursewithsubcat', 'mod_quiz'); + + case CONTEXT_COURSECAT: + $contextname = shorten_text($context->get_context_name(false), 100); + return get_string('randomcoursecatwithsubcat', 'mod_quiz', $contextname); + + case CONTEXT_SYSTEM: + return get_string('randomsystemwithsubcat', 'mod_quiz'); + + default: + throw new coding_exception('Unsupported context.'); + } + } + // Otherwise, return the description of the used standard question category, also indicating whether subcategories + // are included. + return $includesubcategories ? get_string('randomcatwithsubcat', 'mod_quiz', $qcategory->name) : + $qcategory->name; + } } diff --git a/mod/quiz/lang/en/quiz.php b/mod/quiz/lang/en/quiz.php index 65ee4f535c692..5a7a1b51039ca 100644 --- a/mod/quiz/lang/en/quiz.php +++ b/mod/quiz/lang/en/quiz.php @@ -836,11 +836,16 @@ $string['quiztimer'] = 'Quiz Timer'; $string['quizwillopen'] = 'This quiz will open {$a}'; $string['random'] = 'Random question'; +$string['randomcatwithsubcat'] = '{$a} and subcategories'; +$string['randomcoursecatwithsubcat'] = 'Any category inside course category {$a}'; +$string['randomcoursewithsubcat'] = 'Any category in this course'; $string['randomcreate'] = 'Create random questions'; $string['randomediting'] = 'Editing a random question'; +$string['randomfaultynosubcat'] = 'Faulty question'; $string['randomfromcategory'] = 'Random question from category:'; $string['randomfromexistingcategory'] = 'Random question from an existing category'; $string['randomfromunavailabletag'] = '{$a} (unavailable)'; +$string['randommodulewithsubcat'] = 'Any category of this quiz'; $string['randomnumber'] = 'Number of random questions'; $string['randomnosubcat'] = 'Questions from this category only, not its subcategories.'; $string['randomqname'] = 'Random question based on filter condition'; @@ -855,6 +860,7 @@ The "random" questions will be selected from the questions that have all these tags.'; $string['randomquestionusinganewcategory'] = 'Random question using a new category'; +$string['randomsystemwithsubcat'] = 'Any system-level category'; $string['randomwithsubcat'] = 'Questions from this category and its subcategories.'; $string['readytosend'] = 'You are about to send your whole quiz to be graded. Are you sure you want to continue?'; $string['reattemptquiz'] = 'Re-attempt quiz'; diff --git a/mod/quiz/tests/behat/editing_add_random.feature b/mod/quiz/tests/behat/editing_add_random.feature index 79e2a7a169d5d..d8f7395efbf39 100644 --- a/mod/quiz/tests/behat/editing_add_random.feature +++ b/mod/quiz/tests/behat/editing_add_random.feature @@ -72,7 +72,7 @@ Feature: Adding random questions to a quiz based on category and tags Scenario: A random question can be added to the quiz Given I am on the "Quiz 1" "mod_quiz > Edit" page logged in as "teacher1" - When I open the "last" add to quiz menu + And I open the "last" add to quiz menu And I follow "a random question" And I apply question bank filter "Tag" with value "foo" And I select "1" from the "randomcount" singleselect @@ -83,6 +83,155 @@ Feature: Adding random questions to a quiz based on category and tags And I should see "foo" And I should see "question 1 name" And I should see "\"listen\" & \"answer\"" + # Include subcategories. + And I navigate to "Questions" in current page administration + And I open the "Page 1" add to quiz menu + And I follow "a random question" + And I set the field "Also show questions from subcategories" to "1" + And I click on "Apply filters" "button" + And I apply question bank filter "Tag" with value "foo" + And I select "1" from the "randomcount" singleselect + And I press "Add random question" + And I should see "Random (Questions Category 1 and subcategories) based on filter condition with tags: foo" on quiz page "1" + And I click on "Configure question" "link" in the "Random (Questions Category 1 and subcategories) based on filter condition with tags: foo" "list_item" + And I should see "Questions Category 1" + And I should see "foo" + And I should see "question 1 name" + And I should see "\"listen\" & \"answer\"" + And I should see "question 3 name" + + Scenario: A random question from the course's top category can be added to the quiz + Given I am on the "Quiz 1" "mod_quiz > Edit" page logged in as "teacher1" + And I open the "last" add to quiz menu + And I follow "a random question" + And I apply question bank filter "Category" with value "Top for Course 1" + And I set the field "Also show questions from subcategories" to "1" + And I click on "Apply filters" "button" + And I apply question bank filter "Tag" with value "foo" + And I select "1" from the "randomcount" singleselect + When I press "Add random question" + Then I should see "Random (Any category in this course) based on filter condition with tags: foo" on quiz page "1" + And I click on "Configure question" "link" in the "Random (Any category in this course) based on filter condition with tags: foo" "list_item" + And I should see "Top for Course 1" + And I should see "foo" + And I should see "question 1 name" + And I should see "\"listen\" & \"answer\"" + + Scenario: A random question from the quiz's top category can be added to the quiz + Given the following "question categories" exist: + | contextlevel | reference | name | + | Activity module | quiz1 | Quiz 1 category | + And the following "questions" exist: + | questioncategory | qtype | name | user | questiontext | + | Quiz 1 category | essay | quiz 1 question 1 name | teacher1 | Quiz 1 question 1 text | + | Quiz 1 category | essay | quiz 1 question 2 name | teacher1 | Quiz 1 question 2 text | + And the following "core_question > Tags" exist: + | question | tag | + | quiz 1 question 1 name | foo | + And I am on the "Quiz 1" "mod_quiz > Edit" page logged in as "teacher1" + And I open the "last" add to quiz menu + And I follow "a random question" + And I apply question bank filter "Category" with value "Top for Quiz 1" + And I set the field "Also show questions from subcategories" to "1" + And I click on "Apply filters" "button" + And I apply question bank filter "Tag" with value "foo" + And I select "1" from the "randomcount" singleselect + When I press "Add random question" + Then I should see "Random (Any category of this quiz) based on filter condition with tags: foo" on quiz page "1" + And I click on "Configure question" "link" in the "Random (Any category of this quiz) based on filter condition with tags: foo" "list_item" + And I should see "Top for Quiz 1" + And I should see "foo" + And I should see "quiz 1 question 1 name" + And I should not see "quiz 1 question 2 name" + + Scenario: A random question from the course category's top category can be added to the quiz. + Given the following "system role assigns" exist: + | user | role | contextlevel | + | teacher1 | editingteacher | Category | + And the following "categories" exist: + | name | category | idnumber | + | Category 1 | 0 | CAT1 | + And the following "question categories" exist: + | contextlevel | reference | name | + | Category | CAT1 | Default for Category 1 | + And I am on the "Course 1" "core_question > course question bank" page logged in as "teacher1" + # Create a question in the 'Default for Category 1' category. + And I press "Create a new question ..." + And I set the field "item_qtype_essay" to "1" + And I click on "Add" "button" in the "Choose a question type to add" "dialogue" + And I set the field "Category" to "Default for Category 1" + And I set the field "Question name" to "default for category 1 question 1 name" + And I set the field "Question text" to "Default for Category 1 question 1 text" + And I press "id_submitbutton" + # Create a second question in the 'Default for Category 1' category. + And I press "Create a new question ..." + And I set the field "item_qtype_essay" to "1" + And I click on "Add" "button" in the "Choose a question type to add" "dialogue" + And I set the field "Category" to "Default for Category 1" + And I set the field "Question name" to "default for category 1 question 2 name" + And I set the field "Question text" to "Default for Category 1 question 2 text" + And I press "id_submitbutton" + # Add a tag to the second question. + And I choose "Manage tags" action for "default for category 1 question 2 name" in the question bank + And I set the field "Tags" to "bar" + And I click on "Save changes" "button" in the "Question tags" "dialogue" + And I am on the "Quiz 1" "mod_quiz > Edit" page logged in as "teacher1" + And I open the "last" add to quiz menu + And I follow "a random question" + And I apply question bank filter "Category" with value "Top for Category 1" + And I set the field "Also show questions from subcategories" to "1" + And I click on "Apply filters" "button" + And I apply question bank filter "Tag" with value "bar" + And I select "1" from the "randomcount" singleselect + When I press "Add random question" + Then I should see "Random (Any category inside course category Category 1) based on filter condition with tags: bar" on quiz page "1" + And I click on "Configure question" "link" in the "Random (Any category inside course category Category 1) based on filter condition with tags: bar" "list_item" + And I should see "Top for Category 1" + And I should see "bar" + And I should see "default for category 1 question 2 name" + And I should not see "default for category 1 question 1 name" + + Scenario: A random question from the system's top category can be added to the quiz + Given the following "system role assigns" exist: + | user | role | contextlevel | + | teacher1 | editingteacher | System | + And the following "question categories" exist: + | contextlevel | reference | name | + | System | | System category | + And the following "questions" exist: + | questioncategory | qtype | name | user | questiontext | + | System category | essay | system question 1 name | admin | System question 1 text | + | System category | essay | system question 2 name | admin | System question 2 text | + And the following "core_question > Tags" exist: + | question | tag | + | system question 1 name | foo | + And I am on the "Quiz 1" "mod_quiz > Edit" page logged in as "admin" + And I open the "last" add to quiz menu + And I follow "a random question" + And I apply question bank filter "Category" with value "Top for System" + And I set the field "Also show questions from subcategories" to "1" + And I click on "Apply filters" "button" + And I apply question bank filter "Tag" with value "foo" + And I select "1" from the "randomcount" singleselect + When I press "Add random question" + Then I should see "Random (Any system-level category) based on filter condition with tags: foo" on quiz page "1" + And I click on "Configure question" "link" in the "Random (Any system-level category) based on filter condition with tags: foo" "list_item" + And I should see "Top for System" + And I should see "foo" + And I should see "system question 1 name" + And I should not see "system question 2 name" + + Scenario: A random question from a top category, excluding subcategories, shows an indicator of being faulty + Given I am on the "Quiz 1" "mod_quiz > Edit" page logged in as "teacher1" + And I open the "last" add to quiz menu + And I follow "a random question" + And I apply question bank filter "Category" with value "Top for Course 1" + And I set the field "Also show questions from subcategories" to "0" + And I click on "Apply filters" "button" + And I apply question bank filter "Tag" with value "foo" + And I select "1" from the "randomcount" singleselect + When I press "Add random question" + Then I should see "Random (Faulty question) based on filter condition" on quiz page "1" Scenario: After closing and reopening the modal, it still works When I am on the "Quiz 1" "mod_quiz > Edit" page logged in as teacher1