Commit 25215446 authored by 0815-xyz's avatar 0815-xyz
Browse files

Initial commit

parents
<?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/>.
/**
* @copyright 2022 onwards Vitaly Potenko <potenkov@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_adaptivequiz\local\report\users_attempts\user_preferences;
use basic_testcase;
use mod_adaptivequiz\local\report\users_attempts\filter\filter_options;
/**
* @covers \mod_adaptivequiz\local\report\users_attempts\user_preferences\filter_user_preferences
*/
class filter_user_preferences_test extends basic_testcase {
public function test_it_acquires_correct_default_values_when_unexpected_parameters_provided(): void {
$filter = filter_user_preferences::from_array([]);
$this->assertEquals(filter_options::users_option_default(), $filter->users());
$this->assertEquals(filter_options::INCLUDE_INACTIVE_ENROLMENTS_DEFAULT,
$filter->include_inactive_enrolments());
$filter = filter_user_preferences::from_array(['users' => 42, 'includeinactiveenrolments' => 5]);
$this->assertEquals(filter_options::users_option_default(), $filter->users());
$this->assertEquals(filter_options::INCLUDE_INACTIVE_ENROLMENTS_DEFAULT,
$filter->include_inactive_enrolments());
}
public function test_it_can_be_converted_to_array(): void {
$filterasarray = ['users' => filter_options::users_option_default(), 'includeinactiveenrolments' => 1];
$filter = filter_user_preferences::from_array($filterasarray);
$this->assertEquals($filterasarray, $filter->as_array());
}
}
<?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/>.
/**
* @copyright 2022 onwards Vitaly Potenko <potenkov@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_adaptivequiz\local\report\users_attempts\user_preferences;
use advanced_testcase;
/**
* @covers \mod_adaptivequiz\local\report\users_attempts\user_preferences\user_preferences_repository
*/
class user_preferences_repository_test extends advanced_testcase {
public function test_it_stores_and_fetches_preferences(): void {
$this->resetAfterTest();
$preferences = user_preferences::from_array(['perpage' => 20, 'showinitialsbar' => 0]);
user_preferences_repository::save($preferences);
$this->assertEquals($preferences, user_preferences_repository::get());
}
public function test_it_avoids_querying_database_when_not_needed(): void {
global $DB;
$this->resetAfterTest();
$preferences = user_preferences::from_array(['perpage' => 20, 'showinitialsbar' => 0]);
// Check it will not query database to fetch preference after saving them.
$queriescountbefore = $DB->perf_get_reads();
user_preferences_repository::save($preferences);
user_preferences_repository::get();
$this->assertEquals($queriescountbefore, $DB->perf_get_reads());
// Check it will not query the database for subsequent fetches of preferences.
user_preferences_repository::get();
$this->assertEquals($queriescountbefore, $DB->perf_get_reads());
}
}
<?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/>.
/**
* @copyright 2022 onwards Vitaly Potenko <potenkov@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_adaptivequiz\local\report\users_attempts\user_preferences;
use basic_testcase;
use mod_adaptivequiz\local\report\users_attempts\filter\filter_options;
use stdClass;
/**
* @covers \mod_adaptivequiz\local\report\users_attempts\user_preferences\user_preferences
*/
class user_preferences_test extends basic_testcase {
public function test_it_acquires_correct_default_values_when_provided_values_are_not_in_valid_range(): void {
$preferences = user_preferences::from_array(
['perpage' => 100, 'showinitialsbar' => 22, 'persistentfilter' => -1]
);
$this->assertEquals(15, $preferences->rows_per_page());
$this->assertTrue($preferences->show_initials_bar());
$this->assertFalse($preferences->persistent_filter());
$this->assertNull($preferences->filter());
$preferences = user_preferences::from_array([]);
$this->assertEquals(15, $preferences->rows_per_page());
$this->assertTrue($preferences->show_initials_bar());
$this->assertFalse($preferences->persistent_filter());
$this->assertNull($preferences->filter());
$preferencesobject = new stdClass();
$preferencesobject->perpage = -25;
$preferencesobject->showinitialsbar = 100;
$preferencesobject->persistentfilter = "12";
$preferences = user_preferences::from_plain_object($preferencesobject);
$this->assertEquals(15, $preferences->rows_per_page());
$this->assertTrue($preferences->show_initials_bar());
$this->assertFalse($preferences->persistent_filter());
$this->assertNull($preferences->filter());
}
public function test_it_can_acquire_and_loose_filter_preferences_while_preserving_previously_set_preferences(): void {
$preferences = user_preferences::from_array(['perpage' => 10, 'showinitialsbar' => 0, 'persistentfilter' => 1]);
$pfilterpreferencesarray = ['users' => filter_options::users_option_default(),
'includeinactiveenrolments' => filter_options::INCLUDE_INACTIVE_ENROLMENTS_DEFAULT];
$preferences = $preferences->with_filter_preference(filter_user_preferences::from_array($pfilterpreferencesarray));
$this->assertEquals(10, $preferences->rows_per_page());
$this->assertEquals(0, $preferences->show_initials_bar());
$this->assertEquals(1, $preferences->persistent_filter());
$this->assertEquals(filter_user_preferences::from_array($pfilterpreferencesarray), $preferences->filter());
$preferences = $preferences->without_filter_preference();
$this->assertEquals(10, $preferences->rows_per_page());
$this->assertEquals(0, $preferences->show_initials_bar());
$this->assertEquals(1, $preferences->persistent_filter());
$this->assertEquals(null, $preferences->filter());
}
public function test_it_can_be_converted_to_array(): void {
$preferences = user_preferences::defaults();
$this->assertEquals(['perpage' => 15, 'showinitialsbar' => 1, 'persistentfilter' => 0, 'filter' => null],
$preferences->as_array());
}
}
<?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/>.
/**
* @copyright 2022 onwards Vitaly Potenko <potenkov@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_adaptivequiz\local\repository;
use advanced_testcase;
use coding_exception;
use context;
use context_course;
use core_question\local\bank\question_version_status;
use core_question_generator;
use core_tag_tag;
use dml_missing_record_exception;
use question_bank;
use stdClass;
/**
* @covers \mod_adaptivequiz\local\repository\questions_repository
*/
class questions_repository_test extends advanced_testcase {
public function test_it_can_count_adaptive_questions_in_pool(): void {
$this->resetAfterTest();
$generator = $this->getDataGenerator();
/** @var core_question_generator $questionsgenerator */
$questionsgenerator = $generator->get_plugin_generator('core_question');
$course = $generator->create_course();
$questionscat1 = $questionsgenerator->create_question_category(
['contextid' => context_course::instance($course->id)->id]
);
$question1 = $questionsgenerator->create_question('truefalse', null, ['category' => $questionscat1->id]);
$questionsgenerator->create_question_tag(
['questionid' => $question1->id, 'tag' => 'adpq_1']
);
$question2 = $questionsgenerator->create_question('truefalse', null, ['category' => $questionscat1->id]);
$questionsgenerator->create_question_tag(
['questionid' => $question2->id, 'tag' => 'adpq_2']
);
$question3 = $questionsgenerator->create_question('truefalse', null, ['category' => $questionscat1->id]);
$questionsgenerator->create_question_tag(
['questionid' => $question3->id, 'tag' => 'adpq_001']
);
$questionscat2 = $questionsgenerator->create_question_category(
['contextid' => context_course::instance($course->id)->id]
);
$this->assertEquals(1, questions_repository::count_adaptive_questions_in_pool_with_level(
[$questionscat1->id, $questionscat2->id], 1
));
$questionsgenerator->create_question('truefalse', null, ['category' => $questionscat2->id]);
$questionsgenerator->create_question_tag(
['questionid' => $question2->id, 'tag' => 'truefalse_1']
);
$this->assertEquals(1, questions_repository::count_adaptive_questions_in_pool_with_level(
[$questionscat1->id, $questionscat2->id], 1
));
$questionscat3 = $questionsgenerator->create_question_category(
['contextid' => context_course::instance($course->id)->id]
);
$this->assertEquals(0, questions_repository::count_adaptive_questions_in_pool_with_level([$questionscat3->id], 1));
}
public function test_it_can_count_questions_number_per_difficulty(): void {
$this->resetAfterTest();
$generator = $this->getDataGenerator();
/** @var core_question_generator $questionsgenerator */
$questionsgenerator = $generator->get_plugin_generator('core_question');
$course = $generator->create_course();
$category1 = $questionsgenerator->create_question_category(
['contextid' => context_course::instance($course->id)->id]
);
$category2 = $questionsgenerator->create_question_category(
['contextid' => context_course::instance($course->id)->id]
);
$question1 = $questionsgenerator->create_question('truefalse', null, ['category' => $category1->id]);
$question1tag = $this->create_question_tag($question1->id, 'adpq_1');
$question2 = $questionsgenerator->create_question('truefalse', null, ['category' => $category2->id]);
$question2tag = $this->create_question_tag($question2->id, 'adpq_4');
$question3 = $questionsgenerator->create_question('truefalse', null, ['category' => $category1->id]);
// Update one of the questions to populate several versions in the database.
$questionsgenerator->update_question($question3, null, ['name' => 'New question version']);
$question3tag = $this->create_question_tag($question3->id, 'adpq_4');
$draftquestion = $questionsgenerator->create_question('truefalse', null,
['category' => $category1->id, 'status' => question_version_status::QUESTION_STATUS_DRAFT]);
$draftquestiontag = $this->create_question_tag($draftquestion->id, 'adpq_5');
$hiddenquestion = $questionsgenerator->create_question('truefalse', null,
['category' => $category1->id, 'status' => question_version_status::QUESTION_STATUS_HIDDEN]);
$hiddenquestiontag = $this->create_question_tag($hiddenquestion->id, 'adpq_6');
self::assertEquals([
new questions_number_per_difficulty(1, 1),
new questions_number_per_difficulty(4, 2),
],
questions_repository::count_questions_number_per_difficulty(
[$question1tag->id, $question2tag->id, $question3tag->id, $draftquestiontag->id, $hiddenquestiontag->id],
[$category1->id, $category2->id]
)
);
}
public function test_it_finds_questions_with_tags(): void {
$this->resetAfterTest();
// It returns empty array when no tags or no question categories specified.
self::assertEquals([], questions_repository::find_questions_with_tags([], [1, 2], []));
self::assertEquals([], questions_repository::find_questions_with_tags([1, 2], [], []));
$generator = $this->getDataGenerator();
/** @var core_question_generator $questionsgenerator */
$questionsgenerator = $generator->get_plugin_generator('core_question');
$course = $generator->create_course();
$category1 = $questionsgenerator->create_question_category(
['contextid' => context_course::instance($course->id)->id]
);
$category2 = $questionsgenerator->create_question_category(
['contextid' => context_course::instance($course->id)->id]
);
$question1 = $questionsgenerator->create_question('truefalse', null,
['name' => 'Question 1', 'category' => $category1->id]);
$question1tag = $this->create_question_tag($question1->id, 'adpq_1');
$question2 = $questionsgenerator->create_question('truefalse', null,
['name' => 'Question 2', 'category' => $category2->id]);
// Update one of the questions to populate several versions in the database.
$question2 = $questionsgenerator->update_question($question2, null, ['name' => 'New question version']);
$question2tag = $this->create_question_tag($question2->id, 'adpq_4');
$draftquestion = $questionsgenerator->create_question('truefalse', null,
['category' => $category1->id, 'status' => question_version_status::QUESTION_STATUS_DRAFT]);
$draftquestiontag = $this->create_question_tag($draftquestion->id, 'adpq_5');
$hiddenquestion = $questionsgenerator->create_question('truefalse', null,
['category' => $category1->id, 'status' => question_version_status::QUESTION_STATUS_HIDDEN]);
$hiddenquestiontag = $this->create_question_tag($hiddenquestion->id, 'adpq_6');
$tagidlist = [$question1tag->id, $question2tag->id, $draftquestiontag->id, $hiddenquestiontag->id];
$categoryidlist = [$category1->id, $category2->id];
$result = questions_repository::find_questions_with_tags($tagidlist, $categoryidlist, []);
$expectedresult = [];
$expectedrecord = new stdClass;
$expectedrecord->id = $question1->id;
$expectedrecord->name = $question1->name;
$expectedresult[$question1->id] = $expectedrecord;
$expectedrecord = new stdClass;
$expectedrecord->id = $question2->id;
$expectedrecord->name = $question2->name;
$expectedresult[$question2->id] = $expectedrecord;
self::assertEquals($expectedresult, $result);
// It can handle excluded questions.
$result = questions_repository::find_questions_with_tags($tagidlist, $categoryidlist, [$question1->id]);
$expectedresult = [];
$expectedrecord = new stdClass;
$expectedrecord->id = $question2->id;
$expectedrecord->name = $question2->name;
$expectedresult[$question2->id] = $expectedrecord;
self::assertEquals($expectedresult, $result);
}
/**
* @throws dml_missing_record_exception
* @throws coding_exception
*/
private function create_question_tag(int $questionid, string $tagname): core_tag_tag {
$question = question_bank::load_question($questionid);
core_tag_tag::add_item_tag('core_question', 'question', $question->id,
context::instance_by_id($question->contextid), $tagname);
$tagbyname = core_tag_tag::get_by_name(0, $tagname);
if (!$tagbyname) {
throw new coding_exception('Could not find the tag by name, please, check data initialization steps.');
}
return $tagbyname;
}
}
<?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/>.
/**
* @copyright 2022 onwards Vitaly Potenko <potenkov@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_adaptivequiz\local\repository;
use advanced_testcase;
use coding_exception;
use context_course;
use core_question_generator;
use core_tag_tag;
/**
* @covers \mod_adaptivequiz\local\repository\tags_repository
*/
class tags_repository_test extends advanced_testcase {
public function test_it_gets_question_level_to_tag_id_mapping_by_tag_names(): void {
$this->resetAfterTest();
$this->assertEquals(
[],
tags_repository::get_question_level_to_tag_id_mapping_by_tag_names(
['adpq_5', 'adpq_6']
)
);
$generator = $this->getDataGenerator();
/** @var core_question_generator $questionsgenerator */
$questionsgenerator = $generator->get_plugin_generator('core_question');
$course = $generator->create_course();
$questionscat = $questionsgenerator->create_question_category(
['contextid' => context_course::instance($course->id)->id]
);
$question1 = $questionsgenerator->create_question('shortanswer', null, ['category' => $questionscat->id]);
$questionsgenerator->create_question_tag(
['questionid' => $question1->id, 'tag' => 'adpq_5']
);
$question2 = $questionsgenerator->create_question('shortanswer', null, ['category' => $questionscat->id]);
$questionsgenerator->create_question_tag(
['questionid' => $question2->id, 'tag' => 'adpq_6']
);
$question3 = $questionsgenerator->create_question('shortanswer', null, ['category' => $questionscat->id]);
$questionsgenerator->create_question_tag(
['questionid' => $question3->id, 'tag' => 'adpq_7']
);
core_tag_tag::add_item_tag('core', 'course', $course->id, context_course::instance($course->id), 'adpq_5');
$map = tags_repository::get_question_level_to_tag_id_mapping_by_tag_names(
['adpq_5', 'adpq_6']
);
$questionstags = core_tag_tag::get_items_tags('core_question', 'question', [$question1->id, $question2->id]);
$tagidlist = array_map(function(array $itemtags): int {
return $itemtags[array_key_first($itemtags)]->id;
}, $questionstags);
$this->assertEquals(2, count($map));
$this->assertEquals(
[5 => array_shift($tagidlist), 6 => array_shift($tagidlist)],
$map
);
}
public function test_it_fails_to_get_question_level_to_tag_id_mapping_for_an_empty_array_of_tag_names(): void {
$this->expectException(coding_exception::class);
tags_repository::get_question_level_to_tag_id_mapping_by_tag_names([]);
}
public function test_it_gets_tag_id_list_by_tag_names(): void {
$this->resetAfterTest();
$this->assertEquals(
[],
tags_repository::get_tag_id_list_by_tag_names(
['adpq_10', 'adpq_11']
)
);
$generator = $this->getDataGenerator();
/** @var core_question_generator $questionsgenerator */
$questionsgenerator = $generator->get_plugin_generator('core_question');
$course = $generator->create_course();
$questionscat = $questionsgenerator->create_question_category(
['contextid' => context_course::instance($course->id)->id]
);
$question1 = $questionsgenerator->create_question('truefalse', null, ['category' => $questionscat->id]);
$questionsgenerator->create_question_tag(
['questionid' => $question1->id, 'tag' => 'adpq_10']
);
$question2 = $questionsgenerator->create_question('shortanswer', null, ['category' => $questionscat->id]);
$questionsgenerator->create_question_tag(
['questionid' => $question2->id, 'tag' => 'adpq_11']
);
$question3 = $questionsgenerator->create_question('truefalse', null, ['category' => $questionscat->id]);
$questionsgenerator->create_question_tag(
['questionid' => $question3->id, 'tag' => 'adpq_12']
);
core_tag_tag::add_item_tag('core', 'course', $course->id, context_course::instance($course->id), 'adpq_10');
$tagidlist = tags_repository::get_tag_id_list_by_tag_names(
['adpq_10', 'adpq_11']
);
$questionstags = core_tag_tag::get_items_tags('core_question', 'question', [$question1->id, $question2->id]);
$itemtagidlist = array_map(function(array $itemtags): int {
return $itemtags[array_key_first($itemtags)]->id;
}, $questionstags);
$this->assertEquals(2, count($tagidlist));
$this->assertEquals(
[0 => array_shift($itemtagidlist), 1 => array_shift($itemtagidlist)],
$tagidlist
);
}
public function test_it_fails_to_get_a_list_of_tag_id_for_an_empty_array_of_tag_names(): void {
$this->expectException(coding_exception::class);
tags_repository::get_tag_id_list_by_tag_names([]);
}
}
<?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/>.
namespace mod_adaptivequiz;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot .'/mod/adaptivequiz/locallib.php');
use advanced_testcase;
use context_course;
use context_module;
use mod_adaptivequiz\event\attempt_completed;
use mod_adaptivequiz\local\attempt\attempt_state;
/**
* Adaptive locallib.php PHPUnit tests.
*
* @package mod_adaptivequiz
* @copyright 2013 Remote-Learner {@link http://www.remote-learner.ca/}
* @copyright 2022 onwards Vitaly Potenko <potenkov@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class locallib_test extends advanced_testcase {
/**
* This functions loads data via the tests/fixtures/mod_adaptivequiz.xml file
*/
protected function setup_test_data_xml() {
$this->dataset_from_files(
[__DIR__.'/fixtures/mod_adaptivequiz.xml']
)->to_database();
}
/**
* Provide input data to the parameters of the test_count_user_previous_attempts_fail() method.
* @return $data an array with arrays of data
*/
public function fail_attempt_data() {
$data = array(
array(99, 99),
array(99, 3),
array(13, 99),
);
return $data;
}
/**
* Provide input data to the parameters of the test_allowed_attempt_fail() method.
* @return $data an array with arrays of data
*/
public function attempts_allowed_data_fail() {
$data = array(
array(99, 100),
array(99, 99),
);
return $data;
}
/**
* Provide input data to the parameters of the test_allowed_attempt() method.
* @return $data an array with arrays of data
*/
public function attempts_allowed_data() {
$data = array(
array(99, 98),
array(0, 99),
);
return $data;
}
/**
* Provide input data to the parameters of the test_adaptivequiz_construct_view_report_orderby() method.
*/
public function view_reports_data(): array {
return [
['firstname', 'ASC'],
['firstname', 'DESC'],
['lastname', 'ASC'],
['lastname', 'DESC'],
['attempts', 'ASC'],
['attempts', 'DESC'],
['stderror', 'ASC'],
['stderror', 'DESC'],
];
}
/**
* Test retrieving an array of question categories.
*
* @covers ::adaptivequiz_get_question_categories
*/
public function test_get_question_categories() {
$this->resetAfterTest();
$this->setAdminUser();
$course = $this->getDataGenerator()->create_course();
$coursecontext = context_course::instance($course->id);
adaptivequiz_make_default_categories($coursecontext);
// Test it returns data for both course and activity contexts.
$data = adaptivequiz_get_question_categories($coursecontext);
$this->assertEquals(1, count($data));
$questioncategory = $this->getDataGenerator()
->get_plugin_generator('core_question')
->create_question_category(['name' => 'My category']);
$adaptivequiz = $this->getDataGenerator()
->get_plugin_generator('mod_adaptivequiz')
->create_instance([
'course' => $course->id,
'questionpool' => [$questioncategory->id],
]);
$cm = get_coursemodule_from_instance('adaptivequiz', $adaptivequiz->id);
$activitycontext = context_module::instance($cm->id);
$data = adaptivequiz_get_question_categories($activitycontext);
$this->assertEquals(2, count($data));
}
/**
* Test retrieving question categories used by the activity instance.
*
* @covers ::adaptivequiz_get_selected_question_cateogires
*/
public function test_get_selected_question_cateogires() {
$this->resetAfterTest(true);
$this->setup_test_data_xml();
$data = adaptivequiz_get_selected_question_cateogires(12);
$this->assertEquals(6, count($data));
}
/**
* This function tests failing conditions for counting user's previous attempts
* that have been marked as completed.
*
* @dataProvider fail_attempt_data
* @param int $instanceid activity instance id
* @param int $userid user id
* @covers ::adaptivequiz_count_user_previous_attempts
*/
public function test_count_user_previous_attempts_fail($instanceid, $userid) {
$this->resetAfterTest(true);
$this->setup_test_data_xml();
$result = adaptivequiz_count_user_previous_attempts($instanceid, $userid);
$this->assertEquals(0, $result);
}
/**
* This function tests a non-failing conditions for counting user's previous attempts
* that have been marked as completed.
*
* @covers ::adaptivequiz_count_user_previous_attempts
*/
public function test_count_user_previous_attempts_inprogress() {
$this->resetAfterTest(true);
$this->setup_test_data_xml();
$result = adaptivequiz_count_user_previous_attempts(13, 3);
$this->assertEquals(1, $result);
}
/**
* This function tests failing conditions for determining whether a user is allowed
* further attemtps at the activity.
*
* @dataProvider attempts_allowed_data_fail
* @param int $maxattempts the maximum number of attempts allowed
* @param int $attempts the number of attempts taken thus far
* @covers ::adaptivequiz_allowed_attempt
*/
public function test_allowed_attempt_no_more_attempts_allowed($maxattempts, $attempts) {
$data = adaptivequiz_allowed_attempt($maxattempts, $attempts);
$this->assertFalse($data);
}
/**
* This function tests failing conditions for determining whether a user is allowed
* further attemtps at the activity.
*
* @dataProvider attempts_allowed_data
* @param int $maxattempts the maximum number of attempts allowed
* @param int $attempts the number of attempts taken thus far
* @covers ::adaptivequiz_allowed_attempt
*/
public function test_allowed_attempt($maxattempts, $attempts) {
$data = adaptivequiz_allowed_attempt($maxattempts, $attempts);
$this->assertTrue($data);
}
/**
* This function tests adaptivequiz_uniqueid_part_of_attempt().
*
* @covers ::adaptivequiz_uniqueid_part_of_attempt
*/
public function test_adaptivequiz_uniqueid_part_of_attempt() {
$this->resetAfterTest(true);
$this->setup_test_data_xml();
// Assert that there exists a record where the uniqueid, activity instance and userid all match up.
$result = adaptivequiz_uniqueid_part_of_attempt(3, 1, 2);
$this->assertTrue($result);
$result = adaptivequiz_uniqueid_part_of_attempt(1, 1, 1);
$this->assertFalse($result);
}
/**
* This function tests the updating of the attempt data.
*
* @covers ::adaptivequiz_update_attempt_data
*/
public function test_adaptivequiz_update_attempt_data() {
global $DB;
$this->resetAfterTest(true);
$this->setup_test_data_xml();
$result = adaptivequiz_update_attempt_data(3, 13, 3, 50, 0.002, 0.99);
$record = $DB->get_record('adaptivequiz_attempt', ['id' => 2]);
$this->assertTrue($result);
$this->assertEquals(51, $record->difficultysum);
$this->assertEquals(1, $record->questionsattempted);
$this->assertEquals(0.002, $record->standarderror);
$this->assertEquals(0.99, $record->measure);
}
/**
* This function tests the updating of the attempt data.
*
* @covers ::adaptivequiz_update_attempt_data
*/
public function test_adaptivequiz_update_attempt_data_using_infinite_value() {
global $DB;
$this->resetAfterTest(true);
$this->setup_test_data_xml();
$result = adaptivequiz_update_attempt_data(3, 13, 3, -INF, 0.02, 0.1);
$record = $DB->get_record('adaptivequiz_attempt', ['id' => 2]);
$this->assertFalse($result);
}
/**
* This function tests completing an attempt.
*
* @covers ::adaptivequiz_complete_attempt
*/
public function test_adaptivequiz_complete_attempt() {
global $DB;
$this->resetAfterTest();
$this->setup_test_data_xml();
$adaptivequizid = 1;
$cmid = 5;
$userid = 2;
$attemptid = 1;
$adaptivequiz = $DB->get_record('adaptivequiz', ['id' => $adaptivequizid]);
$context = context_module::instance($cmid);
adaptivequiz_complete_attempt(3, $adaptivequiz, $context, $userid, '1', 'php unit test');
$attempt = $DB->get_record('adaptivequiz_attempt', ['id' => $attemptid]);
$this->assertEquals('php unit test', $attempt->attemptstopcriteria);
$this->assertEquals(attempt_state::COMPLETED, $attempt->attemptstate);
$this->assertEquals('1.00000', $attempt->standarderror);
}
/**
* @test
* @covers ::adaptivequiz_complete_attempt
*/
public function event_is_triggered_on_attempt_completion(): void {
global $DB;
$this->resetAfterTest();
$this->setup_test_data_xml();
$eventsink = $this->redirectEvents();
// This comes from fixtures unless we switch over to using generators.
$adaptivequizid = 1;
$cmid = 5;
$userid = 2;
$attemptid = 1;
$attempt = $DB->get_record('adaptivequiz_attempt', ['id' => $attemptid]);
$adaptivequiz = $DB->get_record('adaptivequiz', ['id' => $adaptivequizid]);
$context = context_module::instance($cmid);
adaptivequiz_complete_attempt(3, $adaptivequiz, $context, $userid, '1', 'php unit test');
$events = $eventsink->get_events();
$attemptcompletedevent = null;
foreach ($events as $event) {
if ($event instanceof attempt_completed) {
$attemptcompletedevent = $event;
break;
}
}
$this->assertNotNull($attemptcompletedevent,
sprintf('Failed asserting that event %s was triggered.', attempt_completed::class));
$this->assertEquals($attemptid, $event->objectid);
$this->assertEquals($context, $attemptcompletedevent->get_context());
$this->assertEquals($userid, $event->userid);
$this->assertEquals($attempt, $event->get_record_snapshot('adaptivequiz_attempt', $attemptid));
$this->assertEquals($adaptivequiz, $event->get_record_snapshot('adaptivequiz', $adaptivequizid));
}
/**
* This function tests checking if the minimum number of questions have been attempted.
*
* @covers ::adaptivequiz_min_attempts_reached
*/
public function test_adaptivequiz_min_attempts_reached() {
$this->resetAfterTest(true);
$this->setup_test_data_xml();
$result = adaptivequiz_min_attempts_reached(3, 13, 3);
$this->assertFalse($result);
$result = adaptivequiz_min_attempts_reached(4, 13, 4);
$this->assertTrue($result);
}
}
<?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/>.
/**
* PHPUnit tests for the renderer class.
*
* @copyright 2013 Remote-Learner {@link http://www.remote-learner.ca/}
* @copyright 2022 onwards Vitaly Potenko <potenkov@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_adaptivequiz;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot.'/mod/adaptivequiz/locallib.php');
require_once($CFG->dirroot.'/mod/adaptivequiz/renderer.php');
require_once($CFG->dirroot.'/tag/lib.php');
use advanced_testcase;
use mod_adaptivequiz_renderer;
use moodle_page;
use moodle_url;
use stdClass;
/**
* @group mod_adaptivequiz
* @covers \mod_adaptivequiz_renderer
*/
class renderer_test extends advanced_testcase {
/**
* This function tests the output from the get_js_module.
*/
public function test_adaptivequiz_get_js_module(): void {
$dummypage = new moodle_page();
$target = 'mod_adaptivequiz';
$renderer = new mod_adaptivequiz_renderer($dummypage, $target);
$output = $renderer->adaptivequiz_get_js_module();
$this->assertArrayHasKey('name', $output);
$this->assertContains('mod_adaptivequiz', $output);
$this->assertArrayHasKey('fullpath', $output);
$this->assertContains('/mod/adaptivequiz/module.js', $output);
$this->assertArrayHasKey('requires', $output);
$this->assertEquals(
['base', 'dom', 'event-delegate', 'event-key', 'core_question_engine', 'moodle-core-formchangechecker'],
$output['requires']
);
$this->assertArrayHasKey('strings', $output);
}
/**
* This function tests how init_metadata() handlss an integer
*/
public function test_init_metadata_with_integer() {
$dummypage = new moodle_page();
$target = 'mod_adaptivequiz';
$renderer = new mod_adaptivequiz_renderer($dummypage, $target);
$mockquba = $this->createPartialMock('question_usage_by_activity', ['render_question_head_html']);
$mockquba->expects($this->once())
->method('render_question_head_html')
->willReturn('');
// Only testing that the mock object's method is called once.
$renderer->init_metadata($mockquba, 1);
}
/**
* This function tests the output from print_form_and_button()
*/
public function test_print_form_and_button() {
$dummypage = new moodle_page();
$target = 'mod_adaptivequiz';
$renderer = new mod_adaptivequiz_renderer($dummypage, $target);
$url = new moodle_url('/test/phpunittest/test.php', array('cmid' => 99));
$text = 'phpunit test button';
$output = $renderer->print_form_and_button($url, $text);
$this->assertStringContainsString('<form', $output);
$this->assertStringContainsString('<input', $output);
$this->assertStringContainsString('type="submit"', $output);
$this->assertStringContainsString('/test/phpunittest/test.php', $output);
$this->assertStringContainsString('cmid=99', $output);
$this->assertStringContainsString('phpunit test button', $output);
$this->assertStringContainsString('<center>', $output);
$this->assertStringContainsString('</center>', $output);
$this->assertStringContainsString('</form>', $output);
}
/**
* This function tests the output from format_report_table_headers()
*/
public function test_format_report_table_headers() {
$dummypage = new moodle_page();
$target = 'mod_adaptivequiz';
$renderer = new mod_adaptivequiz_renderer($dummypage, $target);
$dummycm = new stdClass();
$dummycm->id = 99;
$output = $renderer->format_report_table_headers($dummycm, 'stderror', 'ASC');
$this->assertEquals(6, count($output));
$this->assertStringContainsString('/mod/adaptivequiz/viewreport.php', $output[0]);
$this->assertStringContainsString('sort=firstname&amp;sortdir=ASC', $output[0]);
$this->assertStringContainsString('sort=lastname&amp;sortdir=ASC', $output[0]);
$this->assertStringContainsString('/mod/adaptivequiz/viewreport.php', $output[1]);
$this->assertStringContainsString('sort=email&amp;sortdir=ASC', $output[1]);
$this->assertStringContainsString('/mod/adaptivequiz/viewreport.php', $output[2]);
$this->assertStringContainsString('sort=attempts&amp;sortdir=ASC', $output[2]);
$this->assertStringContainsString('/mod/adaptivequiz/viewreport.php', $output[3]);
$this->assertStringContainsString('sort=measure&amp;sortdir=ASC', $output[3]);
$this->assertStringContainsString('/mod/adaptivequiz/viewreport.php', $output[4]);
$this->assertStringContainsString('sort=stderror&amp;sortdir=DESC', $output[4]);
$this->assertStringContainsString('/mod/adaptivequiz/viewreport.php', $output[5]);
$this->assertStringContainsString('sort=timemodified&amp;sortdir=ASC', $output[5]);
}
}
<?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/>.
/**
* Plugin basic info.
*
* @package mod_adaptivequiz
* @copyright 2013 Remote-Learner {@link http://www.remote-learner.ca/}
* @copyright 2022 onwards Vitaly Potenko <potenkov@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2024041901; //version date of the MOODLE-401 branch from which it is forked with counter 01
$plugin->release = '2.3.6dev';
$plugin->maturity = MATURITY_ALPHA;
$plugin->requires = 2022112800;
$plugin->cron = 0;
$plugin->component = 'mod_adaptivequiz';
<?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/>.
/**
* Adaptive testing main view page script.
*
* @package mod_adaptivequiz
* @copyright 2013 Remote-Learner {@link http://www.remote-learner.ca/}
* @copyright 2022 onwards Vitaly Potenko <potenkov@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../../config.php');
require_once($CFG->libdir . '/tablelib.php');
require_once($CFG->dirroot.'/mod/adaptivequiz/locallib.php');
use mod_adaptivequiz\local\report\questions_difficulty_range;
use mod_adaptivequiz\local\report\users_attempts\filter\filter;
use mod_adaptivequiz\local\report\users_attempts\filter\filter_form;
use mod_adaptivequiz\local\report\users_attempts\filter\filter_options;
use mod_adaptivequiz\local\report\users_attempts\user_preferences\filter_user_preferences;
use mod_adaptivequiz\local\user_attempts_table;
use mod_adaptivequiz\local\report\users_attempts\users_attempts_table;
use mod_adaptivequiz\local\report\individual_user_attempts\table as individual_user_attempts_table;
use mod_adaptivequiz\local\report\individual_user_attempts\filter as user_attempts_table_filter;
use mod_adaptivequiz\local\report\users_attempts\user_preferences\user_preferences_form;
use mod_adaptivequiz\local\report\users_attempts\user_preferences\user_preferences_repository;
use mod_adaptivequiz\local\report\users_attempts\user_preferences\user_preferences;
use mod_adaptivequiz\output\user_attempt_summary;
$id = optional_param('id', 0, PARAM_INT);
$downloadusersattempts = optional_param('download', '', PARAM_ALPHA);
$n = optional_param('n', 0, PARAM_INT);
$resetfilter = optional_param('resetfilter', 0, PARAM_INT);
if ($id) {
$cm = get_coursemodule_from_id('adaptivequiz', $id, 0, false, MUST_EXIST);
$course = $DB->get_record('course', ['id' => $cm->course], '*', MUST_EXIST);
$adaptivequiz = $DB->get_record('adaptivequiz', ['id' => $cm->instance], '*', MUST_EXIST);
} else if ($n) {
$adaptivequiz = $DB->get_record('adaptivequiz', ['id' => $n], '*', MUST_EXIST);
$course = $DB->get_record('course', ['id' => $adaptivequiz->course], '*', MUST_EXIST);
$cm = get_coursemodule_from_instance('adaptivequiz', $adaptivequiz->id, $course->id, false, MUST_EXIST);
} else {
throw new moodle_exception('invalidarguments');
}
require_login($course, true, $cm);
$context = context_module::instance($cm->id);
$PAGE->set_url('/mod/adaptivequiz/view.php', ['id' => $cm->id]);
$PAGE->set_context($context);
$PAGE->add_body_class('limitedwidth');
/** @var mod_adaptivequiz_renderer $renderer */
$renderer = $PAGE->get_renderer('mod_adaptivequiz');
$canviewattemptsreport = has_capability('mod/adaptivequiz:viewreport', $context);
if ($canviewattemptsreport) {
$reportuserprefs = user_preferences_repository::get();
$reportuserprefsform = new user_preferences_form($PAGE->url->out());
if ($prefsformdata = $reportuserprefsform->get_data()) {
$reportuserprefs = user_preferences::from_plain_object($prefsformdata);
if (!$reportuserprefs->persistent_filter() && $reportuserprefs->has_filter_preference()) {
$reportuserprefs = $reportuserprefs->without_filter_preference();
}
user_preferences_repository::save($reportuserprefs);
}
$reportuserprefsform->set_data($reportuserprefs->as_array());
$filter = filter::from_vars($adaptivequiz->id, groups_get_activity_group($cm, true));
if ($resetfilter) {
$filter->fill_from_array(['users' => filter_options::users_option_default(),
'includeinactiveenrolments' => filter_options::INCLUDE_INACTIVE_ENROLMENTS_DEFAULT]);
}
$reportfilterform = new filter_form($PAGE->url->out(), ['actionurl' => $PAGE->url]);
if ($reportuserprefs->persistent_filter() && $reportuserprefs->has_filter_preference()) {
$filter->fill_from_preference($reportuserprefs->filter());
$reportfilterform->set_data($reportuserprefs->filter()->as_array());
}
if ($resetfilter) {
$filterdefaultsarray = ['users' => filter_options::users_option_default(),
'includeinactiveenrolments' => filter_options::INCLUDE_INACTIVE_ENROLMENTS_DEFAULT];
$filter->fill_from_array($filterdefaultsarray);
if ($reportuserprefs->persistent_filter()) {
user_preferences_repository::save(
$reportuserprefs->with_filter_preference(filter_user_preferences::from_array($filterdefaultsarray))
);
}
$reportfilterform->set_data($filterdefaultsarray);
}
if ($filterformdata = $reportfilterform->get_data()) {
$filter->fill_from_array((array) $filterformdata);
if ($reportuserprefs->persistent_filter()) {
user_preferences_repository::save(
$reportuserprefs->with_filter_preference(filter_user_preferences::from_array((array) $filterformdata))
);
}
}
$attemptsreporttable = new users_attempts_table($renderer, $cm->id,
questions_difficulty_range::from_activity_instance($adaptivequiz), $PAGE->url, $context, $filter);
$attemptsreporttable->is_downloading($downloadusersattempts,
get_string('reportattemptsdownloadfilename', 'adaptivequiz', format_string($adaptivequiz->name)));
if ($attemptsreporttable->is_downloading()) {
$attemptsreporttable->out(1, false);
exit;
}
}
$event = \mod_adaptivequiz\event\course_module_viewed::create([
'objectid' => $PAGE->cm->instance,
'context' => $PAGE->context,
]);
$event->add_record_snapshot('course', $PAGE->course);
$event->add_record_snapshot($PAGE->cm->modname, $adaptivequiz);
$event->trigger();
$PAGE->set_title(format_string($adaptivequiz->name));
$PAGE->set_heading(format_string($course->fullname));
echo $OUTPUT->header();
if (has_capability('mod/folder:managefiles', $context)) {
//KNIGHT: Capability managefiles checks if the current user is either an admin or a teacher (to show question check reminder)
$completedattemptscount = adaptivequiz_count_user_previous_attempts($adaptivequiz->id, $USER->id);
if($adaptivequiz->questionchecktrigger > 0 && $adaptivequiz->questionschecked == 0 && $completedattemptscount >= $adaptivequiz->questionchecktrigger) {
$notification = \core\notification::add(get_string('questioncheckmessage', 'adaptivequiz'), \core\output\notification::NOTIFY_INFO);
}
}
if (has_capability('mod/adaptivequiz:attempt', $context)) {
$completedattemptscount = adaptivequiz_count_user_previous_attempts($adaptivequiz->id, $USER->id);
echo $renderer->container_start('attempt-controls-or-notification-container pb-3');
echo $renderer->attempt_controls_or_notification($cm->id,
adaptivequiz_allowed_attempt($adaptivequiz->attempts, $completedattemptscount), $adaptivequiz->browsersecurity);
echo $renderer->container_end();
$allattemptscount = $DB->count_records('adaptivequiz_attempt',
['instance' => $adaptivequiz->id, 'userid' => $USER->id]);
echo $renderer->heading(get_string('attemptsuserprevious', 'adaptivequiz'), 3);
//KNIGHT: Filter if students should be allowed to view own attempts
if($adaptivequiz->showownattemptresult && $allattemptscount){
$attemptstable = new individual_user_attempts_table(
$renderer,
user_attempts_table_filter::from_vars($cm->instance, $USER->id),
$PAGE->url,
questions_difficulty_range::from_activity_instance($adaptivequiz),
$cm->id,
$canviewattemptsreport
);
$attemptstable->out(20, false);
}
if (!$allattemptscount) {
echo html_writer::div(get_string('attemptsusernoprevious', 'adaptivequiz'), 'alert alert-info text-center');
}
}
if ($canviewattemptsreport) {
echo $renderer->heading(get_string('activityreports', 'adaptivequiz'), '3', 'text-center');
groups_print_activity_menu($cm, new moodle_url('/mod/adaptivequiz/view.php', ['id' => $cm->id]));
echo $renderer->container_start('usersattemptstable-wrapper');
$attemptsreporttable->out($reportuserprefs->rows_per_page(), $reportuserprefs->show_initials_bar());
echo $renderer->container_end();
$reportuserprefsform->display();
$reportfilterform->display();
$resetfilterul = $PAGE->url;
$resetfilterul->param('resetfilter', 1);
echo $renderer->reset_users_attempts_filter_action($resetfilterul);
}
echo $OUTPUT->footer();
<?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/>.
/**
* Adaptive quiz view attempt report script.
*
* @copyright 2013 onwards Remote-Learner {@link http://www.remote-learner.ca/}
* @copyright 2022 onwards Vitaly Potenko <potenkov@gmail.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(dirname(__FILE__).'/../../config.php');
require_once($CFG->dirroot.'/mod/adaptivequiz/locallib.php');
use mod_adaptivequiz\local\report\individual_user_attempts\filter as user_attempts_table;
use mod_adaptivequiz\local\report\questions_difficulty_range;
use mod_adaptivequiz\local\report\individual_user_attempts\table as individual_user_attempts_table;
$id = required_param('cmid', PARAM_INT);
$userid = required_param('userid', PARAM_INT);
$cm = get_coursemodule_from_id('adaptivequiz', $id, 0, false, MUST_EXIST);
$adaptivequiz = $DB->get_record('adaptivequiz', ['id' => $cm->instance], '*', MUST_EXIST);
$course = $DB->get_record('course', ['id' => $cm->course], '*', MUST_EXIST);
$user = $DB->get_record('user', ['id' => $userid], '*', MUST_EXIST);
require_login($course, true, $cm);
$context = context_module::instance($cm->id);
require_capability('mod/adaptivequiz:viewreport', $context);
$PAGE->set_context($context);
$PAGE->set_url('/mod/adaptivequiz/viewattemptreport.php', ['cmid' => $cm->id, 'userid' => $user->id]);
$a = new stdClass();
$a->quizname = format_string($adaptivequiz->name);
$a->username = fullname($user);
$title = get_string('reportindividualuserattemptpageheading', 'adaptivequiz', $a);
$PAGE->set_title($title);
$PAGE->set_heading(format_string($course->fullname));
/** @var mod_adaptivequiz_renderer $renderer */
$renderer = $PAGE->get_renderer('mod_adaptivequiz');
$header = $renderer->print_header();
$footer = $renderer->print_footer();
echo $header;
echo $renderer->heading($title);
$attemptstable = new individual_user_attempts_table(
$renderer,
user_attempts_table::from_vars($cm->instance, $user->id),
$PAGE->url,
questions_difficulty_range::from_activity_instance($adaptivequiz),
$cm->id,
true //KNIGHT
);
$attemptstable->out(20, false);
echo $footer;
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment