mirror of
https://github.com/moodle/moodle.git
synced 2025-03-14 04:30:15 +01:00
Merge branch 'MDL-61514-master' of git://github.com/rezaies/moodle
This commit is contained in:
commit
d7a6e531ee
@ -58,7 +58,7 @@ class backup_quiz_activity_structure_step extends backup_questions_activity_stru
|
||||
$qinstances = new backup_nested_element('question_instances');
|
||||
|
||||
$qinstance = new backup_nested_element('question_instance', array('id'), array(
|
||||
'slot', 'page', 'requireprevious', 'questionid', 'maxmark'));
|
||||
'slot', 'page', 'requireprevious', 'questionid', 'questioncategoryid', 'includingsubcategories', 'tags', 'maxmark'));
|
||||
|
||||
$sections = new backup_nested_element('sections');
|
||||
|
||||
|
@ -290,7 +290,21 @@ class restore_quiz_activity_structure_step extends restore_questions_activity_st
|
||||
}
|
||||
|
||||
$data->quizid = $this->get_new_parentid('quiz');
|
||||
$data->questionid = $this->get_mappingid('question', $data->questionid);
|
||||
$questionmapping = $this->get_mapping('question', $data->questionid);
|
||||
$data->questionid = $questionmapping ? $questionmapping->newitemid : false;
|
||||
|
||||
if (isset($data->questioncategoryid)) {
|
||||
$data->questioncategoryid = $this->get_mappingid('question_category', $data->questioncategoryid);
|
||||
} else if ($questionmapping && $questionmapping->info->qtype == 'random') {
|
||||
// Backward compatibility for backups created using Moodle 3.4 or earlier.
|
||||
$data->questioncategoryid = $this->get_mappingid('question_category', $questionmapping->parentitemid);
|
||||
$data->includingsubcategories = $questionmapping->info->questiontext ? 1 : 0;
|
||||
}
|
||||
|
||||
if (isset($data->tags)) {
|
||||
$tags = quiz_extract_random_question_tags($data->tags, $this->task->is_samesite());
|
||||
$data->tags = quiz_build_random_question_tag_json($tags);
|
||||
}
|
||||
|
||||
$DB->insert_record('quiz_slots', $data);
|
||||
}
|
||||
|
@ -81,4 +81,20 @@ class randomquestion_form extends \moodleform {
|
||||
$mform->addGroup($buttonarray, 'buttonar', '', array(' '), false);
|
||||
$mform->closeHeaderBefore('buttonar');
|
||||
}
|
||||
|
||||
public function set_data($defaultvalues) {
|
||||
$mform = $this->_form;
|
||||
|
||||
if ($defaultvalues->fromtags) {
|
||||
$fromtagselement = $mform->getElement('fromtags');
|
||||
foreach ($defaultvalues->fromtags as $fromtag) {
|
||||
if (!$fromtagselement->optionExists($fromtag)) {
|
||||
$optionname = get_string('randomfromunavailabletag', 'mod_quiz', explode(',', $fromtag)[1]);
|
||||
$fromtagselement->addOption($optionname, $fromtag);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
parent::set_data($defaultvalues);
|
||||
}
|
||||
}
|
||||
|
@ -700,6 +700,7 @@ $string['randomcreate'] = 'Create random questions';
|
||||
$string['randomediting'] = 'Editing a random question';
|
||||
$string['randomfromcategory'] = 'Random question from category:';
|
||||
$string['randomfromexistingcategory'] = 'Random question from an existing category';
|
||||
$string['randomfromunavailabletag'] = '{$a} (unavailable)';
|
||||
$string['randomnumber'] = 'Number of random questions';
|
||||
$string['randomnosubcat'] = 'Questions from this category only, not its subcategories.';
|
||||
$string['randomquestion'] = 'Random question';
|
||||
|
@ -2449,14 +2449,14 @@ function quiz_is_overriden_calendar_event(\calendar_event $event) {
|
||||
function quiz_build_random_question_tag_json($tagrecords) {
|
||||
$tags = [];
|
||||
foreach ($tagrecords as $tagrecord) {
|
||||
if ($tag = core_tag_tag::get($tagrecord->id, 'id, name')) {
|
||||
if ($tagrecord->id && $tag = core_tag_tag::get($tagrecord->id, 'id, name')) {
|
||||
$tags[] = [
|
||||
'id' => (int)$tagrecord->id,
|
||||
'name' => $tag->name
|
||||
];
|
||||
} else if ($tag = core_tag_tag::get_by_name(0, $tagrecord->name, 'id, name')) {
|
||||
$tags[] = [
|
||||
'id' => $tag->id,
|
||||
'id' => (int)$tag->id,
|
||||
'name' => $tagrecord->name
|
||||
];
|
||||
} else {
|
||||
@ -2475,20 +2475,25 @@ function quiz_build_random_question_tag_json($tagrecords) {
|
||||
* @param string $tagsjson The JSON string representing an array of tags in the [{"id":tagid,"name":"tagname"}] format.
|
||||
* E.g. [{"id":1,"name":"tag1"},{"id":2,"name":"tag2"}]
|
||||
* Usually equal to the value of the tags field retrieved from the quiz_slots table.
|
||||
* @param bool $matchbyid If set to true, then the function tries to find tags by their id.
|
||||
* If no tag is found by the tag id or if $matchbyid is set to false, then the function tries to find the tag by its name.
|
||||
* @return array An array of tags containing the id and name properties, indexed by tag ids.
|
||||
*/
|
||||
function quiz_extract_random_question_tags($tagsjson) {
|
||||
function quiz_extract_random_question_tags($tagsjson, $matchbyid = true) {
|
||||
$tagrecords = [];
|
||||
if (!empty($tagsjson)) {
|
||||
$tags = json_decode($tagsjson);
|
||||
// Only work with tags that exist.
|
||||
|
||||
foreach ($tags as $tagdata) {
|
||||
if (!array_key_exists($tagdata->id, $tagrecords)) {
|
||||
if ($tag = core_tag_tag::get($tagdata->id, 'id, name')) {
|
||||
$tagrecords[$tag->id] = $tag->to_object();
|
||||
} else if ($tag = core_tag_tag::get_by_name(0, $tagdata->name, 'id, name')) {
|
||||
$tagrecords[$tag->id] = $tag->to_object();
|
||||
}
|
||||
if ($matchbyid && $tag = core_tag_tag::get($tagdata->id, 'id, name')) {
|
||||
$tagrecords[] = $tag->to_object();
|
||||
} else if ($tag = core_tag_tag::get_by_name(0, $tagdata->name, 'id, name')) {
|
||||
$tagrecords[] = $tag->to_object();
|
||||
} else {
|
||||
$tagrecords[] = (object)[
|
||||
'id' => null,
|
||||
'name' => $tagdata->name
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -2502,9 +2507,13 @@ function quiz_extract_random_question_tags($tagsjson) {
|
||||
* @param string $tagsjson The JSON string representing an array of tags in the [{"id":tagid,"name":"tagname"}] format.
|
||||
* E.g. [{"id":1,"name":"tag1"},{"id":2,"name":"tag2"}]
|
||||
* Usually equal to the value of the tags field retrieved from the {quiz_slots} table.
|
||||
* @param bool $matchbyid If set to true, then this function relies on the tag ids that are stored in $tagsjson to find tags.
|
||||
* If no tag is found by the tag id or if $matchbyid is set to false, then this function tries to find the tag by its name.
|
||||
* @return int[] List of tag ids.
|
||||
*/
|
||||
function quiz_extract_random_question_tag_ids($tagsjson) {
|
||||
$tags = quiz_extract_random_question_tags($tagsjson);
|
||||
return array_keys($tags);
|
||||
function quiz_extract_random_question_tag_ids($tagsjson, $matchbyid = true) {
|
||||
$tags = quiz_extract_random_question_tags($tagsjson, $matchbyid);
|
||||
|
||||
// Only work with tags that exist.
|
||||
return array_filter(array_column($tags, 'id'));
|
||||
}
|
BIN
mod/quiz/tests/fixtures/random_by_tag_quiz.mbz
vendored
Normal file
BIN
mod/quiz/tests/fixtures/random_by_tag_quiz.mbz
vendored
Normal file
Binary file not shown.
@ -420,4 +420,224 @@ class mod_quiz_locallib_testcase extends advanced_testcase {
|
||||
|
||||
$this->assertEquals($comparearray, quiz_get_user_timeclose($course->id));
|
||||
}
|
||||
|
||||
public function test_quiz_build_random_question_tag_json() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Setup test data.
|
||||
$footagrecord = array(
|
||||
'isstandard' => 1,
|
||||
'flag' => 0,
|
||||
'rawname' => 'foo',
|
||||
'description' => 'foo desc'
|
||||
);
|
||||
$footag = $this->getDataGenerator()->create_tag($footagrecord);
|
||||
$bartagrecord = array(
|
||||
'isstandard' => 1,
|
||||
'flag' => 0,
|
||||
'rawname' => 'bar',
|
||||
'description' => 'bar desc'
|
||||
);
|
||||
$bartag = $this->getDataGenerator()->create_tag($bartagrecord);
|
||||
$baztagrecord = array(
|
||||
'isstandard' => 1,
|
||||
'flag' => 0,
|
||||
'rawname' => 'baz',
|
||||
'description' => 'baz desc'
|
||||
);
|
||||
$baztag = $this->getDataGenerator()->create_tag($baztagrecord);
|
||||
$quxtagrecord = array(
|
||||
'isstandard' => 1,
|
||||
'flag' => 0,
|
||||
'rawname' => 'qux',
|
||||
'description' => 'qux desc'
|
||||
);
|
||||
$quxtag = $this->getDataGenerator()->create_tag($quxtagrecord);
|
||||
$quuxtagrecord = array(
|
||||
'isstandard' => 1,
|
||||
'flag' => 0,
|
||||
'rawname' => 'quux',
|
||||
'description' => 'quux desc'
|
||||
);
|
||||
$quuxtag = $this->getDataGenerator()->create_tag($quuxtagrecord);
|
||||
|
||||
$tagrecords = array(
|
||||
(object)[
|
||||
'id' => $footag->id,
|
||||
'name' => 'foo'
|
||||
],
|
||||
(object)[
|
||||
'id' => 999, // An invalid tag id.
|
||||
'name' => 'bar'
|
||||
],
|
||||
(object)[
|
||||
'id' => null,
|
||||
'name' => 'baz'
|
||||
],
|
||||
(object)[
|
||||
'id' => $quxtag->id,
|
||||
'name' => 'invalidqux' // An invalid tag name.
|
||||
],
|
||||
(object)[
|
||||
'id' => 999, // An invalid tag id.
|
||||
'name' => 'invalidquux' // An invalid tag name.
|
||||
],
|
||||
);
|
||||
|
||||
$expectedjson = json_encode(array(
|
||||
['id' => (int)$footag->id, 'name' => $footag->name],
|
||||
['id' => (int)$bartag->id, 'name' => $bartag->name],
|
||||
['id' => (int)$baztag->id, 'name' => $baztag->name],
|
||||
['id' => (int)$quxtag->id, 'name' => $quxtag->name],
|
||||
['id' => null, 'name' => 'invalidquux'],
|
||||
));
|
||||
$this->assertEquals($expectedjson, quiz_build_random_question_tag_json($tagrecords));
|
||||
}
|
||||
|
||||
public function test_quiz_extract_random_question_tags() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Setup test data.
|
||||
$footagrecord = array(
|
||||
'isstandard' => 1,
|
||||
'flag' => 0,
|
||||
'rawname' => 'foo',
|
||||
'description' => 'foo desc'
|
||||
);
|
||||
$footag = $this->getDataGenerator()->create_tag($footagrecord);
|
||||
$bartagrecord = array(
|
||||
'isstandard' => 1,
|
||||
'flag' => 0,
|
||||
'rawname' => 'bar',
|
||||
'description' => 'bar desc'
|
||||
);
|
||||
$bartag = $this->getDataGenerator()->create_tag($bartagrecord);
|
||||
$baztagrecord = array(
|
||||
'isstandard' => 1,
|
||||
'flag' => 0,
|
||||
'rawname' => 'baz',
|
||||
'description' => 'baz desc'
|
||||
);
|
||||
$baztag = $this->getDataGenerator()->create_tag($baztagrecord);
|
||||
$quxtagrecord = array(
|
||||
'isstandard' => 1,
|
||||
'flag' => 0,
|
||||
'rawname' => 'qux',
|
||||
'description' => 'qux desc'
|
||||
);
|
||||
$quxtag = $this->getDataGenerator()->create_tag($quxtagrecord);
|
||||
$quuxtagrecord = array(
|
||||
'isstandard' => 1,
|
||||
'flag' => 0,
|
||||
'rawname' => 'quux',
|
||||
'description' => 'quux desc'
|
||||
);
|
||||
$quuxtag = $this->getDataGenerator()->create_tag($quuxtagrecord);
|
||||
|
||||
$tagjson = json_encode(array(
|
||||
[
|
||||
'id' => $footag->id,
|
||||
'name' => 'foo'
|
||||
],
|
||||
[
|
||||
'id' => 999, // An invalid tag id.
|
||||
'name' => 'bar'
|
||||
],
|
||||
[
|
||||
'id' => null,
|
||||
'name' => 'baz'
|
||||
],
|
||||
[
|
||||
'id' => $quxtag->id,
|
||||
'name' => 'invalidqux' // An invalid tag name.
|
||||
],
|
||||
[
|
||||
'id' => 999, // An invalid tag id.
|
||||
'name' => 'invalidquux' // An invalid tag name.
|
||||
],
|
||||
));
|
||||
|
||||
$expectedrecords = array(
|
||||
(object)['id' => $footag->id, 'name' => $footag->name],
|
||||
(object)['id' => $bartag->id, 'name' => $bartag->name],
|
||||
(object)['id' => $baztag->id, 'name' => $baztag->name],
|
||||
(object)['id' => $quxtag->id, 'name' => $quxtag->name],
|
||||
(object)['id' => null, 'name' => 'invalidquux'],
|
||||
);
|
||||
|
||||
$this->assertEquals($expectedrecords, quiz_extract_random_question_tags($tagjson));
|
||||
}
|
||||
|
||||
public function test_quiz_extract_random_question_tag_ids() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Setup test data.
|
||||
$footagrecord = array(
|
||||
'isstandard' => 1,
|
||||
'flag' => 0,
|
||||
'rawname' => 'foo',
|
||||
'description' => 'foo desc'
|
||||
);
|
||||
$footag = $this->getDataGenerator()->create_tag($footagrecord);
|
||||
$bartagrecord = array(
|
||||
'isstandard' => 1,
|
||||
'flag' => 0,
|
||||
'rawname' => 'bar',
|
||||
'description' => 'bar desc'
|
||||
);
|
||||
$bartag = $this->getDataGenerator()->create_tag($bartagrecord);
|
||||
$baztagrecord = array(
|
||||
'isstandard' => 1,
|
||||
'flag' => 0,
|
||||
'rawname' => 'baz',
|
||||
'description' => 'baz desc'
|
||||
);
|
||||
$baztag = $this->getDataGenerator()->create_tag($baztagrecord);
|
||||
$quxtagrecord = array(
|
||||
'isstandard' => 1,
|
||||
'flag' => 0,
|
||||
'rawname' => 'qux',
|
||||
'description' => 'qux desc'
|
||||
);
|
||||
$quxtag = $this->getDataGenerator()->create_tag($quxtagrecord);
|
||||
$quuxtagrecord = array(
|
||||
'isstandard' => 1,
|
||||
'flag' => 0,
|
||||
'rawname' => 'quux',
|
||||
'description' => 'quux desc'
|
||||
);
|
||||
$quuxtag = $this->getDataGenerator()->create_tag($quuxtagrecord);
|
||||
|
||||
$tagjson = json_encode(array(
|
||||
[
|
||||
'id' => $footag->id,
|
||||
'name' => 'foo'
|
||||
],
|
||||
[
|
||||
'id' => 999, // An invalid tag id.
|
||||
'name' => 'bar'
|
||||
],
|
||||
[
|
||||
'id' => null,
|
||||
'name' => 'baz'
|
||||
],
|
||||
[
|
||||
'id' => $quxtag->id,
|
||||
'name' => 'invalidqux' // An invalid tag name.
|
||||
],
|
||||
[
|
||||
'id' => 999, // An invalid tag id.
|
||||
'name' => 'invalidquux' // An invalid tag name.
|
||||
],
|
||||
));
|
||||
|
||||
$expectedrecords = array(
|
||||
$footag->id,
|
||||
$bartag->id,
|
||||
$baztag->id,
|
||||
$quxtag->id,
|
||||
);
|
||||
|
||||
$this->assertEquals($expectedrecords, quiz_extract_random_question_tag_ids($tagjson));
|
||||
}
|
||||
}
|
||||
|
95
mod/quiz/tests/tags_test.php
Normal file
95
mod/quiz/tests/tags_test.php
Normal file
@ -0,0 +1,95 @@
|
||||
<?php
|
||||
// This file is part of Moodle - http://moodle.org/
|
||||
//
|
||||
// Moodle is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// Moodle is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
/**
|
||||
* Unit tests for usage of tags in quizzes.
|
||||
*
|
||||
* @package mod_quiz
|
||||
* @copyright 2018 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
|
||||
|
||||
/**
|
||||
* Class mod_quiz_tags_testcase
|
||||
* Class for tests related to usage of question tags in quizzes.
|
||||
*
|
||||
* @copyright 2018 Shamim Rezaie <shamim@moodle.com>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class mod_quiz_tags_testcase extends advanced_testcase {
|
||||
public function test_restore_random_question_by_tag() {
|
||||
global $CFG, $USER, $DB;
|
||||
|
||||
require_once($CFG->dirroot . '/backup/util/includes/restore_includes.php');
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->setAdminUser();
|
||||
|
||||
$backupid = 'abc';
|
||||
$backuppath = $CFG->tempdir . '/backup/' . $backupid;
|
||||
check_dir_exists($backuppath);
|
||||
get_file_packer('application/vnd.moodle.backup')->extract_to_pathname(
|
||||
__DIR__ . "/fixtures/random_by_tag_quiz.mbz", $backuppath);
|
||||
|
||||
// Do the restore to new course with default settings.
|
||||
$categoryid = $DB->get_field_sql("SELECT MIN(id) FROM {course_categories}");
|
||||
$newcourseid = restore_dbops::create_new_course('Test fullname', 'Test shortname', $categoryid);
|
||||
$rc = new restore_controller($backupid, $newcourseid, backup::INTERACTIVE_NO, backup::MODE_GENERAL, $USER->id,
|
||||
backup::TARGET_NEW_COURSE);
|
||||
|
||||
$this->assertTrue($rc->execute_precheck());
|
||||
$rc->execute_plan();
|
||||
$rc->destroy();
|
||||
|
||||
// Get the information about the resulting course and check that it is set up correctly.
|
||||
$modinfo = get_fast_modinfo($newcourseid);
|
||||
$quiz = array_values($modinfo->get_instances_of('quiz'))[0];
|
||||
$quizobj = quiz::create($quiz->instance);
|
||||
$structure = \mod_quiz\structure::create_for_quiz($quizobj);
|
||||
|
||||
// Are the correct slots returned?
|
||||
$slots = $structure->get_slots();
|
||||
$this->assertCount(1, $slots);
|
||||
|
||||
$quizobj->preload_questions();
|
||||
$quizobj->load_questions();
|
||||
$questions = $quizobj->get_questions();
|
||||
|
||||
$this->assertCount(1, $questions);
|
||||
|
||||
$question = array_values($questions)[0];
|
||||
|
||||
$tag1 = core_tag_tag::get_by_name(0, 't1', 'id, name');
|
||||
$this->assertNotFalse($tag1);
|
||||
|
||||
$tag2 = core_tag_tag::get_by_name(0, 't2', 'id, name');
|
||||
$this->assertNotFalse($tag2);
|
||||
|
||||
$tag3 = core_tag_tag::get_by_name(0, 't3', 'id, name');
|
||||
$this->assertNotFalse($tag3);
|
||||
|
||||
$tagrecords = array($tag2->to_object());
|
||||
$this->assertEquals(quiz_build_random_question_tag_json($tagrecords), $question->randomfromtags);
|
||||
|
||||
$defaultcategory = question_get_default_category(context_course::instance($newcourseid)->id);
|
||||
$this->assertEquals($defaultcategory->id, $question->randomfromcategory);
|
||||
$this->assertEquals(0, $question->randomincludingsubcategories);
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user