From 066a8b7b479d79baa9a902710a7089104e1a1315 Mon Sep 17 00:00:00 2001 From: Sebastian Date: Sun, 13 Oct 2024 11:41:00 +0200 Subject: [PATCH] #222 #234 DRAFT - Refactoring to simplify codebase --- action.php | 26 +- async.php | 32 +- backup/moodle2/backup_capquiz_stepslib.php | 27 +- backup/moodle2/restore_capquiz_stepslib.php | 33 +-- classes/bank/add_to_quiz_action.php | 14 +- classes/capquiz.php | 275 ++++++------------ classes/capquiz_action_performer.php | 54 ++-- classes/capquiz_matchmaking_strategy.php | 67 ----- .../capquiz_matchmaking_strategy_loader.php | 227 --------------- classes/capquiz_question.php | 145 ++++----- classes/capquiz_question_attempt.php | 216 +++++++------- classes/capquiz_question_engine.php | 158 +++++++--- classes/capquiz_question_list.php | 41 ++- classes/capquiz_question_rating.php | 100 ++----- classes/capquiz_rating_system.php | 75 ----- classes/capquiz_rating_system_loader.php | 213 -------------- classes/capquiz_urls.php | 222 ++------------ classes/capquiz_user.php | 161 ++++------ classes/capquiz_user_rating.php | 91 ++---- classes/elo_rating_system.php | 79 +++++ .../form/view/grading_configuration_form.php | 121 -------- .../matchmaking_strategy_selection_form.php | 98 ------- .../form/view/question_list_create_form.php | 2 +- .../view/rating_system_selection_form.php | 97 ------ .../capquiz_matchmaking_strategy_registry.php | 155 ---------- .../chronologic/chronologic_selector.php | 86 ------ .../n_closest_configuration_form.php | 111 ------- .../n_closest/n_closest_selector.php | 174 ----------- classes/output/basic_renderer.php | 79 ----- classes/output/classlist_renderer.php | 21 +- .../output/grading_configuration_renderer.php | 121 -------- classes/output/import_renderer.php | 4 +- .../output/instructor_dashboard_renderer.php | 12 +- .../matchmaking_configuration_renderer.php | 109 ------- ...atchmaking_strategy_selection_renderer.php | 99 ------- classes/output/question_attempt_renderer.php | 69 ++--- classes/output/question_bank_renderer.php | 23 +- .../output/question_list_creator_renderer.php | 15 +- classes/output/question_list_renderer.php | 40 ++- .../question_list_selection_renderer.php | 29 +- .../rating_system_configuration_renderer.php | 107 ------- .../rating_system_selection_renderer.php | 98 ------- classes/output/renderer.php | 177 ++--------- classes/output/report_renderer.php | 29 +- classes/output/unauthorized_view_renderer.php | 57 ---- classes/privacy/provider.php | 4 +- .../capquiz_rating_system_registry.php | 138 --------- .../elo_rating/elo_rating_system.php | 121 -------- .../elo_rating/elo_rating_system_form.php | 81 ------ db/access.php | 4 +- db/install.xml | 32 +- db/upgrade.php | 81 +++++- edit.php | 42 --- error.php | 46 --- grade.php | 48 --- index.php | 4 +- lang/en/capquiz.php | 3 +- lib.php | 44 ++- locallib.php | 34 --- mod_form.php | 176 ++++++++++- report/attempts/report.php | 6 +- report/attemptsreport.php | 2 +- report/attemptsreport_table.php | 1 - report/questions/questions_table.php | 2 - report/questions/report.php | 8 +- report/report.php | 2 +- report/reportfactory.php | 8 +- report/reportlib.php | 9 +- templates/button.mustache | 31 -- templates/configure_grading.mustache | 26 -- templates/matchmaking_configuration.mustache | 27 -- .../matchmaking_selection_strategy.mustache | 26 -- .../rating_system_configuration.mustache | 27 -- templates/rating_system_selection.mustache | 26 -- templates/unauthorized.mustache | 24 -- version.php | 8 +- view.php | 83 ++++-- view_classlist.php | 40 --- view_create_question_list.php | 47 --- view_grading.php | 40 --- view_import.php | 40 --- view_rating_system.php | 40 --- view_report.php | 41 --- 83 files changed, 1215 insertions(+), 4396 deletions(-) delete mode 100755 classes/capquiz_matchmaking_strategy.php delete mode 100755 classes/capquiz_matchmaking_strategy_loader.php delete mode 100755 classes/capquiz_rating_system.php delete mode 100755 classes/capquiz_rating_system_loader.php create mode 100644 classes/elo_rating_system.php delete mode 100644 classes/form/view/grading_configuration_form.php delete mode 100755 classes/form/view/matchmaking_strategy_selection_form.php delete mode 100755 classes/form/view/rating_system_selection_form.php delete mode 100755 classes/matchmaking/capquiz_matchmaking_strategy_registry.php delete mode 100755 classes/matchmaking/chronologic/chronologic_selector.php delete mode 100755 classes/matchmaking/n_closest/n_closest_configuration_form.php delete mode 100755 classes/matchmaking/n_closest/n_closest_selector.php delete mode 100755 classes/output/basic_renderer.php delete mode 100644 classes/output/grading_configuration_renderer.php delete mode 100644 classes/output/matchmaking_configuration_renderer.php delete mode 100644 classes/output/matchmaking_strategy_selection_renderer.php delete mode 100644 classes/output/rating_system_configuration_renderer.php delete mode 100644 classes/output/rating_system_selection_renderer.php delete mode 100755 classes/output/unauthorized_view_renderer.php delete mode 100755 classes/rating_system/capquiz_rating_system_registry.php delete mode 100755 classes/rating_system/elo_rating/elo_rating_system.php delete mode 100755 classes/rating_system/elo_rating/elo_rating_system_form.php delete mode 100755 edit.php delete mode 100755 error.php delete mode 100644 grade.php delete mode 100644 locallib.php delete mode 100755 templates/button.mustache delete mode 100644 templates/configure_grading.mustache delete mode 100755 templates/matchmaking_configuration.mustache delete mode 100755 templates/matchmaking_selection_strategy.mustache delete mode 100755 templates/rating_system_configuration.mustache delete mode 100755 templates/rating_system_selection.mustache delete mode 100755 templates/unauthorized.mustache delete mode 100755 view_classlist.php delete mode 100755 view_create_question_list.php delete mode 100644 view_grading.php delete mode 100644 view_import.php delete mode 100644 view_rating_system.php delete mode 100644 view_report.php diff --git a/action.php b/action.php index 191e8eb..2f3475c 100755 --- a/action.php +++ b/action.php @@ -19,28 +19,34 @@ * * @package mod_capquiz * @author Aleksander Skrede - * @copyright 2018 NTNU + * @copyright 2018 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -namespace mod_capquiz; +use mod_capquiz\capquiz; +use mod_capquiz\capquiz_action_performer; + +require_once ("../../config.php"); +global $CFG; -require_once("../../config.php"); require_once($CFG->libdir . '/formslib.php'); require_once($CFG->dirroot . '/mod/capquiz/lib.php'); require_once($CFG->dirroot . '/mod/capquiz/classes/capquiz_action_performer.php'); -$cmid = capquiz_urls::require_course_module_id_param(); +global $PAGE; + +$cmid = required_param('id', PARAM_INT); $cm = get_coursemodule_from_id('capquiz', $cmid, 0, false, MUST_EXIST); require_login($cm->course, false, $cm); -$context = \context_module::instance($cmid); +$context = context_module::instance($cmid); require_capability('mod/capquiz:instructor', $context); - $action = required_param('action', PARAM_TEXT); -$capquiz = new capquiz($cmid); -capquiz_urls::set_page_url($capquiz, capquiz_urls::$urlasync); -capquiz_action_performer::perform($action, $capquiz); +$PAGE->set_context($context); +$PAGE->set_cm($cm); +$PAGE->set_url(new moodle_url('/mod/capquiz/async.php')); + +capquiz_action_performer::perform($action, new capquiz($cmid)); -capquiz_urls::redirect_to_dashboard(); +redirect(new moodle_url('/mod/capquiz/view.php', ['id' => $cmid])); diff --git a/async.php b/async.php index 7a9ed25..7b21d6c 100755 --- a/async.php +++ b/async.php @@ -19,36 +19,42 @@ * * @package mod_capquiz * @author Aleksander Skrede - * @copyright 2018 NTNU + * @copyright 2018 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -namespace mod_capquiz; +use mod_capquiz\capquiz; +use mod_capquiz\capquiz_question_attempt; +use mod_capquiz\capquiz_question_engine; -require_once('../../config.php'); +require_once(__DIR__ . '/../../config.php'); -$cmid = capquiz_urls::require_course_module_id_param(); +global $PAGE; + +$cmid = required_param('id', PARAM_INT); $cm = get_coursemodule_from_id('capquiz', $cmid, 0, false, MUST_EXIST); require_login($cm->course, false, $cm); -$context = \context_module::instance($cmid); + +$context = context_module::instance($cmid); require_capability('mod/capquiz:student', $context); -$action = required_param('action', PARAM_TEXT); $attemptid = optional_param('attempt', null, PARAM_INT); -$cmid = capquiz_urls::require_course_module_id_param(); -$capquiz = new capquiz($cmid); +$action = required_param('action', PARAM_TEXT); -capquiz_urls::set_page_url($capquiz, capquiz_urls::$urlasync); +$PAGE->set_context($context); +$PAGE->set_cm($cm); +$PAGE->set_url(new moodle_url('/mod/capquiz/async.php')); if ($attemptid !== null) { + $capquiz = new capquiz($cmid); $user = $capquiz->user(); $attempt = capquiz_question_attempt::load_attempt($user, $attemptid); if ($action === 'answered') { - $capquiz->question_engine($user)->attempt_answered($user, $attempt); + (new capquiz_question_engine($capquiz))->attempt_answered($user, $attempt); } else if ($action === 'reviewed') { - $capquiz->question_engine($user)->attempt_reviewed($attempt); + (new capquiz_question_engine($capquiz))->attempt_reviewed($attempt); } - capquiz_urls::redirect_to_dashboard(); + redirect(new moodle_url('/mod/capquiz/view.php', ['id' => $cmid])); } -capquiz_urls::redirect_to_front_page(); +redirect(new moodle_url('/')); diff --git a/backup/moodle2/backup_capquiz_stepslib.php b/backup/moodle2/backup_capquiz_stepslib.php index 0bd25a8..cfd8cee 100644 --- a/backup/moodle2/backup_capquiz_stepslib.php +++ b/backup/moodle2/backup_capquiz_stepslib.php @@ -42,6 +42,13 @@ protected function define_structure() { 'timemodified', 'published', 'default_user_rating', + 'stars_to_pass', + 'timedue', + 'numquestioncandidates', + 'minquestionsuntilreappearance', + 'userwinprobability', + 'userkfactor', + 'questionkfactor', ]); $questionlist = new backup_nested_element('questionlist', null, [ 'id', @@ -68,18 +75,6 @@ protected function define_structure() { 'manual', 'timecreated', ]); - $questionselections = new backup_nested_element('questionselections'); - $questionselection = new backup_nested_element('questionselection', ['id'], [ - 'capquiz_id', - 'strategy', - 'configuration', - ]); - $ratingsystems = new backup_nested_element('ratingsystems'); - $ratingsystem = new backup_nested_element('ratingsystem', ['id'], [ - 'capquiz_id', - 'rating_system', - 'configuration', - ]); $users = new backup_nested_element('users'); $user = new backup_nested_element('user', ['id'], [ 'user_id', @@ -121,12 +116,6 @@ protected function define_structure() { $question->add_child($questionratings); $questionratings->add_child($questionrating); - $capquiz->add_child($questionselections); - $questionselections->add_child($questionselection); - - $capquiz->add_child($ratingsystems); - $ratingsystems->add_child($ratingsystem); - $capquiz->add_child($users); $users->add_child($user); $user->add_child($userratings); @@ -139,8 +128,6 @@ protected function define_structure() { $questionlist->set_source_table('capquiz_question_list', ['capquiz_id' => backup::VAR_PARENTID]); $question->set_source_table('capquiz_question', ['question_list_id' => backup::VAR_PARENTID]); $questionrating->set_source_table('capquiz_question_rating', ['capquiz_question_id' => backup::VAR_PARENTID]); - $questionselection->set_source_table('capquiz_question_selection', ['capquiz_id' => backup::VAR_PARENTID]); - $ratingsystem->set_source_table('capquiz_rating_system', ['capquiz_id' => backup::VAR_PARENTID]); if ($this->get_setting_value('userinfo')) { $user->set_source_table('capquiz_user', ['capquiz_id' => backup::VAR_PARENTID]); $userrating->set_source_table('capquiz_user_rating', ['capquiz_user_id' => backup::VAR_PARENTID]); diff --git a/backup/moodle2/restore_capquiz_stepslib.php b/backup/moodle2/restore_capquiz_stepslib.php index ff80470..5e302ec 100644 --- a/backup/moodle2/restore_capquiz_stepslib.php +++ b/backup/moodle2/restore_capquiz_stepslib.php @@ -41,13 +41,10 @@ class restore_capquiz_activity_structure_step extends restore_questions_activity protected function define_structure() { $paths = []; $paths[] = new restore_path_element('capquiz', '/activity/capquiz'); - $questionlist = new restore_path_element('capquiz_question_list', '/activity/capquiz/questionlist'); - $paths[] = $questionlist; + $paths[] = new restore_path_element('capquiz_question_list', '/activity/capquiz/questionlist'); $paths[] = new restore_path_element('capquiz_question', '/activity/capquiz/questionlist/questions/question'); $paths[] = new restore_path_element( 'capquiz_question_rating', '/activity/capquiz/questionlist/questions/question/questionratings/question_rating'); - $paths[] = new restore_path_element('capquiz_question_selection', '/activity/capquiz/questionselections/questionselection'); - $paths[] = new restore_path_element('capquiz_rating_system', '/activity/capquiz/ratingsystems/ratingsystem'); if ($this->get_setting_value('userinfo')) { $capuser = new restore_path_element('capquiz_user', '/activity/capquiz/users/user'); $this->add_question_usages($capuser, $paths); @@ -124,34 +121,6 @@ protected function process_capquiz_question_rating($data) { $this->set_mapping('capquiz_question_rating', $oldid, $newitemid); } - /** - * Processes and backs up capquiz question selection - * - * @param object $data - */ - protected function process_capquiz_question_selection($data) { - global $DB; - $data = (object)$data; - $data->capquiz_id = $this->get_new_parentid('capquiz'); - $oldid = $data->id; - $newitemid = $DB->insert_record('capquiz_question_selection', $data); - $this->set_mapping('capquiz_question_selection', $oldid, $newitemid); - } - - /** - * Processes and backs up capquiz question rating system - * - * @param object $data - */ - protected function process_capquiz_rating_system($data) { - global $DB; - $data = (object)$data; - $data->capquiz_id = $this->get_new_parentid('capquiz'); - $oldid = $data->id; - $newitemid = $DB->insert_record('capquiz_rating_system', $data); - $this->set_mapping('capquiz_rating_system', $oldid, $newitemid); - } - /** * Processes and backs up capquiz user * diff --git a/classes/bank/add_to_quiz_action.php b/classes/bank/add_to_quiz_action.php index 6cb9496..9ed6b84 100644 --- a/classes/bank/add_to_quiz_action.php +++ b/classes/bank/add_to_quiz_action.php @@ -16,15 +16,15 @@ namespace mod_capquiz\bank; -use mod_capquiz\capquiz_urls; +use moodle_url; use stdClass; /** * Question bank action to add question to quiz. * * @package mod_capquiz - * @copyright 2024 NTNU - * @author 2024 Sebastian Gundersen + * @author 2024 Sebastian Gundersen + * @copyright 2024 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class add_to_quiz_action extends \core_question\local\bank\question_action_base { @@ -38,9 +38,15 @@ class add_to_quiz_action extends \core_question\local\bank\question_action_base * $label - text label to display in the UI (either in the menu, or as a tool-tip on the icon) */ protected function get_url_icon_and_label(stdClass $question): array { + global $PAGE; if (!question_has_capability_on($question, 'use')) { return [null, null, null]; } - return [capquiz_urls::add_question_to_list_url($question->id), 't/add', get_string('addtoquiz', 'quiz')]; + $url = new moodle_url('/mod/capquiz/action.php', [ + 'id' => $PAGE->cm->id, + 'action' => 'add-question', + 'question-id' => $question->id, + ]); + return [$url, 't/add', get_string('addtoquiz', 'quiz')]; } } diff --git a/classes/capquiz.php b/classes/capquiz.php index 23a849e..0323ccc 100755 --- a/classes/capquiz.php +++ b/classes/capquiz.php @@ -18,56 +18,122 @@ * This file defines the class capquiz representing a capquiz * * @package mod_capquiz - * @author Sebastian S. Gundersen + * @author Sebastian Gundersen * @author Aleksander Skrede - * @copyright 2019 NTNU + * @copyright 2019 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace mod_capquiz; use context_module; -use moodle_page; +use core\persistent; use renderer_base; use stdClass; defined('MOODLE_INTERNAL') || die(); require_once($CFG->libdir . '/questionlib.php'); -require_once($CFG->dirroot . '/mod/capquiz/classes/capquiz_rating_system_loader.php'); -require_once($CFG->dirroot . '/mod/capquiz/classes/capquiz_matchmaking_strategy_loader.php'); /** * Class capquiz * * @package mod_capquiz - * @author Sebastian S. Gundersen + * @author Sebastian Gundersen * @author Aleksander Skrede - * @copyright 2019 NTNU + * @copyright 2024 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class capquiz { +class capquiz extends persistent { - /** @var context_module $context */ - private $context; + /** @var ?context_module $context */ + private ?context_module $context; - /** @var stdClass $cm */ - private $cm; + /** @var ?stdClass $cm */ + private ?stdClass $cm; - /** @var stdClass $courserecord */ - private $courserecord; - - /** @var stdClass $record */ - private $record; - - /** @var renderer_base $renderer */ - private renderer_base $renderer; + /** @var ?stdClass $courserecord */ + private ?stdClass $courserecord; /** @var ?capquiz_question_list $qlist */ private ?capquiz_question_list $qlist; - /** @var moodle_page $page */ - private moodle_page $page; + /** + * Return the definition of the properties of this model. + * + * @return array + */ + protected static function define_properties(): array { + return [ + 'course' => [ + 'type' => PARAM_INT, + 'default' => 0, + 'null' => NULL_NOT_ALLOWED, + ], + 'name' => [ + 'type' => PARAM_TEXT, + 'null' => NULL_NOT_ALLOWED, + ], + 'intro' => [ + 'type' => PARAM_TEXT, + 'null' => NULL_ALLOWED, + ], + 'introformat' => [ + 'type' => PARAM_INT, + 'default' => 0, + 'null' => NULL_NOT_ALLOWED, + ], + 'published' => [ + 'type' => PARAM_INT, + 'null' => NULL_NOT_ALLOWED, + ], + 'default_user_rating' => [ + 'type' => PARAM_INT, + 'default' => 1200, + 'null' => NULL_NOT_ALLOWED, + ], + 'stars_to_pass' => [ + 'type' => PARAM_INT, + 'default' => 3, + 'null' => NULL_NOT_ALLOWED, + ], + 'timedue' => [ + 'type' => PARAM_INT, + 'default' => 0, + 'null' => NULL_NOT_ALLOWED, + ], + 'questionselectiontype' => [ + 'type' => PARAM_TEXT, + 'default' => 'nclosest', + 'null' => NULL_NOT_ALLOWED, + ], + 'numquestioncandidates' => [ + 'type' => PARAM_INT, + 'default' => 10, + 'null' => NULL_NOT_ALLOWED, + ], + 'minquestionsuntilreappearance' => [ + 'type' => PARAM_INT, + 'default' => 0, + 'null' => NULL_NOT_ALLOWED, + ], + 'userwinprobability' => [ + 'type' => PARAM_FLOAT, + 'default' => 0.75, + 'null' => NULL_NOT_ALLOWED, + ], + 'userkfactor' => [ + 'type' => PARAM_FLOAT, + 'default' => 32.0, + 'null' => NULL_NOT_ALLOWED, + ], + 'questionkfactor' => [ + 'type' => PARAM_FLOAT, + 'default' => 8.0, + 'null' => NULL_NOT_ALLOWED, + ], + ]; + } /** * Constructor. @@ -79,164 +145,34 @@ public function __construct(int $cmid) { $this->cm = get_coursemodule_from_id('capquiz', $cmid, 0, false, MUST_EXIST); $this->context = context_module::instance($cmid); $PAGE->set_context($this->context); - $this->renderer = $PAGE->get_renderer('mod_capquiz'); $this->courserecord = $DB->get_record('course', ['id' => $this->cm->course], '*', MUST_EXIST); $this->record = $DB->get_record('capquiz', ['id' => $this->cm->instance], '*', MUST_EXIST); $this->qlist = capquiz_question_list::load_question_list($this); - $this->page = $PAGE; - } - - /** - * Updates the grades if the grading is completed or if forced - * - * @param bool $force - */ - public function update_grades(bool $force = false): void { - if (!$this->is_grading_completed() || $force) { - capquiz_update_grades($this->record); - } - } - - /** - * Returns the page of the CapQuiz - */ - public function get_page(): moodle_page { - return $this->page; - } - - /** - * Returns the capquiz' id - */ - public function id(): int { - return $this->record->id; - } - - /** - * Returns the capquiz' name - */ - public function name(): string { - return $this->record->name; - } - - /** - * Returns true if the capquiz is published - */ - public function is_published(): bool { - return $this->record->published; } /** * Returns true if the capquiz is completely graded */ public function is_grading_completed(): bool { - return $this->record->timedue < time() && $this->record->timedue > 0; - } - - /** - * Returns the amount of stars needed to pass - */ - public function stars_to_pass(): int { - return $this->record->stars_to_pass; - } - - /** - * Returns the time when the quiz is due - */ - public function time_due(): int { - return $this->record->timedue; - } - - /** - * Sets a new value for stars to pass - * - * @param int $stars - */ - public function set_stars_to_pass(int $stars): void { - global $DB; - $this->record->stars_to_pass = $stars; - $DB->update_record('capquiz', $this->record); - } - - /** - * Sets a new due time - * - * @param int $time - */ - public function set_time_due(int $time): void { - global $DB; - $this->record->timedue = $time; - $DB->update_record('capquiz', $this->record); - } - - /** - * Sets a new default rating - * - * @param float $rating - */ - public function set_default_user_rating(float $rating): void { - global $DB; - $this->record->default_user_rating = $rating; - $DB->update_record('capquiz', $this->record); - } - - /** - * Publishes the capquiz if it can publish it - */ - public function publish(): bool { - global $DB; - if (!$this->can_publish()) { - return false; - } - $this->record->published = true; - $DB->update_record('capquiz', $this->record); - return $this->is_published(); + return $this->get('timedue') < time() && $this->get('timedue') > 0; } /** * Returns true if the capquiz is publishable */ public function can_publish(): bool { - if (!$this->has_question_list() || $this->is_published()) { + if (!$this->question_list() || $this->get('published')) { return false; } return $this->question_list()->has_questions(); } - /** - * Returns a new question engine based on the user - * - * @param capquiz_user $user - */ - public function question_engine(capquiz_user $user): ?capquiz_question_engine { - $quba = $user->question_usage(); - if (!$quba) { - return null; - } - $ratingsystemloader = new capquiz_rating_system_loader($this); - $strategyloader = new capquiz_matchmaking_strategy_loader($this); - return new capquiz_question_engine($this, $quba, $strategyloader, $ratingsystemloader); - } - /** * Returns the capquiz user */ public function user(): ?capquiz_user { global $USER; - return capquiz_user::load_user($this, $USER->id, $this->context()); - } - - /** - * Returns the default rating - */ - public function default_user_rating(): float { - return $this->record->default_user_rating; - } - - /** - * Returns true if the capquiz has a question list - */ - public function has_question_list(): bool { - return $this->qlist !== null; + return capquiz_user::load_user($this, $USER->id, $this->context); } /** @@ -246,13 +182,6 @@ public function question_list(): ?capquiz_question_list { return $this->qlist; } - /** - * Returns the current context - */ - public function context(): context_module { - return $this->context; - } - /** * Returns teh current course module */ @@ -266,26 +195,4 @@ public function course_module(): stdClass { public function course(): stdClass { return $this->courserecord; } - - /** - * Returns the current renderer - */ - public function renderer(): renderer_base { - return $this->renderer; - } - - /** - * Validates the matchmaking and rating systems - */ - public function validate_matchmaking_and_rating_systems(): void { - $ratingsystemloader = new capquiz_rating_system_loader($this); - if (!$ratingsystemloader->has_rating_system()) { - $ratingsystemloader->set_default_rating_system(); - } - $strategyloader = new capquiz_matchmaking_strategy_loader($this); - if (!$strategyloader->has_strategy()) { - $strategyloader->set_default_strategy(); - } - } - } diff --git a/classes/capquiz_action_performer.php b/classes/capquiz_action_performer.php index 8e5e304..a73bed8 100755 --- a/classes/capquiz_action_performer.php +++ b/classes/capquiz_action_performer.php @@ -19,28 +19,27 @@ * * @package mod_capquiz * @author Aleksander Skrede - * @author Sebastian S. Gundersen - * @copyright 2019 NTNU + * @author Sebastian Gundersen + * @copyright 2019 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace mod_capquiz; +use moodle_url; use stdClass; defined('MOODLE_INTERNAL') || die(); require_once($CFG->libdir . '/questionlib.php'); -require_once($CFG->dirroot . '/mod/capquiz/classes/capquiz_rating_system_loader.php'); -require_once($CFG->dirroot . '/mod/capquiz/classes/capquiz_matchmaking_strategy_loader.php'); /** * Class capquiz_action_performer * * @package mod_capquiz * @author Aleksander Skrede - * @author Sebastian S. Gundersen - * @copyright 2019 NTNU + * @author Sebastian Gundersen + * @copyright 2019 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class capquiz_action_performer { @@ -52,6 +51,7 @@ class capquiz_action_performer { * @param capquiz $capquiz The capquiz on which the action will be performed */ public static function perform(string $action, capquiz $capquiz): void { + global $PAGE; switch ($action) { case 'redirect': self::redirect(); @@ -84,8 +84,8 @@ public static function perform(string $action, capquiz $capquiz): void { self::delete_question_list(); break; case 'regrade-all': - $capquiz->update_grades(true); - capquiz_urls::redirect_to_url(capquiz_urls::view_classlist_url()); + capquiz_update_grades($capquiz->to_record()); + redirect(new moodle_url('/mod/capquiz/view.php', ['id' => $PAGE->cm->id, 'page' => 'classlist'])); break; default: break; @@ -98,7 +98,7 @@ public static function perform(string $action, capquiz $capquiz): void { public static function redirect(): void { $url = optional_param('target-url', null, PARAM_TEXT); if ($url) { - capquiz_urls::redirect_to_url(new \moodle_url($url)); + redirect(new moodle_url($url)); } } @@ -110,10 +110,7 @@ public static function redirect(): void { public static function assign_question_list(capquiz $capquiz): void { $qlistid = optional_param('question-list-id', 0, PARAM_INT); $qlist = capquiz_question_list::load_any($qlistid); - if ($qlist) { - $capquiz->validate_matchmaking_and_rating_systems(); - $qlist->create_instance_copy($capquiz); - } + $qlist?->create_instance_copy($capquiz); } /** @@ -128,7 +125,8 @@ public static function add_question_to_list(capquiz $capquiz): void { foreach ($questionids as $questionid) { self::create_capquiz_question((int)$questionid, $qlist, $qlist->default_question_rating()); } - capquiz_urls::redirect_to_previous(); + header('Location: ' . $_SERVER['HTTP_REFERER']); + exit; } /** @@ -138,10 +136,11 @@ public static function add_question_to_list(capquiz $capquiz): void { */ public static function remove_question_from_list(capquiz $capquiz): void { $questionid = optional_param('question-id', 0, PARAM_INT); - if ($questionid && $capquiz->has_question_list()) { + if ($questionid && $capquiz->question_list() !== null) { self::remove_capquiz_question($questionid, $capquiz->question_list()->id()); } - capquiz_urls::redirect_to_previous(); + header('Location: ' . $_SERVER['HTTP_REFERER']); + exit; } /** @@ -150,7 +149,10 @@ public static function remove_question_from_list(capquiz $capquiz): void { * @param capquiz $capquiz */ public static function publish_capquiz(capquiz $capquiz): void { - $capquiz->publish(); + if ($capquiz->can_publish()) { + $capquiz->set('published', 1); + $capquiz->save(); + } } /** @@ -159,6 +161,7 @@ public static function publish_capquiz(capquiz $capquiz): void { * @param capquiz $capquiz */ public static function set_question_rating(capquiz $capquiz): void { + global $PAGE; $questionid = required_param('question-id', PARAM_INT); $question = $capquiz->question_list()->question($questionid); if (!$question) { @@ -168,7 +171,7 @@ public static function set_question_rating(capquiz $capquiz): void { if ($rating !== null) { $question->set_rating($rating, true); } - capquiz_urls::redirect_to_url(capquiz_urls::view_question_list_url()); + redirect(new moodle_url('/mod/capquiz/view.php', ['id' => $PAGE->cm->id, 'page' => 'questions'])); } /** @@ -177,11 +180,12 @@ public static function set_question_rating(capquiz $capquiz): void { * @param capquiz $capquiz */ public static function set_default_question_rating(capquiz $capquiz): void { + global $PAGE; $rating = optional_param('rating', null, PARAM_FLOAT); if ($rating !== null) { $capquiz->question_list()->set_default_question_rating($rating); } - capquiz_urls::redirect_to_url(capquiz_urls::view_question_list_url()); + redirect(new moodle_url('/mod/capquiz/view.php', ['id' => $PAGE->cm->id, 'page' => 'questions'])); } /** @@ -220,8 +224,8 @@ private static function create_capquiz_question(int $questionid, capquiz_questio $ratedquestion->question_list_id = $list->id(); $ratedquestion->question_id = $questionid; $ratedquestion->rating = $rating; - $capquizquestionid = $DB->insert_record('capquiz_question', $ratedquestion, true); - capquiz_question_rating::insert_question_rating_entry($capquizquestionid, $rating); + $capquizquestionid = $DB->insert_record('capquiz_question', $ratedquestion); + capquiz_question_rating::insert($capquizquestionid, $rating); } /** @@ -241,24 +245,24 @@ private static function remove_capquiz_question(int $questionid, int $qlistid): * @param capquiz $capquiz */ private static function merge_question_list(capquiz $capquiz): void { - global $DB; + global $DB, $PAGE; $srcqlistid = required_param('qlistid', PARAM_INT); $srcqlistrecord = $DB->get_record('capquiz_question_list', ['id' => $srcqlistid]); if ($srcqlistrecord) { $capquiz->question_list()->merge(new capquiz_question_list($srcqlistrecord)); } - capquiz_urls::redirect_to_url(capquiz_urls::view_question_list_url()); + redirect(new moodle_url('/mod/capquiz/view.php', ['id' => $PAGE->cm->id, 'page' => 'questions'])); } /** * Deletes the question list and its questions from the database */ private static function delete_question_list(): void { - global $DB; + global $DB, $PAGE; $srcqlistid = required_param('qlistid', PARAM_INT); $DB->delete_records('capquiz_question', ['question_list_id' => $srcqlistid]); $DB->delete_records('capquiz_question_list', ['id' => $srcqlistid]); - capquiz_urls::redirect_to_url(capquiz_urls::view_import_url()); + redirect(new moodle_url('/mod/capquiz/view.php', ['id' => $PAGE->cm->id, 'page' => 'import'])); } } diff --git a/classes/capquiz_matchmaking_strategy.php b/classes/capquiz_matchmaking_strategy.php deleted file mode 100755 index 8736d33..0000000 --- a/classes/capquiz_matchmaking_strategy.php +++ /dev/null @@ -1,67 +0,0 @@ -. - -/** - * This file defines a class that represents a capquiz matchmaking strategy - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz; - -use stdClass; - -/** - * Class capquiz_matchmaking_strategy - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -abstract class capquiz_matchmaking_strategy { - - /** - * Sets a new matchmaking strategy configuration - * - * @param stdClass $configuration - */ - abstract public function configure(stdClass $configuration): void; - - /** - * Returns the current configuration - */ - abstract public function configuration(): stdClass; - - /** - * Returns the default configuration - */ - abstract public function default_configuration(): stdClass; - - /** - * Returns a new question for the user based on the matchmaking strategy configuration - * - * @param capquiz_user $user - * @param capquiz_question_list $qlist - * @param array $inactiveattempts - */ - abstract public function next_question_for_user(capquiz_user $user, capquiz_question_list $qlist, - array $inactiveattempts): ?capquiz_question; - -} diff --git a/classes/capquiz_matchmaking_strategy_loader.php b/classes/capquiz_matchmaking_strategy_loader.php deleted file mode 100755 index ee1d8cf..0000000 --- a/classes/capquiz_matchmaking_strategy_loader.php +++ /dev/null @@ -1,227 +0,0 @@ -. - -/** - * This file defines a class used to load capquiz matchmaking strategies - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz; - -use moodle_url; -use stdClass; - -defined('MOODLE_INTERNAL') || die(); - -require_once($CFG->dirroot . '/mod/capquiz/classes/matchmaking/capquiz_matchmaking_strategy_registry.php'); -require_once($CFG->dirroot . '/mod/capquiz/classes/matchmaking/chronologic/chronologic_selector.php'); -require_once($CFG->dirroot . '/mod/capquiz/classes/matchmaking/n_closest/n_closest_selector.php'); -require_once($CFG->dirroot . '/mod/capquiz/classes/matchmaking/n_closest/n_closest_configuration_form.php'); - -/** - * Class capquiz_matchmaking_strategy_loader - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class capquiz_matchmaking_strategy_loader { - - /** @var capquiz $capquiz */ - private capquiz $capquiz; - - /** @var ?stdClass $record */ - private ?stdClass $record = null; - - /** @var capquiz_matchmaking_strategy_registry $registry */ - private capquiz_matchmaking_strategy_registry $registry; - - /** @var ?stdClass $configuration */ - private ?stdClass $configuration; - - /** - * Constructor. - * - * @param capquiz $capquiz - */ - public function __construct(capquiz $capquiz) { - $this->capquiz = $capquiz; - $this->registry = new capquiz_matchmaking_strategy_registry($capquiz); - $this->load_configuration(); - } - - /** - * Returns localized strategy name - * - * @param string $name - */ - public static function localized_strategy_name(string $name): string { - // TODO: This is a hack. The database records currently store the names, which makes localization hard. - return match ($name) { - 'N-closest' => get_string('n_closest', 'capquiz'), - 'Chronological' => get_string('chronological', 'capquiz'), - default => get_string('no_strategy_specified', 'capquiz'), - }; - } - - /** - * Returns the selected strategy - * - * @return ?capquiz_matchmaking_strategy - */ - public function selector(): ?capquiz_matchmaking_strategy { - if (!$this->record) { - return null; - } - $strategy = $this->registry->selector($this->record->strategy); - if ($this->configuration) { - $strategy->configure($this->configuration); - } - return $strategy; - } - - /** - * Returns configuration form for the current matchmaking strategy - * - * @param moodle_url $url - */ - public function configuration_form(moodle_url $url): mixed { - if ($this->record && $this->configuration) { - return $this->registry->configuration_form($this->record->strategy, $this->configuration, $url); - } - return null; - } - - /** - * Returns true if this instance has a strategy set - */ - public function has_strategy(): bool { - return $this->selector() !== null; - } - - /** - * Returns the name of the current strategy - */ - public function current_strategy_name(): string { - if ($this->record) { - return $this->record->strategy; - } - return get_string('no_strategy_specified', 'capquiz'); - } - - /** - * Configures teh current strategy - * - * @param stdClass $candidateconfig - */ - public function configure_current_strategy(stdClass $candidateconfig): void { - if (!$this->record) { - return; - } - $selector = $this->selector(); - $selector->configure($candidateconfig); - $config = $selector->configuration(); - $this->record->configuration = empty((array)$config) ? '' : $this->serialize($config); - $this->update_configuration($this->record); - } - - /** - * Sets the default strategy - */ - public function set_default_strategy(): void { - $this->set_strategy($this->registry->default_selection_strategy()); - } - - /** - * Sets strategy based on the strategy name - * - * @param string $strategy - */ - public function set_strategy(string $strategy): void { - $selector = $this->registry->selector($strategy); - $record = new stdClass; - $record->strategy = $strategy; - $record->capquiz_id = $this->capquiz->id(); - $defaultconfig = $selector->default_configuration(); - $record->configuration = empty((array)$defaultconfig) ? '' : $this->serialize($defaultconfig); - global $DB; - if ($this->record) { - $record->id = $this->record->id; - $this->update_configuration($record); - } else { - $DB->insert_record('capquiz_question_selection', $record); - $this->set_configuration($record); - } - } - - /** - * Loads the strategy configuration from the database - */ - private function load_configuration(): void { - global $DB; - $conditions = ['capquiz_id' => $this->capquiz->id()]; - $config = $DB->get_record('capquiz_question_selection', $conditions); - if ($config) { - $this->set_configuration($config); - } - } - - /** - * Updates the strategy configuration and updates the database record - * - * @param stdClass $config - */ - private function update_configuration(stdClass $config): void { - global $DB; - if ($DB->update_record('capquiz_question_selection', $config)) { - $this->set_configuration($config); - } - } - - /** - * Sets this configuration as a new configuration - * - * @param stdClass $record - */ - private function set_configuration(stdClass $record): void { - $this->record = $record; - $this->configuration = $this->deserialize($record->configuration) ?: null; - } - - /** - * Returns the current configuration as a JSON string - * - * @param stdClass $configuration - */ - private function serialize(stdClass $configuration): string { - return json_encode($configuration); - } - - /** - * Takes in JSON encoded configuration string and returns a decoded configuration - * - * @param string $configuration - */ - private function deserialize(string $configuration): mixed { - return json_decode($configuration, false); - } - -} diff --git a/classes/capquiz_question.php b/classes/capquiz_question.php index 39bb865..3ac02fa 100755 --- a/classes/capquiz_question.php +++ b/classes/capquiz_question.php @@ -26,140 +26,105 @@ namespace mod_capquiz; +use core\persistent; use stdClass; /** * Class capquiz_question * * @package mod_capquiz + * @author Sebastian Gundersen * @author Aleksander Skrede - * @author Sebastian S. Gundersen - * @copyright 2018 NTNU + * @copyright 2024 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class capquiz_question { +class capquiz_question extends persistent { - /** @var stdClass $record */ - private stdClass $record; + const TABLE = 'capquiz_question'; - /** @var capquiz_question_rating $rating */ - private capquiz_question_rating $rating; + /** @var ?capquiz_question_rating $rating */ + private ?capquiz_question_rating $rating = null; - /** - * Constructor. - * - * @param stdClass $record - */ - public function __construct(stdClass $record) { - global $DB; - $this->record = $record; - // TODO: This query should probably be done in question list. - $question = $DB->get_record('question', ['id' => $record->question_id]); - if ($question !== false) { - $this->record->name = $question->name; - $this->record->text = $question->questiontext; - } else { - $this->record->name = get_string('missing_question', 'capquiz'); - $this->record->text = $this->record->name; - } - $rating = capquiz_question_rating::latest_question_rating_by_question($record->id); - if ($rating === null) { - $this->rating = capquiz_question_rating::insert_question_rating_entry($this->id(), $this->rating()); - } else { - $this->rating = $rating; - } - } + /** @var ?stdClass $questionrecord */ + private ?stdClass $questionrecord = null; /** - * Loads a specific question from the database + * Return the definition of the properties of this model. * - * @param int $questionid + * @return array */ - public static function load(int $questionid): ?capquiz_question { - global $DB; - $record = $DB->get_record('capquiz_question', ['id' => $questionid]); - return empty($record) ? null : new capquiz_question($record); + protected static function define_properties(): array { + return [ + 'question_id' => [ + 'type' => PARAM_INT, + 'null' => NULL_NOT_ALLOWED, + ], + 'question_list_id' => [ + 'type' => PARAM_INT, + 'null' => NULL_NOT_ALLOWED, + ], + 'rating' => [ + 'type' => PARAM_FLOAT, + 'default' => 0.0, + 'null' => NULL_NOT_ALLOWED, + ], + ]; } /** - * Returns this questions database entry - */ - public function entry(): stdClass { - return $this->record; - } - - /** - * Returns this questions database entry id - */ - public function id(): int { - return $this->record->id; - } - - /** - * Returns this question's question bank question id - */ - public function question_id(): int { - return $this->record->question_id; - } - - /** - * Returns the id of the question list this question is in - */ - public function question_list_id(): int { - return $this->record->question_list_id; - } - - /** - * Returns this questions rating - */ - public function rating(): float { - return $this->record->rating; - } - - /** - * Returns this questions capquiz question rating + * Get current question rating. * * @return capquiz_question_rating */ public function get_capquiz_question_rating(): capquiz_question_rating { + if ($this->rating === null) { + $this->rating = capquiz_question_rating::latest_question_rating_by_question($this->get('id')); + if ($this->rating === null) { + $this->rating = capquiz_question_rating::insert($this->get('id'), $this->get('rating')); + } + } return $this->rating; } /** - * Sets this questions rating and capquiz question rating + * Get the core question record. * - * @param float $rating - * @param bool $manual + * @return ?stdClass */ - public function set_rating(float $rating, bool $manual = false) { + private function get_question_record(): ?stdClass { global $DB; - $this->record->rating = $rating; - $DB->update_record('capquiz_question', $this->record); - $questionrating = capquiz_question_rating::create_question_rating($this, $rating, $manual); - $this->rating = $questionrating; + if (!$this->questionrecord) { + $this->questionrecord = $DB->get_record('question', ['id' => $this->get('question_id')]); + } + return $this->questionrecord; } /** - * Returns this questions name + * Get the question name. + * + * @return string */ - public function name(): string { - return $this->record->name; + public function get_question_name(): string { + $question = $this->get_question_record(); + return $question !== null ? $question->name : get_string('missing_question', 'capquiz'); } /** - * Returns this questions text + * Sets this questions rating and capquiz question rating * - * @return string + * @param float $rating + * @param bool $manual */ - public function text(): string { - return $this->record->text; + public function set_rating(float $rating, bool $manual = false): void { + $this->set('rating', $rating); + $this->save(); + $this->rating = capquiz_question_rating::insert($this->get('id'), $rating, $manual); } /** * Returns the id of the course this question is in * * @return int - * @throws \dml_exception */ public function course_id(): int { global $DB; @@ -177,7 +142,7 @@ public function course_id(): int { JOIN {course} c ON (ctx.contextlevel = 50 AND c.id = ctx.instanceid) OR (ctx.contextlevel = 70 AND c.id = cm.course) WHERE cq.id = :questionid'; - $course = $DB->get_record_sql($sql, ['questionid' => $this->id()]); + $course = $DB->get_record_sql($sql, ['questionid' => $this->get('id')]); return $course ? $course->id : 0; } diff --git a/classes/capquiz_question_attempt.php b/classes/capquiz_question_attempt.php index 6db66f2..ea088f3 100755 --- a/classes/capquiz_question_attempt.php +++ b/classes/capquiz_question_attempt.php @@ -18,13 +18,15 @@ * This file defines a class represeting a capquiz question attempt * * @package mod_capquiz + * @author Sebastian Gundersen * @author Aleksander Skrede - * @copyright 2018 NTNU + * @copyright 2024 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace mod_capquiz; +use core\persistent; use question_bank; use question_engine; use question_state; @@ -36,26 +38,80 @@ * * @package mod_capquiz * @author Aleksander Skrede - * @copyright 2018 NTNU + * @copyright 2018 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class capquiz_question_attempt { +class capquiz_question_attempt extends persistent { - /** @var stdClass $record */ - private stdClass $record; + const TABLE = 'capquiz_attempt'; - /** @var question_usage_by_activity $quba */ - private question_usage_by_activity $quba; + /** @var ?question_usage_by_activity $quba */ + private ?question_usage_by_activity $quba = null; /** - * Constructor. + * Return the definition of the properties of this model. * - * @param question_usage_by_activity $quba - * @param stdClass $record + * @return array */ - public function __construct(question_usage_by_activity $quba, stdClass $record) { - $this->record = $record; - $this->quba = $quba; + protected static function define_properties(): array { + return [ + 'slot' => [ + 'type' => PARAM_INT, + 'null' => NULL_NOT_ALLOWED, + ], + 'user_id' => [ + 'type' => PARAM_INT, + 'null' => NULL_NOT_ALLOWED, + ], + 'question_id' => [ + 'type' => PARAM_INT, + 'null' => NULL_NOT_ALLOWED, + ], + 'reviewed' => [ + 'type' => PARAM_INT, + 'default' => 0, + 'null' => NULL_NOT_ALLOWED, + ], + 'answered' => [ + 'type' => PARAM_INT, + 'default' => 0, + 'null' => NULL_NOT_ALLOWED, + ], + 'time_answered' => [ + 'type' => PARAM_INT, + 'default' => 0, + 'null' => NULL_NOT_ALLOWED, + ], + 'time_reviewed' => [ + 'type' => PARAM_INT, + 'default' => 0, + 'null' => NULL_NOT_ALLOWED, + ], + 'question_rating_id' => [ + 'type' => PARAM_INT, + 'null' => NULL_ALLOWED, + ], + 'question_prev_rating_id' => [ + 'type' => PARAM_INT, + 'null' => NULL_ALLOWED, + ], + 'prev_question_rating_id' => [ + 'type' => PARAM_INT, + 'null' => NULL_ALLOWED, + ], + 'prev_question_prev_rating_id' => [ + 'type' => PARAM_INT, + 'null' => NULL_ALLOWED, + ], + 'user_rating_id' => [ + 'type' => PARAM_INT, + 'null' => NULL_ALLOWED, + ], + 'user_prev_rating_id' => [ + 'type' => PARAM_INT, + 'null' => NULL_ALLOWED, + ], + ]; } /** @@ -64,9 +120,9 @@ public function __construct(question_usage_by_activity $quba, stdClass $record) * @param capquiz_user $user * @param capquiz_question $question */ - public static function create_attempt(capquiz_user $user, capquiz_question $question): ?capquiz_question_attempt { + public static function create_attempt(capquiz_user $user, capquiz_question $question): ?self { $quba = $user->question_usage(); - $questions = question_load_questions([$question->question_id()]); + $questions = question_load_questions([$question->get('question_id')]); $targetquestion = reset($questions); if (!$targetquestion) { return null; @@ -75,7 +131,7 @@ public static function create_attempt(capquiz_user $user, capquiz_question $ques $slot = $quba->add_question($questiondefinition); $quba->start_question($slot); question_engine::save_questions_usage_by_activity($quba); - return self::insert_attempt_entry($user, $question, $slot); + return self::insert($user, $question, $slot); } /** @@ -85,11 +141,11 @@ public static function create_attempt(capquiz_user $user, capquiz_question $ques */ public static function active_attempt(capquiz_user $user): ?capquiz_question_attempt { global $DB; - $entry = $DB->get_record('capquiz_attempt', ['user_id' => $user->id(), 'reviewed' => false]); + $entry = $DB->get_record('capquiz_attempt', ['user_id' => $user->get('id'), 'reviewed' => 0]); if (empty($entry)) { return null; } - return new capquiz_question_attempt($user->question_usage(), $entry); + return new self($user->question_usage(), $entry); } /** @@ -98,13 +154,13 @@ public static function active_attempt(capquiz_user $user): ?capquiz_question_att * @param capquiz_user $user * @param int $attemptid */ - public static function load_attempt(capquiz_user $user, int $attemptid): ?capquiz_question_attempt { + public static function load_attempt(capquiz_user $user, int $attemptid): ?self { global $DB; - $entry = $DB->get_record('capquiz_attempt', ['id' => $attemptid, 'user_id' => $user->id()]); + $entry = $DB->get_record('capquiz_attempt', ['id' => $attemptid, 'user_id' => $user->get('id')]); if (empty($entry)) { return null; } - return new capquiz_question_attempt($user->question_usage(), $entry); + return new self($user->question_usage(), $entry); } /** @@ -112,15 +168,16 @@ public static function load_attempt(capquiz_user $user, int $attemptid): ?capqui * * @param capquiz_user $user */ - public static function previous_attempt(capquiz_user $user): ?capquiz_question_attempt { + public static function previous_attempt(capquiz_user $user): ?self { global $DB; $sql = 'SELECT * FROM {capquiz_attempt} WHERE user_id = :userid ORDER BY time_reviewed DESC LIMIT 1'; - $attempt = $DB->get_record_sql($sql, ['userid' => $user->id()], MUST_EXIST); - return new capquiz_question_attempt($user->question_usage(), $attempt); + //$DB->get_records('capquiz_attempt', ['user_id' => $user->get('id')], 'time_reviewed desc', '*', 0, 1); + $attempt = $DB->get_record_sql($sql, ['userid' => $user->get('id')], MUST_EXIST); + return new self($user->question_usage(), $attempt); } /** @@ -132,7 +189,7 @@ public static function previous_attempt(capquiz_user $user): ?capquiz_question_a public static function inactive_attempts(capquiz_user $user): array { global $DB; $records = $DB->get_records('capquiz_attempt', [ - 'user_id' => $user->id(), + 'user_id' => $user->get('id'), 'answered' => true, 'reviewed' => true, ]); @@ -141,42 +198,14 @@ public static function inactive_attempts(capquiz_user $user): array { }, array_values($records)); } - /** - * Returns the attempts id - */ - public function id(): int { - return $this->record->id; - } - - /** - * Returns the id of the question - */ - public function question_id(): int { - return $this->record->question_id; - } - - /** - * Returns the slot of the question - */ - public function question_slot(): int { - return $this->record->slot; - } - - /** - * Returns true if the attempt has an answer - */ - public function is_answered(): bool { - return $this->record->answered; - } - /** * Returns true if the answer is correct */ public function is_correctly_answered(): bool { - if (!$this->is_answered()) { + if (!$this->get('answered')) { return false; } - $moodleattempt = $this->quba->get_question_attempt($this->question_slot()); + $moodleattempt = $this->quba->get_question_attempt($this->get('slot')); return $moodleattempt->get_state()->is_correct(); } @@ -184,24 +213,10 @@ public function is_correctly_answered(): bool { * Returns the state of the question */ public function get_state(): question_state { - $moodleattempt = $this->quba->get_question_attempt($this->question_slot()); + $moodleattempt = $this->quba->get_question_attempt($this->get('slot')); return $moodleattempt->get_state(); } - /** - * Returns true if the attempt is reviewed - */ - public function is_reviewed(): bool { - return $this->record->reviewed; - } - - /** - * Returns true if the attempt is not reviewed - */ - public function is_pending(): bool { - return !$this->is_reviewed(); - } - /** * Checks if the question is valid */ @@ -212,40 +227,21 @@ public function is_question_valid(): bool { JOIN {capquiz_question} cq ON ca.question_id = cq.id WHERE ca.id = :attemptid'; - $result = $DB->get_record_sql($sql, ['attemptid' => $this->id()]); + $result = $DB->get_record_sql($sql, ['attemptid' => $this->get('id')]); return $result !== false; } - /** - * Deletes attempt from database - */ - public function delete(): void { - global $DB; - $DB->delete_records('capquiz_attempt', ['id' => $this->id()]); - } - /** * Marks attempt as answered */ public function mark_as_answered(): void { - global $DB; - $submitteddata = $this->quba->extract_responses($this->question_slot()); - $this->quba->process_action($this->question_slot(), $submitteddata); - $this->record->answered = true; - $this->record->time_answered = time(); - $this->quba->finish_question($this->question_slot(), time()); + $submitteddata = $this->quba->extract_responses($this->get('slot')); + $this->quba->process_action($this->get('slot'), $submitteddata); + $this->set('answered', 1); + $this->set('time_answered', time()); + $this->quba->finish_question($this->get('slot'), time()); question_engine::save_questions_usage_by_activity($this->quba); - $DB->update_record('capquiz_attempt', $this->record); - } - - /** - * Marks attempt as viewed - */ - public function mark_as_reviewed(): void { - global $DB; - $this->record->reviewed = true; - $this->record->time_reviewed = time(); - $DB->update_record('capquiz_attempt', $this->record); + $this->save(); } /** @@ -255,13 +251,12 @@ public function mark_as_reviewed(): void { * @param bool $previous */ public function set_question_rating(capquiz_question_rating $rating, bool $previous = false): void { - global $DB; if ($previous) { - $this->record->question_prev_rating_id = $rating->id(); + $this->set('question_prev_rating_id', $rating->get('id')); } else { - $this->record->question_rating_id = $rating->id(); + $this->set('question_rating_id', $rating->get('id')); } - $DB->update_record('capquiz_attempt', $this->record); + $this->save(); } /** @@ -271,13 +266,12 @@ public function set_question_rating(capquiz_question_rating $rating, bool $previ * @param bool $previous */ public function set_previous_question_rating(capquiz_question_rating $rating, bool $previous = false): void { - global $DB; if ($previous) { - $this->record->prev_question_prev_rating_id = $rating->id(); + $this->set('prev_question_prev_rating_id', $rating->get('id')); } else { - $this->record->prev_question_rating_id = $rating->id(); + $this->set('prev_question_rating_id', $rating->get('id')); } - $DB->update_record('capquiz_attempt', $this->record); + $this->save(); } /** @@ -287,13 +281,12 @@ public function set_previous_question_rating(capquiz_question_rating $rating, bo * @param bool $previous */ public function set_user_rating(capquiz_user_rating $rating, bool $previous = false): void { - global $DB; if ($previous) { - $this->record->user_prev_rating_id = $rating->id(); + $this->set('user_prev_rating_id', $rating->get('id')); } else { - $this->record->user_rating_id = $rating->id(); + $this->set('user_rating_id', $rating->get('id')); } - $DB->update_record('capquiz_attempt', $this->record); + $this->save(); } /** @@ -303,13 +296,12 @@ public function set_user_rating(capquiz_user_rating $rating, bool $previous = fa * @param capquiz_question $question * @param int $slot */ - private static function insert_attempt_entry(capquiz_user $user, capquiz_question $question, - int $slot): ?capquiz_question_attempt { + private static function insert(capquiz_user $user, capquiz_question $question, int $slot): ?self { global $DB; $record = new stdClass(); $record->slot = $slot; - $record->user_id = $user->id(); - $record->question_id = $question->id(); + $record->user_id = $user->get('id'); + $record->question_id = $question->get('id'); $DB->insert_record('capquiz_attempt', $record); return self::active_attempt($user); } diff --git a/classes/capquiz_question_engine.php b/classes/capquiz_question_engine.php index bbbbe91..16f8391 100755 --- a/classes/capquiz_question_engine.php +++ b/classes/capquiz_question_engine.php @@ -25,14 +25,12 @@ namespace mod_capquiz; -use question_usage_by_activity; - /** * Class capquiz_question_engine * * @package mod_capquiz * @author Aleksander Skrede - * @copyright 2018 NTNU + * @copyright 2018 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class capquiz_question_engine { @@ -40,31 +38,13 @@ class capquiz_question_engine { /** @var capquiz $capquiz */ private capquiz $capquiz; - /** @var question_usage_by_activity $quba */ - private question_usage_by_activity $quba; - - /** @var capquiz_matchmaking_strategy_loader $matchmakingloader */ - private capquiz_matchmaking_strategy_loader $matchmakingloader; - - /** @var capquiz_rating_system_loader $ratingsystemloader */ - private capquiz_rating_system_loader $ratingsystemloader; - /** * Constructor. * * @param capquiz $capquiz - * @param question_usage_by_activity $quba - * @param capquiz_matchmaking_strategy_loader $strategyloader - * @param capquiz_rating_system_loader $ratingsystemloader - */ - public function __construct(capquiz $capquiz, - question_usage_by_activity $quba, - capquiz_matchmaking_strategy_loader $strategyloader, - capquiz_rating_system_loader $ratingsystemloader) { + */ + public function __construct(capquiz $capquiz) { $this->capquiz = $capquiz; - $this->quba = $quba; - $this->matchmakingloader = $strategyloader; - $this->ratingsystemloader = $ratingsystemloader; } /** @@ -121,15 +101,14 @@ public function attempt_answered(capquiz_user $user, capquiz_question_attempt $a if (!$attempt->is_question_valid()) { return; } - $ratingsystem = $this->ratingsystemloader->rating_system(); $attempt->mark_as_answered(); $attempt->set_user_rating($user->get_capquiz_user_rating(), true); - $question = $this->capquiz->question_list()->question($attempt->question_id()); + $question = $this->capquiz->question_list()->question($attempt->get('question_id')); if ($attempt->is_correctly_answered()) { - $ratingsystem->update_user_rating($user, $question, 1); + elo_rating_system::update_user_rating($this->capquiz->record->userkfactor, $user, $question, 1); $this->set_new_highest_star_if_attained($user); } else { - $ratingsystem->update_user_rating($user, $question, 0); + elo_rating_system::update_user_rating($this->capquiz->record->userkfactor, $user, $question, 0); } $attempt->set_user_rating($user->get_capquiz_user_rating()); $previousattempt = capquiz_question_attempt::previous_attempt($user); @@ -147,8 +126,9 @@ private function set_new_highest_star_if_attained(capquiz_user $user): void { $qlist = $this->capquiz->question_list(); for ($star = $qlist->max_stars(); $star > 0; $star--) { $required = $qlist->star_rating($star); - if ($user->rating() >= $required && $user->highest_stars_achieved() < $star) { - $user->set_highest_star($star); + if ($user->get('rating') >= $required && $user->get('highest_level') < $star) { + $user->set('highest_level', $star); + $user->save(); break; } } @@ -160,7 +140,9 @@ private function set_new_highest_star_if_attained(capquiz_user $user): void { * @param capquiz_question_attempt $attempt */ public function attempt_reviewed(capquiz_question_attempt $attempt): void { - $attempt->mark_as_reviewed(); + $attempt->set('reviewed', 1); + $attempt->set('time_reviewed', time()); + $attempt->save(); } /** @@ -182,13 +164,105 @@ private function new_attempt_for_user(capquiz_user $user): ?capquiz_question_att * @param capquiz_user $user */ private function find_question_for_user(capquiz_user $user): ?capquiz_question { - $selector = $this->matchmakingloader->selector(); - if ($selector === null) { + $inactiveattempts = capquiz_question_attempt::inactive_attempts($user); + return match ($this->capquiz->record->questionselectiontype) { + 'nclosest' => $this->n_closest_next_question($user, $inactiveattempts), + 'chronological' => $this->chronologic_next_question($this->capquiz->question_list(), $inactiveattempts), + default => null, + }; + } + + /** + * Returns the next question for the user in a chronological order + * + * @param capquiz_question_list $qlist + * @param capquiz_question_attempt[] $inactiveattempts + */ + private function chronologic_next_question(capquiz_question_list $qlist, array $inactiveattempts): ?capquiz_question { + $answered = function (capquiz_question $q) use ($inactiveattempts) { + foreach ($inactiveattempts as $inactiveattempt) { + if ($inactiveattempt->get('question_id') === $q->get('id')) { + return true; + } + } + return false; + }; + foreach ($qlist->questions() as $question) { + if (!$answered($question)) { + return $question; + } + } + return null; + } + + /** + * Selects the next question for the user based on the configuration + * + * @param capquiz_user $user + * @param capquiz_question_attempt[] $inactiveattempts + */ + public function n_closest_next_question(capquiz_user $user, array $inactiveattempts): ?capquiz_question { + $excluded = $this->determine_excluded_questions($inactiveattempts); + $candidates = $this->find_questions_closest_to_rating($user, $excluded); + if (empty($candidates)) { return null; } - $questionlist = $this->capquiz->question_list(); - $inactiveattempts = capquiz_question_attempt::inactive_attempts($user); - return $selector->next_question_for_user($user, $questionlist, $inactiveattempts); + $index = mt_rand(0, count($candidates) - 1); + if ($question = $candidates[$index]) { + return $question; + } + return null; + } + + /** + * Returns the ideal question rating + * + * @param capquiz_user $user + */ + private function ideal_question_rating(capquiz_user $user): float { + return 400.0 * log((1.0 / $this->capquiz->record->userwinprobability) - 1.0, 10.0) + $user->get('rating'); + } + + /** + * Identifies questions to exclude and returns them in an array + * + * @param capquiz_question_attempt[] $inactiveattempts + */ + private function determine_excluded_questions(array $inactiveattempts): array { + $it = new \ArrayIterator(array_reverse($inactiveattempts, true)); + $excluded = []; + for ($i = 0; $i < $this->capquiz->record->preventsamequestionforturns; $i++) { + if (!$it->valid()) { + break; + } + $excluded[] = $it->current()->question_id(); + $it->next(); + } + return array_unique($excluded); + } + + /** + * Finds the questions closest to the users rating + * + * @param capquiz_user $user + * @param array $excludedquestions + */ + private function find_questions_closest_to_rating(capquiz_user $user, array $excludedquestions): array { + global $DB; + $sql = 'SELECT * FROM {capquiz_question} WHERE question_list_id = ?'; + $sql .= str_repeat(' AND id <> ?', count($excludedquestions)); + $sql .= ' ORDER BY ABS(? - rating)'; + $params = []; + $params[] = $this->capquiz->question_list()->id(); + if (count($excludedquestions) > 0) { + array_push($params, ...$excludedquestions); + } + $params[] = $this->ideal_question_rating($user); + $questions = []; + foreach ($DB->get_records_sql($sql, $params, 0, $this->capquiz->record->numquestionstoselect) as $questionentry) { + $questions[] = new capquiz_question($questionentry); + } + return $questions; } /** @@ -198,29 +272,27 @@ private function find_question_for_user(capquiz_user $user): ?capquiz_question { * @param capquiz_question_attempt $current */ private function update_question_rating(capquiz_question_attempt $previous, capquiz_question_attempt $current): void { - $ratingsystem = $this->ratingsystemloader->rating_system(); $currentcorrect = $current->is_correctly_answered(); $previouscorrect = $previous->is_correctly_answered(); $qlist = $this->capquiz->question_list(); - $currentquestion = $qlist->question($current->question_id()); + $currentquestion = $qlist->question($current->get('question_id')); if (!$currentquestion) { return; } - $previousquestion = $qlist->question($previous->question_id()); + $previousquestion = $qlist->question($previous->get('question_id')); if (!$previousquestion) { return; } $current->set_previous_question_rating($previousquestion->get_capquiz_question_rating(), true); $current->set_question_rating($currentquestion->get_capquiz_question_rating(), true); if ($previouscorrect && !$currentcorrect) { - $ratingsystem->question_victory_ratings($currentquestion, $previousquestion); + elo_rating_system::question_victory_ratings($this->capquiz->record->questionkfactor, $currentquestion, $previousquestion); } else if (!$previouscorrect && $currentcorrect) { - $ratingsystem->question_victory_ratings($previousquestion, $currentquestion); + elo_rating_system::question_victory_ratings($this->capquiz->record->questionkfactor, $previousquestion, $currentquestion); } else { - $previousquestion->set_rating($previousquestion->rating()); - $currentquestion->set_rating($currentquestion->rating()); + $previousquestion->set_rating($previousquestion->get('rating')); + $currentquestion->set_rating($currentquestion->get('rating')); } - $current->set_previous_question_rating($previousquestion->get_capquiz_question_rating()); $current->set_question_rating($currentquestion->get_capquiz_question_rating()); } diff --git a/classes/capquiz_question_list.php b/classes/capquiz_question_list.php index 20e2d43..6773fb1 100755 --- a/classes/capquiz_question_list.php +++ b/classes/capquiz_question_list.php @@ -19,8 +19,8 @@ * * @package mod_capquiz * @author Aleksander Skrede - * @author Sebastian S. Gundersen - * @copyright 2019 NTNU + * @author Sebastian Gundersen + * @copyright 2019 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -34,8 +34,8 @@ * * @package mod_capquiz * @author Aleksander Skrede - * @author Sebastian S. Gundersen - * @copyright 2019 NTNU + * @author Sebastian Gundersen + * @copyright 2019 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class capquiz_question_list { @@ -110,7 +110,7 @@ public function next_level_percent(capquiz $capquiz, float $rating): int { for ($star = 1; $star <= $this->max_stars(); $star++) { $goal = $this->star_rating($star); if ($goal > $rating) { - $previous = $star > 1 ? $this->star_rating($star - 1) : $capquiz->default_user_rating(); + $previous = $star > 1 ? $this->star_rating($star - 1) : $capquiz->get('default_user_rating'); $rating -= $previous; $goal -= $previous; break; @@ -217,7 +217,7 @@ public function questions(): array { */ public function question(int $questionid): ?capquiz_question { foreach ($this->questions as $question) { - if ($question->id() === $questionid) { + if ($question->get('id') === $questionid) { return $question; } } @@ -232,7 +232,7 @@ public function question(int $questionid): ?capquiz_question { */ public function has_question(int $questionid): mixed { foreach ($this->questions as $question) { - if ($question->question_id() === $questionid) { + if ($question->get('question_id') === $questionid) { return $question; } } @@ -247,13 +247,13 @@ public function has_question(int $questionid): mixed { public function merge(capquiz_question_list $that): void { global $DB; foreach ($that->questions as $question) { - if ($this->has_question($question->question_id()) === null) { + if ($this->has_question($question->get('question_id')) === null) { $newquestion = new stdClass(); $newquestion->question_list_id = $this->id(); - $newquestion->question_id = $question->question_id(); - $newquestion->rating = $question->rating(); - $capquizquestionid = $DB->insert_record('capquiz_question', $newquestion, true); - capquiz_question_rating::insert_question_rating_entry($capquizquestionid, $newquestion->rating); + $newquestion->question_id = $question->get('question_id'); + $newquestion->rating = $question->get('rating'); + $capquizquestionid = $DB->insert_record('capquiz_question', $newquestion); + capquiz_question_rating::insert($capquizquestionid, $newquestion->rating); } } } @@ -300,11 +300,11 @@ public function create_template_copy(capquiz $capquiz): ?capquiz_question_list { private function copy_questions_to_list(int $qlistid): void { global $DB; foreach ($this->questions() as $question) { - $record = $question->entry(); + $record = $question->to_record(); $record->id = null; $record->question_list_id = $qlistid; $capquizquestionid = $DB->insert_record('capquiz_question', $record); - capquiz_question_rating::insert_question_rating_entry($capquizquestionid, $record->rating); + capquiz_question_rating::insert($capquizquestionid, $record->rating); } } @@ -319,7 +319,7 @@ private function create_copy(capquiz $capquiz, bool $template): ?capquiz_questio global $DB; $record = $this->record; $record->id = null; - $record->capquiz_id = $template ? null : $capquiz->id(); + $record->capquiz_id = $template ? null : $capquiz->get('id'); $record->context_id = context_course::instance($capquiz->course()->id)->id; $record->is_template = $template; $record->time_created = time(); @@ -351,7 +351,7 @@ public static function create_new_instance(capquiz $capquiz, string $title, stri return null; } $record = new stdClass(); - $record->capquiz_id = $capquiz->id(); + $record->capquiz_id = $capquiz->get('id'); $record->title = $title; $record->description = $description; $record->star_ratings = implode(',', $ratings); @@ -361,12 +361,7 @@ public static function create_new_instance(capquiz $capquiz, string $title, stri $record->time_modified = time(); $record->context_id = context_course::instance($capquiz->course()->id)->id; $qlistid = $DB->insert_record('capquiz_question_list', $record); - $qlist = self::load_any($qlistid); - if (!$qlist) { - return null; - } - $capquiz->validate_matchmaking_and_rating_systems(); - return $qlist; + return self::load_any($qlistid); } /** @@ -376,7 +371,7 @@ public static function create_new_instance(capquiz $capquiz, string $title, stri */ public static function load_question_list(capquiz $capquiz): ?capquiz_question_list { global $DB; - $record = $DB->get_record('capquiz_question_list', ['capquiz_id' => $capquiz->id()]); + $record = $DB->get_record('capquiz_question_list', ['capquiz_id' => $capquiz->get('id')]); return $record ? new capquiz_question_list($record) : null; } diff --git a/classes/capquiz_question_rating.php b/classes/capquiz_question_rating.php index 80d2bca..c35829b 100755 --- a/classes/capquiz_question_rating.php +++ b/classes/capquiz_question_rating.php @@ -25,52 +25,44 @@ namespace mod_capquiz; -use dml_exception; +use core\persistent; use stdClass; /** * Class capquiz_question_rating * * @package mod_capquiz + * @author Sebastian Gundersen * @author André Storhaug - * @copyright 2019 NTNU + * @copyright 2024 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class capquiz_question_rating { +class capquiz_question_rating extends persistent { - /** @var stdClass $record */ - private stdClass $record; + const TABLE = 'capquiz_question_rating'; /** - * Constructor. + * Return the definition of the properties of this model. * - * @param stdClass $record + * @return array */ - public function __construct(stdClass $record) { - $this->record = $record; - } - - /** - * Loads a question rating from the database with a matching id - * - * @param int $questionratingid - */ - public static function load_question_rating(int $questionratingid): ?capquiz_question_rating { - global $DB; - $record = $DB->get_record('capquiz_question_rating', ['id' => $questionratingid]); - return empty($record) ? null : new capquiz_question_rating($record); - } - - /** - * Creates a new question rating and inserts it to the database - * - * @param capquiz_question $question - * @param float $rating - * @param bool $manual - */ - public static function create_question_rating(capquiz_question $question, float $rating, - bool $manual = false): capquiz_question_rating { - return self::insert_question_rating_entry($question->id(), $rating, $manual); + protected static function define_properties(): array { + return [ + 'capquiz_question_id' => [ + 'type' => PARAM_INT, + 'null' => NULL_NOT_ALLOWED, + ], + 'rating' => [ + 'type' => PARAM_FLOAT, + 'default' => 0.0, + 'null' => NULL_NOT_ALLOWED, + ], + 'manual' => [ + 'type' => PARAM_INT, + 'default' => 0, + 'null' => NULL_NOT_ALLOWED, + ], + ]; } /** @@ -80,17 +72,15 @@ public static function create_question_rating(capquiz_question $question, float * @param float $rating * @param bool $manual */ - public static function insert_question_rating_entry(int $questionid, float $rating, - bool $manual = false): capquiz_question_rating { + public static function insert(int $questionid, float $rating, bool $manual = false): self { global $DB; $record = new stdClass(); $record->capquiz_question_id = $questionid; $record->rating = $rating; $record->manual = $manual; $record->timecreated = time(); - $ratingid = $DB->insert_record('capquiz_question_rating', $record); - $record->id = $ratingid; - return new capquiz_question_rating($record); + $record->id = $DB->insert_record(self::TABLE, $record); + return new self(0, $record); } /** @@ -98,7 +88,7 @@ public static function insert_question_rating_entry(int $questionid, float $rati * * @param int $questionid */ - public static function latest_question_rating_by_question(int $questionid): ?capquiz_question_rating { + public static function latest_question_rating_by_question(int $questionid): ?self { global $DB; $sql = "SELECT cqr.* FROM {capquiz_question_rating} cqr @@ -111,38 +101,6 @@ public static function latest_question_rating_by_question(int $questionid): ?cap ) AND cq.id = :question_id"; $record = $DB->get_record_sql($sql, ['question_id' => $questionid]); - return empty($record) ? null : new capquiz_question_rating($record); - } - - /** - * Returns this question ratings id - */ - public function id(): int { - return $this->record->id; - } - - /** - * Returns the time of when the question rating was created - */ - public function timecreated(): string { - return $this->record->timecreated; - } - - /** - * Returns the question rating - */ - public function rating(): float { - return $this->record->rating; - } - - /** - * Sets the question rating - * - * @param float $rating - */ - public function set_rating(float $rating): void { - global $DB; - $this->record->rating = $rating; - $DB->update_record('capquiz_question_rating', $this->record); + return empty($record) ? null : new self(0, $record); } } diff --git a/classes/capquiz_rating_system.php b/classes/capquiz_rating_system.php deleted file mode 100755 index baad384..0000000 --- a/classes/capquiz_rating_system.php +++ /dev/null @@ -1,75 +0,0 @@ -. - -/** - * This file defines an abstract capquiz rating system - * - * @package mod_capquiz - * @author Sebastian S. Gundersen - * @author Aleksander Skrede - * @copyright 2019 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz; - -use stdClass; - -/** - * Class capquiz_rating_system - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -abstract class capquiz_rating_system { - - /** - * Function to configure a rating system - * - * @param stdClass $config - */ - abstract public function configure(stdClass $config): void; - - /** - * Function to get the rating system configuration - */ - abstract public function configuration(): stdClass; - - /** - * Function to get the default rating system configuration - */ - abstract public function default_configuration(): stdClass; - - /** - * Updates the users rating based on the rating system and its configuration - * - * @param capquiz_user $user - * @param capquiz_question $question - * @param float $score - */ - abstract public function update_user_rating(capquiz_user $user, capquiz_question $question, float $score): void; - - /** - * Updates the winning and losing questions ratings - * - * @param capquiz_question $winner - * @param capquiz_question $loser - */ - abstract public function question_victory_ratings(capquiz_question $winner, capquiz_question $loser): void; - -} diff --git a/classes/capquiz_rating_system_loader.php b/classes/capquiz_rating_system_loader.php deleted file mode 100755 index 881fe94..0000000 --- a/classes/capquiz_rating_system_loader.php +++ /dev/null @@ -1,213 +0,0 @@ -. - -/** - * This file defines a class used to load capquiz rating systems - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2019 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz; - -use moodle_url; -use stdClass; - -defined('MOODLE_INTERNAL') || die(); - -require_once($CFG->dirroot . '/mod/capquiz/classes/rating_system/capquiz_rating_system_registry.php'); -require_once($CFG->dirroot . '/mod/capquiz/classes/rating_system/elo_rating/elo_rating_system.php'); - -/** - * capquiz_rating_system_loader class - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class capquiz_rating_system_loader { - - /** @var capquiz $capquiz */ - private capquiz $capquiz; - - /** @var ?stdClass $record */ - private ?stdClass $record = null; - - /** @var capquiz_rating_system_registry $registry */ - private capquiz_rating_system_registry $registry; - - /** @var ?stdClass $configuration */ - private ?stdClass $configuration; - - /** - * Constructor. - * - * @param capquiz $capquiz - */ - public function __construct(capquiz $capquiz) { - $this->capquiz = $capquiz; - $this->registry = new capquiz_rating_system_registry(); - $this->load_configuration(); - } - - /** - * Returns rating system - */ - public function rating_system(): ?capquiz_rating_system { - if (!$this->record) { - return null; - } - $system = $this->registry->rating_system($this->record->rating_system); - if ($this->configuration) { - $system->configure($this->configuration); - } - return $system; - } - - /** - * Checks if this instance has a rating system - * - * @return bool - */ - public function has_rating_system(): bool { - return $this->rating_system() !== null; - } - - /** - * Returns configuration form - * - * @param moodle_url $url - */ - public function configuration_form(moodle_url $url): mixed { - if ($this->record && $this->configuration) { - return $this->registry->configuration_form($this->record->rating_system, $this->configuration, $url); - } - return null; - } - - /** - * Returns the current rating systems name - * - * @return string rating system name - */ - public function current_rating_system_name(): string { - if ($this->record) { - return $this->record->rating_system; - } - return 'No rating system specified'; - } - - /** - * Configure the current rating system. - * - * @param stdClass $candidateconfig - */ - public function configure_current_rating_system(stdClass $candidateconfig): void { - if (!$this->record) { - return; - } - $system = $this->rating_system(); - $system->configure($candidateconfig); - $config = $system->configuration(); - $this->record->configuration = empty((array)$config) ? '' : $this->serialize($config); - $this->update_configuration($this->record); - } - - /** - * Set the default rating system. - */ - public function set_default_rating_system(): void { - $this->set_rating_system($this->registry->default_rating_system()); - } - - /** - * Set the rating system - * - * @param string $ratingsystem - */ - public function set_rating_system(string $ratingsystem): void { - global $DB; - $system = $this->registry->rating_system($ratingsystem); - $record = new stdClass; - $record->rating_system = $ratingsystem; - $record->capquiz_id = $this->capquiz->id(); - $defaultconfig = $system->default_configuration(); - $record->configuration = empty((array)$defaultconfig) ? '' : $this->serialize($defaultconfig); - if ($this->record) { - $record->id = $this->record->id; - $this->update_configuration($record); - } else { - $DB->insert_record('capquiz_rating_system', $record); - $this->set_configuration($record); - } - } - - /** - * Loads this instances configuration - */ - private function load_configuration(): void { - global $DB; - $configuration = $DB->get_record('capquiz_rating_system', ['capquiz_id' => $this->capquiz->id()]); - if ($configuration) { - $this->set_configuration($configuration); - } - } - - /** - * Updates this instances configuration as well as updates the database - * - * @param stdClass $configuration - */ - private function update_configuration(stdClass $configuration): void { - global $DB; - if ($DB->update_record('capquiz_rating_system', $configuration)) { - $this->set_configuration($configuration); - } - } - - /** - * Sets this instances configuration - * - * @param stdClass $record - */ - private function set_configuration(stdClass $record): void { - $this->record = $record; - $this->configuration = $this->deserialize($record->configuration); - } - - /** - * Serializes the input configuration object - * - * @param stdClass $configuration the configuration to be serialized - * @return string json string representing the input configuration - */ - private function serialize(stdClass $configuration): string { - return json_encode($configuration); - } - - /** - * Deserializes JSON formatted configuration string - * - * @param string $configuration The JSON string to be deserialized back into a configuration object - */ - private function deserialize(string $configuration): mixed { - return json_decode($configuration, false); - } - -} diff --git a/classes/capquiz_urls.php b/classes/capquiz_urls.php index 85e100d..4735736 100755 --- a/classes/capquiz_urls.php +++ b/classes/capquiz_urls.php @@ -19,9 +19,9 @@ * * @package mod_capquiz * @author Aleksander Skrede - * @author Sebastian S. Gundersen + * @author Sebastian Gundersen * @author André Storhaug - * @copyright 2019 NTNU + * @copyright 2019 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -38,66 +38,20 @@ * * @package mod_capquiz * @author Aleksander Skrede - * @author Sebastian S. Gundersen + * @author Sebastian Gundersen * @author André Storhaug - * @copyright 2019 NTNU + * @copyright 2019 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class capquiz_urls { - /** @var string The URL to the entrypoint view for the capquiz */ - public static string $urlview = '/mod/capquiz/view.php'; - - /** @var string The URL to update the user attempts and return to the dashboard */ - public static string $urlasync = '/mod/capquiz/async.php'; - - /** @var string The URL to the error page */ - public static string $urlerror = '/mod/capquiz/error.php'; - - /** @var string The URL to the action page */ - public static string $urlaction = '/mod/capquiz/action.php'; - - /** @var string The URL to the classlist view */ - public static string $urlviewclasslist = '/mod/capquiz/view_classlist.php'; - - /** @var string The URL to the grading view */ - public static string $urlviewgrading = '/mod/capquiz/view_grading.php'; - - /** @var string The URL to the import view */ - public static string $urlviewimport = '/mod/capquiz/view_import.php'; - - /** @var string The URL to the report view */ - public static string $urlviewreport = '/mod/capquiz/view_report.php'; - - /** @var string The URL for the capquiz editor */ - public static string $urledit = '/mod/capquiz/edit.php'; - - /** @var string The URL to the create question list view */ - public static string $urlviewcreateqlist = '/mod/capquiz/view_create_question_list.php'; - - /** @var string The URL to the rating system view */ - public static string $urlviewratingsystemconfig = '/mod/capquiz/view_rating_system.php'; - - /** - * Returns a redirect url - * - * @param moodle_url $target - */ - public static function redirect(moodle_url $target): moodle_url { - $url = self::create_view_url(self::$urlaction); - $url->param('action', 'redirect'); - $url->param('target-url', $target->out_as_local_url()); - return $url; - } - /** * Generates a url based on a relative url * * @param string $relativeurl */ public static function create_view_url(string $relativeurl): moodle_url { - global $CFG; - $url = new moodle_url($CFG->wwwroot . $relativeurl); + $url = new moodle_url($relativeurl); $url->param('id', self::require_course_module_id_param()); return $url; } @@ -105,147 +59,17 @@ public static function create_view_url(string $relativeurl): moodle_url { /** * Returns the course module id */ - public static function require_course_module_id_param(): int { + private static function require_course_module_id_param(): int { $id = optional_param('id', 0, PARAM_INT); if ($id !== 0) { return $id; } - return required_param('cmid', PARAM_INT); - } - - /** - * Redirects to the front page - */ - public static function redirect_to_front_page(): void { - global $CFG; - redirect(new moodle_url($CFG->wwwroot)); - } - - /** - * Redirects to the dashboard - */ - public static function redirect_to_dashboard(): void { - self::redirect_to_url(self::create_view_url(self::$urlview)); - } - - /** - * Redirects to specified url - * - * @param moodle_url $url - */ - public static function redirect_to_url(moodle_url $url): void { - redirect($url); - } - - /** - * Redirects to the previous page - */ - public static function redirect_to_previous(): void { - header('Location: ' . $_SERVER['HTTP_REFERER']); - exit; - } - - /** - * Sets teh current page url - * - * @param capquiz $capquiz - * @param string $url - */ - public static function set_page_url(capquiz $capquiz, string $url): void { - global $PAGE; - $PAGE->set_context($capquiz->context()); - $PAGE->set_cm($capquiz->course_module()); - $PAGE->set_pagelayout('incourse'); - $PAGE->set_url(self::create_view_url($url)); - } - - /** - * Returns url to the front page of the capquiz dashboard - */ - public static function view_url(): moodle_url { - return self::create_view_url(self::$urlview); - } - - /** - * Returns the url to the question list view - * - * @param int $questionpage - */ - public static function view_question_list_url(int $questionpage = 0): moodle_url { - $url = self::create_view_url(self::$urledit); - $url->param('qpage', $questionpage); - return $url; - } - - /** - * Returns the url to the rating system view - */ - public static function view_rating_system_url(): moodle_url { - return self::create_view_url(self::$urlviewratingsystemconfig); - } - - /** - * Returns the url to the grading view - */ - public static function view_grading_url(): moodle_url { - return self::create_view_url(self::$urlviewgrading); - } - - /** - * Returns the url to the classlist/leaderboard view - */ - public static function view_classlist_url(): moodle_url { - return self::create_view_url(self::$urlviewclasslist); - } - - /** - * Returns url to the "create question list" view - */ - public static function view_create_question_list_url(): moodle_url { - return self::create_view_url(self::$urlviewcreateqlist); - } - - /** - * Returns url to the import view - */ - public static function view_import_url(): moodle_url { - return self::create_view_url(self::$urlviewimport); - } - - /** - * Returns url to the report view - * - * @param string $mode - */ - public static function view_report_url(string $mode = ''): moodle_url { - return self::report_url(self::$urlviewreport, $mode); - } - - /** - * Generates and returns url to the report view - * - * @param string $relativeurl - * @param string $mode - */ - public static function report_url(string $relativeurl, string $mode): moodle_url { - $url = self::create_view_url($relativeurl); - if ($mode !== '') { - $url->param('mode', $mode); + $id = optional_param('cmid', 0, PARAM_INT); + if ($id !== 0) { + return $id; } - return $url; - } - - /** - * Generates and returns url to add a qyestion to the list with - * the parameters to add question to the list - * - * @param int $questionid - */ - public static function add_question_to_list_url(int $questionid): moodle_url { - $url = self::create_view_url(self::$urlaction); - $url->param('action', 'add-question'); - $url->param('question-id', $questionid); - return $url; + // For when we are on course/modedit.php + return required_param('update', PARAM_INT); } /** @@ -255,7 +79,7 @@ public static function add_question_to_list_url(int $questionid): moodle_url { * @param int $questionid */ public static function remove_question_from_list_url(int $questionid): moodle_url { - $url = self::create_view_url(self::$urlaction); + $url = self::create_view_url('/mod/capquiz/action.php'); $url->param('action', 'remove-question'); $url->param('question-id', $questionid); return $url; @@ -268,7 +92,7 @@ public static function remove_question_from_list_url(int $questionid): moodle_ur * @param capquiz_question_list $qlist */ public static function question_list_publish_url(capquiz_question_list $qlist): moodle_url { - $url = self::create_view_url(self::$urlaction); + $url = self::create_view_url('/mod/capquiz/action.php'); $url->param('action', 'publish-question-list'); $url->param('question-list-id', $qlist->id()); return $url; @@ -281,7 +105,7 @@ public static function question_list_publish_url(capquiz_question_list $qlist): * @param capquiz_question_list $qlist */ public static function question_list_create_template_url(capquiz_question_list $qlist): moodle_url { - $url = self::create_view_url(self::$urlaction); + $url = self::create_view_url('/mod/capquiz/action.php'); $url->param('action', 'create-question-list-template'); $url->param('question-list-id', $qlist->id()); return $url; @@ -294,7 +118,7 @@ public static function question_list_create_template_url(capquiz_question_list $ * @param capquiz_question_list $qlist */ public static function question_list_select_url(capquiz_question_list $qlist): moodle_url { - $url = self::create_view_url(self::$urlaction); + $url = self::create_view_url('/mod/capquiz/action.php'); $url->param('action', 'set-question-list'); $url->param('question-list-id', $qlist->id()); return $url; @@ -307,7 +131,7 @@ public static function question_list_select_url(capquiz_question_list $qlist): m * @param int $questionid */ public static function set_question_rating_url(int $questionid): moodle_url { - $url = self::create_view_url(self::$urlaction); + $url = self::create_view_url('/mod/capquiz/action.php'); $url->param('action', 'set-question-rating'); $url->param('question-id', $questionid); return $url; @@ -317,7 +141,7 @@ public static function set_question_rating_url(int $questionid): moodle_url { * Generates and returns url to regrade all */ public static function regrade_all_url(): moodle_url { - $url = self::create_view_url(self::$urlaction); + $url = self::create_view_url('/mod/capquiz/action.php'); $url->param('action', 'regrade-all'); return $url; } @@ -328,7 +152,7 @@ public static function regrade_all_url(): moodle_url { * @param int $qlistid */ public static function merge_qlist(int $qlistid): moodle_url { - $url = self::create_view_url(self::$urlaction); + $url = self::create_view_url('/mod/capquiz/action.php'); $url->param('action', 'merge_qlist'); $url->param('qlistid', $qlistid); return $url; @@ -340,7 +164,7 @@ public static function merge_qlist(int $qlistid): moodle_url { * @param int $qlistid */ public static function delete_qlist(int $qlistid): moodle_url { - $url = self::create_view_url(self::$urlaction); + $url = self::create_view_url('/mod/capquiz/action.php'); $url->param('action', 'delete_qlist'); $url->param('qlistid', $qlistid); return $url; @@ -352,9 +176,9 @@ public static function delete_qlist(int $qlistid): moodle_url { * @param capquiz_question_attempt $attempt */ public static function response_submit_url(capquiz_question_attempt $attempt): moodle_url { - $url = self::create_view_url(self::$urlasync); + $url = self::create_view_url('/mod/capquiz/async.php'); $url->param('action', 'answered'); - $url->param('attempt', $attempt->id()); + $url->param('attempt', $attempt->get('id')); return $url; } @@ -364,9 +188,9 @@ public static function response_submit_url(capquiz_question_attempt $attempt): m * @param capquiz_question_attempt $attempt */ public static function response_reviewed_url(capquiz_question_attempt $attempt): moodle_url { - $url = self::create_view_url(self::$urlasync); + $url = self::create_view_url('/mod/capquiz/async.php'); $url->param('action', 'reviewed'); - $url->param('attempt', $attempt->id()); + $url->param('attempt', $attempt->get('id')); return $url; } } diff --git a/classes/capquiz_user.php b/classes/capquiz_user.php index 043aa32..585517f 100755 --- a/classes/capquiz_user.php +++ b/classes/capquiz_user.php @@ -19,14 +19,15 @@ * * @package mod_capquiz * @author Aleksander Skrede - * @author Sebastian S. Gundersen - * @copyright 2019 NTNU + * @author Sebastian Gundersen + * @copyright 2019 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ namespace mod_capquiz; use context_module; +use core\persistent; use question_engine; use question_usage_by_activity; use stdClass; @@ -36,14 +37,13 @@ * * @package mod_capquiz * @author Aleksander Skrede - * @author Sebastian S. Gundersen - * @copyright 2019 NTNU + * @author Sebastian Gundersen + * @copyright 2019 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class capquiz_user { +class capquiz_user extends persistent { - /** @var stdClass $record */ - private stdClass $record; + const TABLE = 'capquiz_user'; /** @var stdClass $user */ private stdClass $user; @@ -57,50 +57,68 @@ class capquiz_user { /** * Constructor. * - * @param stdClass $record * @param context_module $context + * @param int $id + * @param ?stdClass $record */ - public function __construct(stdClass $record, context_module $context) { + public function __construct(context_module $context, int $id = 0, ?stdClass $record = null) { global $DB; - $this->record = $record; - $this->user = $DB->get_record('user', ['id' => $this->record->user_id]); - - $rating = capquiz_user_rating::latest_user_rating_by_user($record->id); + parent::__construct($id, $record); + $this->user = $DB->get_record('user', ['id' => $this->get('user_id')]); + $rating = capquiz_user_rating::latest_user_rating_by_user($this->get('id')); if ($rating === null) { - $this->rating = capquiz_user_rating::insert_user_rating_entry($this->id(), $this->rating()); + $this->rating = capquiz_user_rating::insert($this->get('id'), $this->get('rating')); } else { $this->rating = $rating; } - $this->create_question_usage($context); - $this->quba = question_engine::load_questions_usage_by_activity($this->record->question_usage_id); - } - - /** - * Verify if user has permission to use question - */ - private function has_question_usage(): bool { - return $this->record->question_usage_id !== null; + if ($this->get('question_usage_id') === null) { + $quba = question_engine::make_questions_usage_by_activity('mod_capquiz', $context); + $quba->set_preferred_behaviour('immediatefeedback'); + // TODO: Don't suppress the error if it becomes possible to save QUBAs without slots. + @question_engine::save_questions_usage_by_activity($quba); + $this->set('question_usage_id', $quba->get_id()); + $this->save(); + } + $this->quba = question_engine::load_questions_usage_by_activity($this->get('question_usage_id')); } /** - * Create question usage + * Return the definition of the properties of this model. * - * @param context_module $context - */ - public function create_question_usage(context_module $context): void { - global $DB; - if ($this->has_question_usage()) { - return; - } - $quba = question_engine::make_questions_usage_by_activity('mod_capquiz', $context); - $quba->set_preferred_behaviour('immediatefeedback'); - // TODO: Don't suppress the error if it becomes possible to save QUBAs without slots. - @question_engine::save_questions_usage_by_activity($quba); - $this->record->question_usage_id = $quba->get_id(); - $DB->update_record('capquiz_user', $this->record); + * @return array + */ + protected static function define_properties(): array { + return [ + 'user_id' => [ + 'type' => PARAM_INT, + 'null' => NULL_NOT_ALLOWED, + ], + 'capquiz_id' => [ + 'type' => PARAM_INT, + 'null' => NULL_NOT_ALLOWED, + ], + 'question_usage_id' => [ + 'type' => PARAM_INT, + 'null' => NULL_ALLOWED, + ], + 'rating' => [ + 'type' => PARAM_FLOAT, + 'default' => 0.0, + 'null' => NULL_NOT_ALLOWED, + ], + 'highest_level' => [ + 'type' => PARAM_INT, + 'default' => 0, + 'null' => NULL_NOT_ALLOWED, + ], + 'stars_graded' => [ + 'type' => PARAM_INT, + 'default' => 0, + 'null' => NULL_NOT_ALLOWED, + ], + ]; } - /** * Return this user's quba. */ @@ -122,10 +140,10 @@ public static function load_user(capquiz $capquiz, int $moodleuserid, context_mo } $record = new stdClass(); $record->user_id = $moodleuserid; - $record->capquiz_id = $capquiz->id(); - $record->rating = $capquiz->default_user_rating(); + $record->capquiz_id = $capquiz->get('id'); + $record->rating = $capquiz->get('default_user_rating'); $capquizuserid = $DB->insert_record('capquiz_user', $record); - capquiz_user_rating::insert_user_rating_entry($capquizuserid, $record->rating); + capquiz_user_rating::insert($capquizuserid, $record->rating); return self::load_db_entry($capquiz, $moodleuserid, $context); } @@ -150,14 +168,7 @@ public static function user_count(int $capquizid): int { public static function list_users(int $capquizid, context_module $context): array { global $DB; $records = $DB->get_records('capquiz_user', ['capquiz_id' => $capquizid]); - return array_map(fn(stdClass $record) => new capquiz_user($record, $context), array_values($records)); - } - - /** - * Return this user's id - */ - public function id(): int { - return $this->record->id; + return array_map(fn(stdClass $record) => new capquiz_user($context, 0, $record), array_values($records)); } /** @@ -181,13 +192,6 @@ public function last_name(): string { return $this->user->lastname; } - /** - * Return users rating - */ - public function rating(): float { - return $this->record->rating; - } - /** * Get this user's capquiz rating */ @@ -195,45 +199,6 @@ public function get_capquiz_user_rating(): capquiz_user_rating { return $this->rating; } - /** - * Return the highest star rating this user has achieved - */ - public function highest_stars_achieved(): int { - return $this->record->highest_level; - } - - /** - * Return the highest star grade - */ - public function highest_stars_graded(): int { - return $this->record->stars_graded; - } - - /** - * Set this user's highest star rating - * - * @param int $higheststar - */ - public function set_highest_star(int $higheststar): void { - global $DB; - $this->record->highest_level = $higheststar; - $DB->update_record('capquiz_user', $this->record); - } - - /** - * Set this user's rating - * - * @param float $rating - * @param bool $manual - */ - public function set_rating(float $rating, bool $manual = false): void { - global $DB; - $this->record->rating = $rating; - $DB->update_record('capquiz_user', $this->record); - $userrating = capquiz_user_rating::create_user_rating($this, $rating, $manual); - $this->rating = $userrating; - } - /** * Load user entry from database * @@ -241,13 +206,13 @@ public function set_rating(float $rating, bool $manual = false): void { * @param int $moodleuserid * @param context_module $context */ - private static function load_db_entry(capquiz $capquiz, int $moodleuserid, context_module $context): ?capquiz_user { + private static function load_db_entry(capquiz $capquiz, int $moodleuserid, context_module $context): ?self { global $DB; $entry = $DB->get_record('capquiz_user', [ 'user_id' => $moodleuserid, - 'capquiz_id' => $capquiz->id(), + 'capquiz_id' => $capquiz->get('id'), ]); - return empty($entry) ? null : new capquiz_user($entry, $context); + return empty($entry) ? null : new self($context, 0, $entry); } } diff --git a/classes/capquiz_user_rating.php b/classes/capquiz_user_rating.php index 5bfcd1f..4dfd133 100755 --- a/classes/capquiz_user_rating.php +++ b/classes/capquiz_user_rating.php @@ -25,53 +25,44 @@ namespace mod_capquiz; +use core\persistent; use stdClass; /** * Class capquiz_user_rating * * @package mod_capquiz + * @author Sebastian Gundersen * @author André Storhaug - * @copyright 2019 NTNU + * @copyright 2024 NTNU * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -class capquiz_user_rating { - - /** @var stdClass $record */ - private stdClass $record; +class capquiz_user_rating extends persistent { - /** - * Constructor. - * - * @param stdClass $record - */ - public function __construct(stdClass $record) { - $this->record = $record; - } + const TABLE = 'capquiz_user_rating'; /** - * Loads and returns user rating from database + * Return the definition of the properties of this model. * - * @param int $questionratingid + * @return array */ - public static function load_user_rating(int $questionratingid): ?capquiz_user_rating { - global $DB; - $record = $DB->get_record('capquiz_question_rating', ['id' => $questionratingid]); - if ($record === false) { - return null; - } - return new capquiz_user_rating($record); - } - - /** - * Creates and inserts a new user rating to the database - * - * @param capquiz_user $user - * @param float $rating - * @param bool $manual - */ - public static function create_user_rating(capquiz_user $user, float $rating, bool $manual = false): ?capquiz_user_rating { - return self::insert_user_rating_entry($user->id(), $rating, $manual); + protected static function define_properties(): array { + return [ + 'capquiz_user_id' => [ + 'type' => PARAM_INT, + 'null' => NULL_NOT_ALLOWED, + ], + 'rating' => [ + 'type' => PARAM_FLOAT, + 'default' => 0.0, + 'null' => NULL_NOT_ALLOWED, + ], + 'manual' => [ + 'type' => PARAM_INT, + 'default' => 0, + 'null' => NULL_NOT_ALLOWED, + ], + ]; } /** @@ -79,7 +70,7 @@ public static function create_user_rating(capquiz_user $user, float $rating, boo * * @param int $capquizuserid */ - public static function latest_user_rating_by_user(int $capquizuserid): ?capquiz_user_rating { + public static function latest_user_rating_by_user(int $capquizuserid): ?self { global $DB; $sql = "SELECT cur.* FROM {capquiz_user_rating} cur @@ -92,8 +83,7 @@ public static function latest_user_rating_by_user(int $capquizuserid): ?capquiz_ ) AND cu.id = :capquiz_user_id"; $record = $DB->get_record_sql($sql, ['capquiz_user_id' => $capquizuserid]); - - return $record ? new capquiz_user_rating($record) : null; + return empty($record) ? null : new self(0, $record); } /** @@ -103,7 +93,7 @@ public static function latest_user_rating_by_user(int $capquizuserid): ?capquiz_ * @param float $rating * @param bool $manual */ - public static function insert_user_rating_entry(int $capquizuserid, float $rating, bool $manual = false): capquiz_user_rating { + public static function insert(int $capquizuserid, float $rating, bool $manual = false): self { global $DB, $USER; $record = new stdClass(); $record->capquiz_user_id = $capquizuserid; @@ -112,31 +102,6 @@ public static function insert_user_rating_entry(int $capquizuserid, float $ratin $record->timecreated = time(); $record->user_id = $USER->id; $record->id = $DB->insert_record('capquiz_user_rating', $record); - return new capquiz_user_rating($record); - } - - /** - * Returns this user ratings id - */ - public function id(): int { - return $this->record->id; - } - - /** - * Returns this user ratings rating - */ - public function rating(): float { - return $this->record->rating; - } - - /** - * Sets this user ratings rating and updates the database record - * - * @param float $rating - */ - public function set_rating(float $rating): void { - global $DB; - $this->record->rating = $rating; - $DB->update_record('capquiz_user_rating', $this->record); + return new self(0, $record); } } diff --git a/classes/elo_rating_system.php b/classes/elo_rating_system.php new file mode 100644 index 0000000..eb049d3 --- /dev/null +++ b/classes/elo_rating_system.php @@ -0,0 +1,79 @@ +. + +/** + * This file defines a class used as a registry for the rating system + * + * @package mod_capquiz + * @author Aleksander Skrede + * @copyright 2018 Norwegian University of Science and Technology (NTNU) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + +namespace mod_capquiz; + +/** + * Class elo_rating_system + * + * @package mod_capquiz + * @author Aleksander Skrede + * @copyright 2018 Norwegian University of Science and Technology (NTNU) + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class elo_rating_system { + /** + * Updates the user's rating + * + * @param float $userkfactor + * @param capquiz_user $user + * @param capquiz_question $question + * @param float $score + */ + public static function update_user_rating(float $userkfactor, capquiz_user $user, capquiz_question $question, float $score): void { + $currentrating = $user->get('rating'); + $newrating = $currentrating + $userkfactor * ($score - self::expected_result($currentrating, $question->get('rating'))); + $user->set('rating', $newrating); + $user->save(); + capquiz_user_rating::insert($user->get('id'), $newrating); + } + + /** + * Updates the winning and losing question ratings + * + * @param float $questionkfactor + * @param capquiz_question $winner + * @param capquiz_question $loser + */ + public static function question_victory_ratings(float $questionkfactor, capquiz_question $winner, capquiz_question $loser): void { + $loserating = $loser->get('rating'); + $winrating = $winner->get('rating'); + $newloserating = $loserating + $questionkfactor * (0 - self::expected_result($loserating, $winrating)); + $newwinrating = $winrating + $questionkfactor * (1 - self::expected_result($winrating, $loserating)); + $loser->set_rating($newloserating); + $winner->set_rating($newwinrating); + } + + /** + * Calculates the expected score in favor of the player with rating $a, against a player with rating $b + * + * @param float $a + * @param float $b + */ + private static function expected_result(float $a, float $b): float { + $exponent = ($b - $a) / 400.0; + return 1.0 / (1.0 + pow(10.0, $exponent)); + } +} diff --git a/classes/form/view/grading_configuration_form.php b/classes/form/view/grading_configuration_form.php deleted file mode 100644 index 65852fe..0000000 --- a/classes/form/view/grading_configuration_form.php +++ /dev/null @@ -1,121 +0,0 @@ -. - -/** - * CAPQuiz grading configuration form definition. - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz\form\view; - -use mod_capquiz\capquiz; -use moodle_url; - -defined('MOODLE_INTERNAL') || die(); - -require_once($CFG->libdir . '/formslib.php'); - -/** - * grading_configuration_form class - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class grading_configuration_form extends \moodleform { - - /** @var capquiz $capquiz */ - private capquiz $capquiz; - - /** - * Constructor. - * - * @param capquiz $capquiz - * @param moodle_url $url - */ - public function __construct(capquiz $capquiz, moodle_url $url) { - $this->capquiz = $capquiz; - parent::__construct($url); - } - - /** - * Defines form - */ - public function definition(): void { - $qlist = $this->capquiz->question_list(); - $form = $this->_form; - $form->addElement('text', 'default_user_rating', get_string('default_user_rating', 'capquiz')); - $form->setType('default_user_rating', PARAM_INT); - $form->setDefault('default_user_rating', $this->capquiz->default_user_rating()); - $form->addRule('default_user_rating', get_string('default_user_rating_required', 'capquiz'), 'required', null, 'client'); - for ($star = 1; $star <= $qlist->max_stars(); $star++) { - $groupname = "star_group_$star"; - $input = "star_rating_$star"; - $text = get_string('level_rating', 'capquiz', $star); - $elements = []; - $elements[] = $form->createElement('text', $input, $text); - if ($star > 1) { - $elements[] = $form->createElement('submit', "delstarbutton$star", get_string('delete_star', 'capquiz')); - } - $form->addGroup($elements, $groupname, $text, [''], false); - $form->setType($input, PARAM_INT); - $form->setDefault($input, $qlist->star_rating($star)); - } - - $form->addElement('submit', 'addstarbutton', get_string('add_star', 'capquiz')); - - $strstarstopass = get_string('stars_to_pass', 'capquiz'); - $strstarstopassrequired = get_string('stars_to_pass_required', 'capquiz'); - $form->addElement('text', 'starstopass', $strstarstopass); - $form->setType('starstopass', PARAM_INT); - $form->setDefault('starstopass', $this->capquiz->stars_to_pass()); - $form->addRule('starstopass', $strstarstopassrequired, 'required', null, 'client'); - - $strduedate = get_string('due_time_grading', 'capquiz'); - $form->addElement('date_time_selector', 'timedue', $strduedate); - $form->setType('timedue', PARAM_INT); - $timedue = $this->capquiz->time_due(); - $oneweek = 60 * 60 * 24 * 7; - $form->setDefault('timedue', $timedue ? $timedue : time() + $oneweek); - - $form->addElement('submit', 'submitbutton', get_string('savechanges')); - } - - /** - * Validate the data from the form. - * - * @param array $data array of ("fieldname"=>value) of submitted data - * @param array $files array of uploaded files "element_name"=>tmp_file_path - * @return array of "element_name"=>"error_description" if there are errors, - * or an empty array if everything is OK (true allowed for backwards compatibility too). - */ - public function validations($data, $files): array { - $errors = []; - if (empty($data['default_user_rating'])) { - $errors['default_user_rating'] = get_string('default_user_rating_required', 'capquiz'); - } - if (empty($data['starstopass']) || $data['starstopass'] < 0 || $data['starstopass'] > 5) { - $errors['starstopass'] = get_string('stars_to_pass_required', 'capquiz'); - } - return $errors; - } - -} diff --git a/classes/form/view/matchmaking_strategy_selection_form.php b/classes/form/view/matchmaking_strategy_selection_form.php deleted file mode 100755 index 5b25326..0000000 --- a/classes/form/view/matchmaking_strategy_selection_form.php +++ /dev/null @@ -1,98 +0,0 @@ -. - -/** - * CAPQuiz matchmaking strategy selection form definition. - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz\form\view; - -use mod_capquiz\capquiz; -use mod_capquiz\capquiz_matchmaking_strategy_loader; -use mod_capquiz\capquiz_matchmaking_strategy_registry; -use moodle_url; - -defined('MOODLE_INTERNAL') || die(); - -require_once($CFG->libdir . '/formslib.php'); - -/** - * matchmaking_strategy_selection form class - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class matchmaking_strategy_selection_form extends \moodleform { - - /** @var capquiz $capquiz */ - private capquiz $capquiz; - - /** - * Constructor. - * - * @param capquiz $capquiz - * @param moodle_url $url - */ - public function __construct(capquiz $capquiz, moodle_url $url) { - $this->capquiz = $capquiz; - parent::__construct($url); - } - - /** - * Defines form - */ - public function definition(): void { - $form = $this->_form; - $loader = new capquiz_matchmaking_strategy_loader($this->capquiz); - $registry = new capquiz_matchmaking_strategy_registry($this->capquiz); - $strategies = $registry->selection_strategies(); - $index = 0; - $selectedindex = -1; - $radioarray = []; - foreach ($strategies as $strategy) { - if ($loader->current_strategy_name() === $strategy) { - $selectedindex = $index; - } - $localized = capquiz_matchmaking_strategy_loader::localized_strategy_name($strategy); - $radioarray[] = $form->createElement('radio', 'strategy', '', $localized, $index, [$strategy]); - $index++; - } - $form->addGroup($radioarray, 'radioar', '', '
', false); - $this->add_action_buttons(false); - if ($selectedindex > -1) { - $form->setDefault('strategy', $selectedindex); - } - } - - /** - * Validate the data from the form. - * - * @param array $data array of ("fieldname"=>value) of submitted data - * @param array $files array of uploaded files "element_name"=>tmp_file_path - * @return array of "element_name"=>"error_description" if there are errors, - * or an empty array if everything is OK (true allowed for backwards compatibility too). - */ - public function validations($data, $files): array { - return []; - } - -} diff --git a/classes/form/view/question_list_create_form.php b/classes/form/view/question_list_create_form.php index 63c5d78..5b4de04 100755 --- a/classes/form/view/question_list_create_form.php +++ b/classes/form/view/question_list_create_form.php @@ -19,7 +19,7 @@ * * @package mod_capquiz * @author Aleksander Skrede - * @copyright 2018 NTNU + * @copyright 2018 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ diff --git a/classes/form/view/rating_system_selection_form.php b/classes/form/view/rating_system_selection_form.php deleted file mode 100755 index c95401f..0000000 --- a/classes/form/view/rating_system_selection_form.php +++ /dev/null @@ -1,97 +0,0 @@ -. - -/** - * CAPQuiz rating system selection form definition. - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz\form\view; - -use mod_capquiz\capquiz; -use mod_capquiz\capquiz_rating_system_loader; -use mod_capquiz\capquiz_rating_system_registry; -use moodle_url; - -defined('MOODLE_INTERNAL') || die(); - -require_once($CFG->libdir . '/formslib.php'); - -/** - * rating_system_selection_form class - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class rating_system_selection_form extends \moodleform { - - /** @var capquiz $capquiz */ - private capquiz $capquiz; - - /** - * Constructor. - * - * @param capquiz $capquiz - * @param moodle_url $url - */ - public function __construct(capquiz $capquiz, moodle_url $url) { - $this->capquiz = $capquiz; - parent::__construct($url); - } - - /** - * Defines form - */ - public function definition(): void { - $form = $this->_form; - $loader = new capquiz_rating_system_loader($this->capquiz); - $registry = new capquiz_rating_system_registry(); - $index = 0; - $selectedindex = -1; - $radioarray = []; - foreach ($registry->rating_systems() as $ratingsystem) { - if ($loader->current_rating_system_name() === $ratingsystem) { - $selectedindex = $index; - } - $radioarray[] = $form->createElement('radio', 'rating_system', '', $ratingsystem, $index, [$ratingsystem]); - $index++; - } - $form->addGroup($radioarray, 'radioar', '', '
', false); - $this->add_action_buttons(false); - if ($selectedindex > -1) { - $form->setDefault('rating_system', $selectedindex); - } - } - - /** - * Validate the data from the form. - * - * @param array $data array of ("fieldname"=>value) of submitted data - * @param array $files array of uploaded files "element_name"=>tmp_file_path - * @return array of "element_name"=>"error_description" if there are errors, - * or an empty array if everything is OK (true allowed for backwards compatibility too). - */ - public function validations($data, $files): array { - return []; - } - -} diff --git a/classes/matchmaking/capquiz_matchmaking_strategy_registry.php b/classes/matchmaking/capquiz_matchmaking_strategy_registry.php deleted file mode 100755 index 1ec206e..0000000 --- a/classes/matchmaking/capquiz_matchmaking_strategy_registry.php +++ /dev/null @@ -1,155 +0,0 @@ -. - -/** - * This file defines a class acting as a registry for matchmaking strategies - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz; - -use coding_exception; -use moodle_url; -use stdClass; - -defined('MOODLE_INTERNAL') || die(); - -require_once($CFG->dirroot . '/mod/capquiz/classes/capquiz_matchmaking_strategy.php'); -require_once($CFG->dirroot . '/mod/capquiz/classes/matchmaking/chronologic/chronologic_selector.php'); -require_once($CFG->dirroot . '/mod/capquiz/classes/matchmaking/n_closest/n_closest_selector.php'); -require_once($CFG->dirroot . '/mod/capquiz/classes/matchmaking/n_closest/n_closest_configuration_form.php'); - -/** - * Class capquiz_matchmaking_strategy_registry - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class capquiz_matchmaking_strategy_registry { - - /** @var capquiz $capquiz */ - private capquiz $capquiz; - - /** @var array $strategies */ - private array $strategies; - - /** - * Constructor. - * - * @param capquiz $capquiz - */ - public function __construct(capquiz $capquiz) { - $this->capquiz = $capquiz; - $this->register_selection_strategies(); - } - - /** - * Returns the specified matchmaking strategy or throws an error if it does not exist - * - * @param string $strategy - */ - public function selector(string $strategy): capquiz_matchmaking_strategy { - $value = $this->strategies[$strategy]; - if ($value) { - return array_values($value)[0](); - } - $this->throw_strategy_exception($strategy); - } - - /** - * Returns a configuration form for the matchmaking strategy - * - * @param string $strategy - * @param stdClass $config - * @param moodle_url $url - */ - public function configuration_form(string $strategy, stdClass $config, moodle_url $url) { - $value = $this->strategies[$strategy]; - if ($value) { - $configfunc = array_values($value)[1]; - return $configfunc($url, $config); - } - $this->throw_strategy_exception($strategy); - } - - /** - * Returns true if the registry has the specified strategy - * - * @param string $strategy - */ - public function has_strategy(string $strategy): bool { - return isset($this->strategies[$strategy]); - } - - /** - * Returns the default selection strategy - */ - public function default_selection_strategy(): string { - // The default selection strategy is added first. - // Modify capquiz_matchmaking_strategy_registry::register_selection_strategies() to change this. - $selectionstrategies = $this->selection_strategies(); - return reset($selectionstrategies); - } - - /** - * Returns all selection strategies' names - * - * @return string[] - */ - public function selection_strategies(): array { - $names = []; - foreach (array_keys($this->strategies) as $value) { - $names[] = $value; - } - return $names; - } - - /** - * Registers the selection strategies, the first registered will be the default strategy - */ - private function register_selection_strategies(): void { - // The first listed will be selected by default when creating a new activity. - $capquiz = $this->capquiz; - $this->strategies = [ - 'N-closest' => [ - fn() => new n_closest_selector($capquiz), - fn(moodle_url $url, stdClass $config) => new n_closest_configuration_form($config, $url), - ], - 'Chronological' => [ - fn() => new chronologic_selector(), - fn(moodle_url $url, stdClass $config) => null, - ], - ]; - } - - /** - * Creates and throws a strategy exception - * - * @param string $strategy - */ - private function throw_strategy_exception(string $strategy) { - $msg = "The specified strategy '$strategy' does not exist."; - $msg .= " Options are {'" . implode("', '", $this->selection_strategies()); - $msg .= "'}. This issue must be fixed by a programmer"; - throw new coding_exception($msg); - } -} diff --git a/classes/matchmaking/chronologic/chronologic_selector.php b/classes/matchmaking/chronologic/chronologic_selector.php deleted file mode 100755 index f09e45a..0000000 --- a/classes/matchmaking/chronologic/chronologic_selector.php +++ /dev/null @@ -1,86 +0,0 @@ -. - -/** - * This file defines a class which acts as a selector for the chronologic matchmaking strategy - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz; - -use stdClass; - -/** - * Class chronologic_selector - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class chronologic_selector extends capquiz_matchmaking_strategy { - - /** - * Nothing to configure - * - * @param stdClass $config - */ - public function configure(stdClass $config): void { - } - - /** - * No configuration needed - */ - public function configuration(): stdClass { - return new stdClass(); - } - - /** - * No configuration needed - */ - public function default_configuration(): stdClass { - return new stdClass(); - } - - /** - * Returns the next question for the user in a chronological order - * - * @param capquiz_user $user - * @param capquiz_question_list $qlist - * @param capquiz_question_attempt[] $inactiveattempts - */ - public function next_question_for_user(capquiz_user $user, capquiz_question_list $qlist, - array $inactiveattempts): ?capquiz_question { - $answered = function (capquiz_question $q) use ($inactiveattempts) { - foreach ($inactiveattempts as $inactiveattempt) { - if ($inactiveattempt->question_id() === $q->id()) { - return true; - } - } - return false; - }; - foreach ($qlist->questions() as $question) { - if (!$answered($question)) { - return $question; - } - } - return null; - } -} diff --git a/classes/matchmaking/n_closest/n_closest_configuration_form.php b/classes/matchmaking/n_closest/n_closest_configuration_form.php deleted file mode 100755 index 6d91a37..0000000 --- a/classes/matchmaking/n_closest/n_closest_configuration_form.php +++ /dev/null @@ -1,111 +0,0 @@ -. - -/** - * This file defines the configuration form for the n_closest matchmaking strategy - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz; - -use moodle_url; -use stdClass; - -defined('MOODLE_INTERNAL') || die(); - -require_once($CFG->libdir . '/formslib.php'); - -/** - * Class n_closest_configuration_form - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class n_closest_configuration_form extends \moodleform { - - /** @var stdClass $configuration */ - private stdClass $configuration; - - /** - * Constructor. - * - * @param stdClass $configuration - * @param moodle_url $url - */ - public function __construct(stdClass $configuration, moodle_url $url) { - $this->configuration = $configuration; - parent::__construct($url); - } - - /** - * Defines form - */ - public function definition(): void { - $form = $this->_form; - - $form->addElement('text', 'number_of_questions_to_select', get_string('number_of_questions_to_select', 'capquiz')); - $form->setType('number_of_questions_to_select', PARAM_INT); - $form->setDefault('number_of_questions_to_select', $this->configuration->number_of_questions_to_select); - $form->addRule('number_of_questions_to_select', get_string('number_of_questions_to_select_required', 'capquiz'), - 'required', null, 'client'); - $form->addHelpButton('number_of_questions_to_select', 'number_of_questions_to_select', 'capquiz'); - - $form->addElement('text', 'user_win_probability', get_string('user_win_probability', 'capquiz')); - $form->setType('user_win_probability', PARAM_FLOAT); - $form->setDefault('user_win_probability', $this->configuration->user_win_probability); - $form->addRule('user_win_probability', get_string('user_win_probability_required', 'capquiz'), - 'required', null, 'client'); - $form->addHelpButton('user_win_probability', 'user_win_probability', 'capquiz'); - - $form->addElement('text', 'prevent_same_question_for_turns', get_string('prevent_question_n_times', 'capquiz')); - $form->setType('prevent_same_question_for_turns', PARAM_INT); - $form->setDefault('prevent_same_question_for_turns', $this->configuration->prevent_same_question_for_turns); - $form->addRule('prevent_same_question_for_turns', get_string('field_required', 'capquiz'), - 'required', null, 'client'); - $form->addHelpButton('prevent_same_question_for_turns', 'prevent_question_n_times', 'capquiz'); - - $this->add_action_buttons(false); - } - - /** - * Validate the data from the form. - * - * @param array $data array of ("fieldname"=>value) of submitted data - * @param array $files array of uploaded files "element_name"=>tmp_file_path - * @return array of "element_name"=>"error_description" if there are errors, - * or an empty array if everything is OK (true allowed for backwards compatibility too). - */ - public function validations($data, $files): array { - $errors = []; - if (empty($data['user_win_probability'])) { - $errors['user_win_probability'] = get_string('user_win_probability_required', 'capquiz'); - } - if (empty($data['number_of_questions'])) { - $errors['number_of_questions'] = get_string('number_of_questions_to_select_required', 'capquiz'); - } - if (empty($data['prevent_same_question_for_turns'])) { - $errors['prevent_same_question_for_turns'] = get_string('field_required', 'capquiz'); - } - return $errors; - } - -} diff --git a/classes/matchmaking/n_closest/n_closest_selector.php b/classes/matchmaking/n_closest/n_closest_selector.php deleted file mode 100755 index 4a2221a..0000000 --- a/classes/matchmaking/n_closest/n_closest_selector.php +++ /dev/null @@ -1,174 +0,0 @@ -. - -/** - * This file defines a class which acts as a selector for the n_closest matchmaking strategy - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz; - -use stdClass; - -/** - * Class n_closest_selector - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class n_closest_selector extends capquiz_matchmaking_strategy { - - /** @var capquiz The capquiz */ - private capquiz $capquiz; - - /** @var float The propability of the user winning */ - private float $userwinprobability; - - /** @var int The number of questions to select */ - private int $numquestionstoselect; - - /** @var int The number of turns between each time a question can be selected */ - private int $preventsamequestionforturns; - - /** - * Constructor. - * - * @param capquiz $capquiz - */ - public function __construct(capquiz $capquiz) { - $this->capquiz = $capquiz; - $this->configure($this->default_configuration()); - } - - /** - * Configure the strategy - * - * @param stdClass $configuration - */ - public function configure(stdClass $configuration): void { - if ($configuration->user_win_probability > 0) { - $this->userwinprobability = $configuration->user_win_probability; - } - if ($configuration->number_of_questions_to_select > 0) { - $this->numquestionstoselect = $configuration->number_of_questions_to_select; - } - if ($configuration->prevent_same_question_for_turns >= 0) { - $this->preventsamequestionforturns = $configuration->prevent_same_question_for_turns; - } - } - - /** - * Returns the current strategy configuration - */ - public function configuration(): stdClass { - $config = new stdClass; - $config->prevent_same_question_for_turns = $this->preventsamequestionforturns; - $config->user_win_probability = $this->userwinprobability; - $config->number_of_questions_to_select = $this->numquestionstoselect; - return $config; - } - - /** - * Returns the default strategy configuration - */ - public function default_configuration(): stdClass { - $config = new stdClass; - $config->user_win_probability = 0.75; - $config->prevent_same_question_for_turns = 0; - $config->number_of_questions_to_select = 10; - return $config; - } - - /** - * Selects the next question for the user based on the configuration - * - * @param capquiz_user $user - * @param capquiz_question_list $qlist - * @param array $inactiveattempts - */ - public function next_question_for_user(capquiz_user $user, capquiz_question_list $qlist, - array $inactiveattempts): ?capquiz_question { - $excluded = $this->determine_excluded_questions($inactiveattempts); - $candidates = $this->find_questions_closest_to_rating($user, $excluded); - if (count($candidates) === 0) { - return null; - } - $index = mt_rand(0, count($candidates) - 1); - if ($question = $candidates[$index]) { - return $question; - } - return null; - } - - /** - * Finds the questions closest to the users rating - * - * @param capquiz_user $user - * @param array $excludedquestions - */ - private function find_questions_closest_to_rating(capquiz_user $user, array $excludedquestions): array { - global $DB; - $sql = 'SELECT * FROM {capquiz_question} WHERE question_list_id = ?'; - $sql .= str_repeat(' AND id <> ?', count($excludedquestions)); - $sql .= ' ORDER BY ABS(? - rating)'; - $params = []; - $params[] = $this->capquiz->question_list()->id(); - if (count($excludedquestions) > 0) { - array_push($params, ...$excludedquestions); - } - $params[] = $this->ideal_question_rating($user); - $questionentries = $DB->get_records_sql($sql, $params, 0, $this->numquestionstoselect); - $questions = []; - foreach ($questionentries as $questionentry) { - $questions[] = new capquiz_question($questionentry); - } - return $questions; - } - - /** - * Returns the ideal question rating - * - * @param capquiz_user $user - */ - private function ideal_question_rating(capquiz_user $user): float { - return 400.0 * log((1.0 / $this->userwinprobability) - 1.0, 10.0) + $user->rating(); - } - - /** - * Identifies questions to exclude and returns them in an array - * - * @param capquiz_question_attempt[] $inactiveattempts - */ - private function determine_excluded_questions(array $inactiveattempts): array { - $it = new \ArrayIterator(array_reverse($inactiveattempts, true)); - $excluded = []; - for ($i = 0; $i < $this->preventsamequestionforturns; $i++) { - if (!$it->valid()) { - break; - } - $excluded[] = $it->current()->question_id(); - $it->next(); - } - return array_unique($excluded); - } - -} diff --git a/classes/output/basic_renderer.php b/classes/output/basic_renderer.php deleted file mode 100755 index 2309030..0000000 --- a/classes/output/basic_renderer.php +++ /dev/null @@ -1,79 +0,0 @@ -. - -/** - * This file defines a class used to render buttons - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz\output; - -use mod_capquiz\capquiz_urls; -use moodle_url; -use renderer_base; - -/** - * Class basic_renderer - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class basic_renderer { - /** - * Renders the home button - * - * @param renderer_base $renderer - */ - public static function render_home_button(renderer_base $renderer): string { - $url = capquiz_urls::redirect(capquiz_urls::view_url()); - return self::render_action_button($renderer, $url, get_string('home', 'capquiz')); - } - - /** - * Renders a button - * - * @param renderer_base $renderer - * @param moodle_url $url - * @param string $label - * @param string $httpmethod The HTTP method to use for the form - * @param string[] $params The keys are used as names - * @param string $id - */ - public static function render_action_button(renderer_base $renderer, moodle_url $url, string $label, - string $httpmethod = 'post', array $params = [], string $id = ''): string { - $paramobjects = []; - foreach ($params as $name => $value) { - $paramobjects = [ - 'name' => $name, - 'value' => $value, - ]; - } - return $renderer->render_from_template('core/single_button', [ - 'type' => 'primary', - 'method' => $httpmethod, - 'url' => $url->out(false), - 'label' => $label, - 'params' => $paramobjects, - 'id' => $id, - ]); - } -} diff --git a/classes/output/classlist_renderer.php b/classes/output/classlist_renderer.php index 17d600c..dcc2e85 100755 --- a/classes/output/classlist_renderer.php +++ b/classes/output/classlist_renderer.php @@ -41,8 +41,8 @@ * * @package mod_capquiz * @author Aleksander Skrede - * @author Sebastian S. Gundersen - * @copyright 2019 NTNU + * @author Sebastian Gundersen + * @copyright 2019 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class classlist_renderer { @@ -53,9 +53,6 @@ class classlist_renderer { /** @var renderer_base $renderer */ private renderer_base $renderer; - /** @var moodle_page $page */ - private moodle_page $page; - /** * Constructor. * @@ -65,16 +62,16 @@ class classlist_renderer { public function __construct(capquiz $capquiz, renderer_base $renderer) { $this->capquiz = $capquiz; $this->renderer = $renderer; - $this->page = $capquiz->get_page(); } /** * Renders the entire classlist of the $capquiz in the constructor */ public function render(): bool|string { + global $PAGE; $cmid = $this->capquiz->course_module()->id; - $this->page->requires->js_call_amd('mod_capquiz/edit_questions', 'initialize', [$cmid]); - $users = capquiz_user::list_users($this->capquiz->id(), $this->capquiz->context()); + $PAGE->requires->js_call_amd('mod_capquiz/edit_questions', 'initialize', [$cmid]); + $users = capquiz_user::list_users($this->capquiz->get('id'), $this->capquiz->context()); $rows = []; for ($i = 0; $i < count($users); $i++) { $user = $users[$i]; @@ -83,10 +80,10 @@ public function render(): bool|string { 'username' => $user->username(), 'firstname' => $user->first_name(), 'lastname' => $user->last_name(), - 'rating' => round($user->rating(), 2), - 'stars' => $user->highest_stars_achieved(), - 'graded_stars' => $user->highest_stars_graded(), - 'passing_grade' => $user->highest_stars_graded() >= $this->capquiz->stars_to_pass(), + 'rating' => round($user->get('rating'), 2), + 'stars' => $user->get('highest_level'), + 'graded_stars' => $user->get('stars_graded'), + 'passing_grade' => $user->get('stars_graded') >= $this->capquiz->get('stars_to_pass'), ]; } return $this->renderer->render_from_template('capquiz/classlist', [ diff --git a/classes/output/grading_configuration_renderer.php b/classes/output/grading_configuration_renderer.php deleted file mode 100644 index 426669a..0000000 --- a/classes/output/grading_configuration_renderer.php +++ /dev/null @@ -1,121 +0,0 @@ -. - -/** - * This file defines a class used to render the grading configuration view - * - * @package mod_capquiz - * @author Sebastian S. Gundersen - * @copyright 2019 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz\output; - -use mod_capquiz\capquiz; -use mod_capquiz\capquiz_urls; -use mod_capquiz\form\view\grading_configuration_form; -use moodle_page; -use stdClass; - -defined('MOODLE_INTERNAL') || die(); - -require_once($CFG->dirroot . '/question/editlib.php'); - -/** - * Class grading_configuration_renderer - * - * @package mod_capquiz - * @author Sebastian S. Gundersen - * @copyright 2019 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class grading_configuration_renderer { - - /** @var capquiz $capquiz */ - private capquiz $capquiz; - - /** @var renderer $renderer */ - private renderer $renderer; - - /** @var moodle_page $page */ - private moodle_page $page; - - /** - * Constructor. - * - * @param capquiz $capquiz - * @param renderer $renderer - */ - public function __construct(capquiz $capquiz, renderer $renderer) { - $this->capquiz = $capquiz; - $this->renderer = $renderer; - $this->page = $capquiz->get_page(); - } - - /** - * Render grading configuration view - */ - public function render(): bool|string { - return $this->renderer->render_from_template('capquiz/configure_grading', [ - 'rating_form' => $this->get_rating_configuration(), - ]); - } - - /** - * Returns rating configuration form - */ - private function get_rating_configuration(): string { - $url = $this->page->url; - $form = new grading_configuration_form($this->capquiz, $url); - $formdata = $form->get_data(); - if ($formdata) { - $this->process_rating_configuration($formdata); - } - return $form->render(); - } - - /** - * Processes the rating configuration formdata - * - * @param stdClass $formdata - */ - private function process_rating_configuration(stdClass $formdata): void { - $star = 1; - $ratings = []; - while (isset($formdata->{"star_rating_$star"})) { - if (!isset($formdata->{"delstarbutton$star"})) { - $ratings[] = (int)$formdata->{"star_rating_$star"}; - } - $star++; - } - if (isset($formdata->addstarbutton)) { - $ratings[] = end($ratings) + 100; - } - if ($formdata->default_user_rating) { - $this->capquiz->set_default_user_rating($formdata->default_user_rating); - } - $this->capquiz->question_list()->set_star_ratings($ratings); - if ($formdata->starstopass) { - $this->capquiz->set_stars_to_pass($formdata->starstopass); - } - if ($formdata->timedue) { - $this->capquiz->set_time_due($formdata->timedue); - } - redirect(capquiz_urls::view_grading_url()); - } - -} diff --git a/classes/output/import_renderer.php b/classes/output/import_renderer.php index 8a457b2..3cfa762 100644 --- a/classes/output/import_renderer.php +++ b/classes/output/import_renderer.php @@ -34,8 +34,8 @@ * Class import_renderer * * @package mod_capquiz - * @author Sebastian S. Gundersen - * @copyright 2019 NTNU + * @author Sebastian Gundersen + * @copyright 2019 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class import_renderer { diff --git a/classes/output/instructor_dashboard_renderer.php b/classes/output/instructor_dashboard_renderer.php index c459b7f..e2b608d 100755 --- a/classes/output/instructor_dashboard_renderer.php +++ b/classes/output/instructor_dashboard_renderer.php @@ -39,7 +39,7 @@ * * @package mod_capquiz * @author Aleksander Skrede - * @copyright 2018 NTNU + * @copyright 2018 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class instructor_dashboard_renderer { @@ -80,10 +80,10 @@ private function render_summary(): bool|string { return 'question list error'; } return $this->renderer->render_from_template('capquiz/instructor_dashboard_summary', [ - 'published_status' => get_string($this->capquiz->is_published() ? 'published' : 'not_published', 'capquiz'), + 'published_status' => get_string($this->capquiz->get('published') ? 'published' : 'not_published', 'capquiz'), 'question_list_title' => $qlist->title(), 'question_count' => $qlist->question_count(), - 'enrolled_student_count' => capquiz_user::user_count($this->capquiz->id()), + 'enrolled_student_count' => capquiz_user::user_count($this->capquiz->get('id')), ]); } @@ -91,17 +91,15 @@ private function render_summary(): bool|string { * Renders publish button */ private function render_publish(): bool|string { - $published = $this->capquiz->is_published(); - $canpublish = $this->capquiz->can_publish(); $qlist = $this->capquiz->question_list(); if (!$qlist) { return 'question list error'; } $message = null; - if (!$canpublish) { + if (!$this->capquiz->can_publish()) { if ($qlist->question_count() === 0) { $message = get_string('publish_no_questions_in_list', 'capquiz'); - } else if ($published) { + } else if ($this->capquiz->get('published')) { $message = get_string('publish_already_published', 'capquiz'); } } diff --git a/classes/output/matchmaking_configuration_renderer.php b/classes/output/matchmaking_configuration_renderer.php deleted file mode 100644 index c720301..0000000 --- a/classes/output/matchmaking_configuration_renderer.php +++ /dev/null @@ -1,109 +0,0 @@ -. - -/** - * This file defines a class used to render the matchmaking configuration view - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz\output; - -use mod_capquiz\capquiz; -use mod_capquiz\capquiz_matchmaking_strategy_loader; -use mod_capquiz\capquiz_urls; -use moodle_page; - -defined('MOODLE_INTERNAL') || die(); - -require_once($CFG->dirroot . '/question/editlib.php'); - -/** - * Class matchmaking_configuration_renderer - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class matchmaking_configuration_renderer { - - /** @var capquiz $capquiz */ - private capquiz $capquiz; - - /** @var renderer $renderer */ - private renderer $renderer; - - /** @var capquiz_matchmaking_strategy_loader $registry */ - private capquiz_matchmaking_strategy_loader $registry; - - /** @var moodle_page $page */ - private moodle_page $page; - - /** - * matchmaking_configuration_renderer constructor. - * @param capquiz $capquiz - * @param renderer $renderer - */ - public function __construct(capquiz $capquiz, renderer $renderer) { - $this->capquiz = $capquiz; - $this->renderer = $renderer; - $this->registry = new capquiz_matchmaking_strategy_loader($this->capquiz); - $this->page = $capquiz->get_page(); - } - - /** - * Calls submethod that renders the matchmaking_configuration view - */ - public function render(): bool|string { - if ($this->registry->has_strategy()) { - return $this->render_configuration(); - } else { - return '

' . get_string('no_matchmaking_strategy_selected', 'capquiz') . '

'; - } - } - - /** - * Renders the matchmaking configuration view - */ - private function render_configuration(): bool|string { - $strategy = $this->registry->current_strategy_name(); - return $this->renderer->render_from_template('capquiz/matchmaking_configuration', [ - 'strategy' => capquiz_matchmaking_strategy_loader::localized_strategy_name($strategy), - 'form' => $this->render_form(), - ]); - } - - /** - * Returns the rendered matchmaking configuration form - */ - private function render_form(): string { - $url = $this->page->url; - if ($form = $this->registry->configuration_form($url)) { - $formdata = $form->get_data(); - if ($formdata) { - $this->registry->configure_current_strategy($formdata); - $url = capquiz_urls::view_rating_system_url(); - redirect($url); - } - return $form->render(); - } - return get_string('nothing_to_configure_for_strategy', 'capquiz'); - } -} diff --git a/classes/output/matchmaking_strategy_selection_renderer.php b/classes/output/matchmaking_strategy_selection_renderer.php deleted file mode 100644 index 76c24ab..0000000 --- a/classes/output/matchmaking_strategy_selection_renderer.php +++ /dev/null @@ -1,99 +0,0 @@ -. - -/** - * This file defines a class used to render the matchmaking strategy selection form - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz\output; - -use mod_capquiz\capquiz; -use mod_capquiz\capquiz_matchmaking_strategy_loader; -use mod_capquiz\capquiz_matchmaking_strategy_registry; -use mod_capquiz\capquiz_urls; -use mod_capquiz\form\view\matchmaking_strategy_selection_form; -use moodle_page; -use moodle_url; - -/** - * class matchmaking_strategy_selection_renderer - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class matchmaking_strategy_selection_renderer { - - /** @var moodle_url $url */ - private moodle_url $url; - - /** @var capquiz $capquiz */ - private capquiz $capquiz; - - /** @var renderer $renderer */ - private renderer $renderer; - - /** @var moodle_page $page */ - private moodle_page $page; - - /** - * matchmaking_strategy_selection_renderer constructor. - * - * @param capquiz $capquiz - * @param renderer $renderer - */ - public function __construct(capquiz $capquiz, renderer $renderer) { - $this->capquiz = $capquiz; - $this->renderer = $renderer; - $this->url = capquiz_urls::view_rating_system_url(); - $this->page = $capquiz->get_page(); - } - - /** - * Sets redirect url - * - * @param moodle_url $url - */ - public function set_redirect_url(moodle_url $url): void { - $this->url = $url; - } - - /** - * Renders the matchmaking strategy selection form - */ - public function render(): bool|string { - $url = $this->page->url; - $form = new matchmaking_strategy_selection_form($this->capquiz, $url); - $formdata = $form->get_data(); - if ($formdata) { - $loader = new capquiz_matchmaking_strategy_loader($this->capquiz); - $registry = new capquiz_matchmaking_strategy_registry($this->capquiz); - $strategy = $registry->selection_strategies()[$formdata->strategy]; - $loader->set_strategy($strategy); - redirect($this->url); - } - return $this->renderer->render_from_template('capquiz/matchmaking_selection_strategy', [ - 'form' => $form->render(), - ]); - } - -} diff --git a/classes/output/question_attempt_renderer.php b/classes/output/question_attempt_renderer.php index 68ea873..d191b23 100755 --- a/classes/output/question_attempt_renderer.php +++ b/classes/output/question_attempt_renderer.php @@ -27,12 +27,12 @@ use core\context\module; use mod_capquiz\capquiz; +use mod_capquiz\capquiz_question_engine; use mod_capquiz\capquiz_question_list; use mod_capquiz\capquiz_user; use mod_capquiz\capquiz_urls; use mod_capquiz\capquiz_question; use mod_capquiz\capquiz_question_attempt; -use moodle_page; use question_display_options; use renderer_base; @@ -41,7 +41,7 @@ * * @package mod_capquiz * @author Aleksander Skrede - * @copyright 2018 NTNU + * @copyright 2018 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class question_attempt_renderer { @@ -52,9 +52,6 @@ class question_attempt_renderer { /** @var renderer_base $renderer */ private renderer_base $renderer; - /** @var moodle_page $page */ - private moodle_page $page; - /** * Constructor. * @@ -65,7 +62,6 @@ public function __construct(capquiz $capquiz, renderer_base $renderer) { $this->capquiz = $capquiz; $this->renderer = $renderer; $this->render_question_head_html(); - $this->page = $capquiz->get_page(); } /** @@ -73,13 +69,9 @@ public function __construct(capquiz $capquiz, renderer_base $renderer) { */ private function render_question_head_html(): void { $user = $this->capquiz->user(); - $qengine = $this->capquiz->question_engine($user); - if ($qengine === null) { - return; - } - $attempt = $qengine->attempt_for_user($user); + $attempt = (new capquiz_question_engine($this->capquiz))->attempt_for_user($user); if ($attempt !== null) { - $user->question_usage()->render_question_head_html($attempt->question_slot()); + $user->question_usage()->render_question_head_html($attempt->get('slot')); } } @@ -87,18 +79,17 @@ private function render_question_head_html(): void { * Renders the question attempt view */ public function render(): string { - if (!$this->capquiz->is_published()) { + global $PAGE; + if (!$this->capquiz->get('published')) { return get_string('nothing_here_yet', 'capquiz'); } - $this->page->requires->js_call_amd('mod_capquiz/attempt', 'initialize', []); - $user = $this->capquiz->user(); - $qengine = $this->capquiz->question_engine($user); - $attempt = $qengine->attempt_for_user($user); + $PAGE->requires->js_call_amd('mod_capquiz/attempt', 'initialize', []); + $attempt = (new capquiz_question_engine($this->capquiz))->attempt_for_user($this->capquiz->user()); if ($attempt) { - if ($attempt->is_answered()) { + if ($attempt->get('answered')) { return $this->render_review($attempt); } - if ($attempt->is_pending()) { + if (!$attempt->get('reviewed')) { return $this->render_attempt($attempt, self::attempt_display_options($this->capquiz->context())); } } @@ -135,9 +126,13 @@ private function render_review(capquiz_question_attempt $attempt): string { * @param capquiz_question_attempt $attempt */ public function render_review_next_button(capquiz_question_attempt $attempt): string { - $url = capquiz_urls::response_reviewed_url($attempt); - $label = get_string('next', 'capquiz'); - return basic_renderer::render_action_button($this->renderer, $url, $label, id: 'capquiz_review_next'); + return $this->renderer->render_from_template('core/single_button', [ + 'type' => 'primary', + 'method' => 'post', + 'url' => capquiz_urls::response_reviewed_url($attempt)->out(false), + 'label' => get_string('next', 'capquiz'), + 'id' => 'capquiz_review_next', + ]); } /** @@ -147,7 +142,7 @@ public function render_review_next_button(capquiz_question_attempt $attempt): st */ private function render_progress(capquiz_user $user): string { $qlist = $this->capquiz->question_list(); - $percent = $qlist->next_level_percent($this->capquiz, $user->rating()); + $percent = $qlist->next_level_percent($this->capquiz, $user->get('rating')); list($stars, $blankstars, $nostars) = $this->user_star_progress($user, $qlist); $student = [ 'up' => $percent >= 0 ? ['percent' => $percent] : false, @@ -168,19 +163,20 @@ private function render_progress(capquiz_user $user): string { * @param question_display_options $options */ public function render_question_attempt(capquiz_question_attempt $attempt, question_display_options $options): string { + global $PAGE; $user = $this->capquiz->user(); $quba = $user->question_usage(); - $this->page->requires->js_module('core_question_engine'); + $PAGE->requires->js_module('core_question_engine'); return $this->renderer->render_from_template('capquiz/student_question_attempt', [ 'attempt' => [ 'url' => capquiz_urls::response_submit_url($attempt)->out(false), - 'body' => $quba->render_question($attempt->question_slot(), $options, $attempt->question_id()), + 'body' => $quba->render_question($attempt->get('slot'), $options, $attempt->get('question_id')), 'slots' => '', ], 'gradingdone' => $this->capquiz->is_grading_completed(), - 'finalgrade' => $user->highest_stars_graded(), - 'gradingpass' => $user->highest_stars_graded() >= $this->capquiz->stars_to_pass(), - 'duedate' => userdate($this->capquiz->time_due(), get_string('strftimedatetime', 'langconfig')), + 'finalgrade' => $user->get('stars_graded'), + 'gradingpass' => $user->get('stars_graded') >= $this->capquiz->get('stars_to_pass'), + 'duedate' => userdate($this->capquiz->get('timedue'), get_string('strftimedatetime', 'langconfig')), ]); } @@ -191,19 +187,16 @@ public function render_question_attempt(capquiz_question_attempt $attempt, quest * @param capquiz_question_attempt $attempt */ public function render_metainfo(capquiz_user $user, capquiz_question_attempt $attempt): string { - $question = capquiz_question::load($attempt->question_id()); - if ($question == null) { - return 'Question was not found.'; - } + $question = new capquiz_question($attempt->get('question_id')); return $this->renderer->render_from_template('capquiz/student_question_metainfo', [ 'metainfo' => [ 'rating' => [ - 'student' => $user->rating(), - 'question' => $question->rating(), + 'student' => $user->get('rating'), + 'question' => $question->get('rating'), ], 'question' => [ - 'capquiz_id' => $question->id(), - 'moodle_id' => $question->question_id(), + 'capquiz_id' => $question->get('id'), + 'moodle_id' => $question->get('question_id'), ], ], ]); @@ -221,8 +214,8 @@ private function user_star_progress(capquiz_user $user, capquiz_question_list $q $blankstars = []; $nostars = []; for ($star = 1; $star <= $qlist->max_stars(); $star++) { - if ($user->highest_stars_achieved() >= $star) { - if ($user->rating() >= $qlist->star_rating($star)) { + if ($user->get('highest_level') >= $star) { + if ($user->get('rating') >= $qlist->star_rating($star)) { $stars[] = true; } else { $blankstars[] = true; diff --git a/classes/output/question_bank_renderer.php b/classes/output/question_bank_renderer.php index 661b538..cbc41d6 100644 --- a/classes/output/question_bank_renderer.php +++ b/classes/output/question_bank_renderer.php @@ -28,14 +28,13 @@ use mod_capquiz\capquiz; use mod_capquiz\capquiz_urls; use mod_capquiz\bank\question_bank_view; -use moodle_page; /** * Class question_bank_renderer * * @package mod_capquiz * @author Aleksander Skrede - * @copyright 2018 NTNU + * @copyright 2018 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class question_bank_renderer { @@ -43,16 +42,12 @@ class question_bank_renderer { /** @var capquiz $capquiz */ private capquiz $capquiz; - /** @var moodle_page $page */ - private moodle_page $page; - /** * question_bank_renderer constructor. * @param capquiz $capquiz */ public function __construct(capquiz $capquiz) { $this->capquiz = $capquiz; - $this->page = $capquiz->get_page(); } /** @@ -67,14 +62,7 @@ public function create_view(): question_bank_view { * Renders question bank */ public function render(): string { - // phpcs:disable - // $questionsperpage = optional_param('qperpage', 10, PARAM_INT); - // $questionpage = optional_param('qpage', 0, PARAM_INT); - // phpcs:enable $questionview = $this->create_view(); - // phpcs:disable - // $html = "

" . get_string('available_questions', 'capquiz') . "

"; - // phpcs:enable ob_start(); $questionview->display(); $qbank = ob_get_clean(); @@ -87,8 +75,10 @@ public function render(): string { * Moodle coding standard does not allow us to override $_GET or $_POST before calling question_edit_setup() */ private function setup_question_edit(): array { + global $PAGE; + $PAGE->set_pagelayout('admin'); $params = []; - $params['cmid'] = capquiz_urls::require_course_module_id_param(); + $params['cmid'] = $PAGE->cm->id; $params['qpage'] = optional_param('qpage', null, PARAM_INT); $params['cat'] = optional_param('cat', null, PARAM_SEQUENCE); $params['category'] = optional_param('category', null, PARAM_SEQUENCE); @@ -106,9 +96,8 @@ private function setup_question_edit(): array { $params['qbshowtext'] = optional_param('qbshowtext', null, PARAM_BOOL); $params['cpage'] = optional_param('cpage', null, PARAM_INT); $params['qtagids'] = optional_param_array('qtagids', null, PARAM_INT); - $this->page->set_pagelayout('admin'); - $edittab = 'editq'; - return question_build_edit_resources($edittab, capquiz_urls::$urledit, $params); + $params['page'] = 'questions'; + return question_build_edit_resources('editq', $PAGE->url->out_as_local_url(false, []), $params); } } diff --git a/classes/output/question_list_creator_renderer.php b/classes/output/question_list_creator_renderer.php index be869e3..33efdff 100644 --- a/classes/output/question_list_creator_renderer.php +++ b/classes/output/question_list_creator_renderer.php @@ -30,6 +30,7 @@ use mod_capquiz\capquiz_question_list; use mod_capquiz\form\view\question_list_create_form; use moodle_page; +use moodle_url; defined('MOODLE_INTERNAL') || die(); @@ -40,7 +41,7 @@ * * @package mod_capquiz * @author Aleksander Skrede - * @copyright 2018 NTNU + * @copyright 2018 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class question_list_creator_renderer { @@ -51,9 +52,6 @@ class question_list_creator_renderer { /** @var renderer $renderer */ private renderer $renderer; - /** @var moodle_page $PAGE */ - private moodle_page $page; - /** * Constructor. * @@ -63,15 +61,14 @@ class question_list_creator_renderer { public function __construct(capquiz $capquiz, renderer $renderer) { $this->capquiz = $capquiz; $this->renderer = $renderer; - $this->page = $capquiz->get_page(); } /** * Renders the question list creator */ public function render(): bool|string { - $url = $this->page->url; - $form = new question_list_create_form($url); + global $PAGE; + $form = new question_list_create_form($PAGE->url); $formdata = $form->get_data(); if ($formdata) { $ratings = [ @@ -85,9 +82,9 @@ public function render(): bool|string { $description = $formdata->description; $qlist = capquiz_question_list::create_new_instance($this->capquiz, $title, $description, $ratings); if ($qlist) { - redirect(capquiz_urls::create_view_url(capquiz_urls::$urlview)); + redirect(new moodle_url('/mod/capquiz/view.php', ['id' => $PAGE->cm->id])); } - capquiz_urls::redirect_to_front_page(); + redirect(new moodle_url('/')); } return $this->renderer->render_from_template('capquiz/create_question_list', [ 'form' => $form->render(), diff --git a/classes/output/question_list_renderer.php b/classes/output/question_list_renderer.php index 0f360de..6eb4391 100644 --- a/classes/output/question_list_renderer.php +++ b/classes/output/question_list_renderer.php @@ -37,8 +37,8 @@ * * @package mod_capquiz * @author Aleksander Skrede - * @author Sebastian S. Gundersen - * @copyright 2019 NTNU + * @author Sebastian Gundersen + * @copyright 2019 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class question_list_renderer { @@ -49,9 +49,6 @@ class question_list_renderer { /** @var renderer $renderer */ private renderer $renderer; - /** @var moodle_page $page */ - private moodle_page $page; - /** * Constructor. * @@ -61,15 +58,15 @@ class question_list_renderer { public function __construct(capquiz $capquiz, renderer $renderer) { $this->capquiz = $capquiz; $this->renderer = $renderer; - $this->page = $capquiz->get_page(); } /** * Renders question list */ public function render(): bool|string { + global $PAGE; $cmid = $this->capquiz->course_module()->id; - $this->page->requires->js_call_amd('mod_capquiz/edit_questions', 'initialize', [$cmid]); + $PAGE->requires->js_call_amd('mod_capquiz/edit_questions', 'initialize', [$cmid]); $qlist = $this->capquiz->question_list(); if ($qlist?->has_questions()) { return $this->render_questions($qlist); @@ -85,18 +82,19 @@ public function render(): bool|string { * @param capquiz_question_list $qlist */ private function render_questions(capquiz_question_list $qlist): bool|string { + global $PAGE; $rows = []; $questions = $qlist->questions(); for ($i = 0; $i < $qlist->question_count(); $i++) { $question = $questions[$i]; $courseid = $question->course_id(); $editurl = new moodle_url('/question/bank/editquestion/question.php', [ - 'cmid' => $this->page->cm->id, - 'id' => $question->question_id(), + 'cmid' => $PAGE->cm->id, + 'id' => $question->get('question_id'), ]); $previewurl = new moodle_url('/question/bank/previewquestion/preview.php', [ - 'cmid' => $this->page->cm->id, - 'id' => $question->question_id(), + 'cmid' => $PAGE->cm->id, + 'id' => $question->get('question_id'), ]); $targetblank = ['name' => 'target', 'value' => '_blank']; $edit = $courseid === 0 ? false : [ @@ -111,14 +109,24 @@ private function render_questions(capquiz_question_list $qlist): bool|string { 'classes' => 'fa fa-search-plus', 'attributes' => [$targetblank], ]; + $setquestionratingurl = new moodle_url('/mod/capquiz/action.php', [ + 'id' => $PAGE->cm->id, + 'action' => 'set-question-rating', + 'question-id' => $question->get('id'), + ]); + $removequestionurl = new moodle_url('/mod/capquiz/action.php', [ + 'id' => $PAGE->cm->id, + 'action' => 'remove-question', + 'question-id' => $question->get('id'), + ]); $rows[] = [ 'index' => $i + 1, - 'name' => $question->name(), - 'rating' => round($question->rating(), 3), - 'question_id' => $question->id(), - 'rating_url' => capquiz_urls::set_question_rating_url($question->id())->out(false), + 'name' => $question->get_question_name(), + 'rating' => round($question->get('rating'), 3), + 'question_id' => $question->get('id'), + 'rating_url' => $setquestionratingurl->out(false), 'delete' => [ - 'url' => capquiz_urls::remove_question_from_list_url($question->id())->out(false), + 'url' => $removequestionurl->out(false), 'label' => get_string('remove', 'capquiz'), 'classes' => 'fa fa-trash', ], diff --git a/classes/output/question_list_selection_renderer.php b/classes/output/question_list_selection_renderer.php index f882c82..bd04bf5 100755 --- a/classes/output/question_list_selection_renderer.php +++ b/classes/output/question_list_selection_renderer.php @@ -25,17 +25,17 @@ namespace mod_capquiz\output; -use context_module; use mod_capquiz\capquiz_question_list; use mod_capquiz\capquiz_urls; +use moodle_url; use renderer_base; /** * Class question_list_selection_renderer * * @package mod_capquiz - * @author Sebastian S. Gundersen - * @copyright 2018 NTNU + * @author Sebastian Gundersen + * @copyright 2018 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class question_list_selection_renderer { @@ -55,10 +55,9 @@ public function __construct(renderer_base $renderer) { /** * Renders the question list selection */ - public function render(): bool|string { - $templates = capquiz_question_list::load_question_list_templates(); + public function render(int $cmid): bool|string { $lists = []; - foreach ($templates as $template) { + foreach (capquiz_question_list::load_question_list_templates() as $template) { $lists[] = [ 'title' => $template->title(), 'description' => $template->description(), @@ -67,15 +66,21 @@ public function render(): bool|string { 'url' => capquiz_urls::question_list_select_url($template), ]; } - - $createurl = capquiz_urls::view_create_question_list_url(); - $params = $createurl->params(); + $createurl = new moodle_url('/mod/capquiz/view.php', ['id' => $cmid, 'page' => 'createquestionlist']); + $params = []; + foreach ($createurl->params() as $name => $value) { + $params = ['name' => $name, 'value' => $value]; + } $createurl->remove_all_params(); - $createlabel = get_string('create_question_list', 'capquiz'); - return $this->renderer->render_from_template('capquiz/question_list_selection', [ 'lists' => $lists, - 'create' => basic_renderer::render_action_button($this->renderer, $createurl, $createlabel, 'get', $params), + 'create' => $this->renderer->render_from_template('core/single_button', [ + 'type' => 'primary', + 'method' => 'get', + 'url' => $createurl->out(false), + 'label' => get_string('create_question_list', 'capquiz'), + 'params' => $params, + ]), ]); } diff --git a/classes/output/rating_system_configuration_renderer.php b/classes/output/rating_system_configuration_renderer.php deleted file mode 100644 index 5a4df1f..0000000 --- a/classes/output/rating_system_configuration_renderer.php +++ /dev/null @@ -1,107 +0,0 @@ -. - -/** - * This file defines a class used to render the rating system configuration view - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz\output; - -use mod_capquiz\capquiz; -use mod_capquiz\capquiz_rating_system_loader; -use mod_capquiz\capquiz_urls; -use moodle_page; - -defined('MOODLE_INTERNAL') || die(); - -require_once($CFG->dirroot . '/question/editlib.php'); - -/** - * Class rating_system_configuration_renderer - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class rating_system_configuration_renderer { - - /** @var capquiz $capquiz */ - private capquiz $capquiz; - - /** @var renderer $renderer */ - private renderer $renderer; - - /** @var capquiz_rating_system_loader $registry */ - private capquiz_rating_system_loader $registry; - - /** @var moodle_page $page */ - private moodle_page $page; - - /** - * rating_system_configuration_renderer constructor. - * @param capquiz $capquiz - * @param renderer $renderer - */ - public function __construct(capquiz $capquiz, renderer $renderer) { - $this->capquiz = $capquiz; - $this->renderer = $renderer; - $this->registry = new capquiz_rating_system_loader($capquiz); - $this->page = $capquiz->get_page(); - } - - /** - * Calls submethod that renders the rating_system_configuration view - */ - public function render(): bool|string { - if ($this->registry->has_rating_system()) { - return $this->render_configuration(); - } else { - return '

No rating system has been specified

'; - } - } - - /** - * Renders the rating configuration view - */ - private function render_configuration(): bool|string { - return $this->renderer->render_from_template('capquiz/rating_system_configuration', [ - 'strategy' => $this->registry->current_rating_system_name(), - 'form' => $this->render_form(), - ]); - } - - /** - * Renders the rating configuration form - */ - private function render_form(): string { - $url = $this->page->url; - if ($form = $this->registry->configuration_form($url)) { - $formdata = $form->get_data(); - if ($formdata) { - $this->registry->configure_current_rating_system($formdata); - redirect(capquiz_urls::view_rating_system_url()); - } - return $form->render(); - } - return 'There is nothing to configure for this rating system'; - } -} diff --git a/classes/output/rating_system_selection_renderer.php b/classes/output/rating_system_selection_renderer.php deleted file mode 100644 index d53dd9f..0000000 --- a/classes/output/rating_system_selection_renderer.php +++ /dev/null @@ -1,98 +0,0 @@ -. - -/** - * This file defines a class used to render the rating system selection form - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz\output; - -use mod_capquiz\capquiz; -use mod_capquiz\capquiz_rating_system_loader; -use mod_capquiz\capquiz_rating_system_registry; -use mod_capquiz\capquiz_urls; -use mod_capquiz\form\view\rating_system_selection_form; -use moodle_page; -use moodle_url; - -/** - * Class rating_system_selection_renderer - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class rating_system_selection_renderer { - - /** @var moodle_url $url */ - private moodle_url $url; - - /** @var capquiz $capquiz */ - private capquiz $capquiz; - - /** @var renderer $renderer */ - private renderer $renderer; - - /** @var moodle_page $page */ - private moodle_page $page; - - /** - * rating_system_selection_renderer constructor. - * - * @param capquiz $capquiz - * @param renderer $renderer - */ - public function __construct(capquiz $capquiz, renderer $renderer) { - $this->capquiz = $capquiz; - $this->renderer = $renderer; - $this->url = capquiz_urls::view_rating_system_url(); - $this->page = $capquiz->get_page(); - } - - /** - * Sets redirect url - * - * @param moodle_url $url - */ - public function set_redirect_url(moodle_url $url): void { - $this->url = $url; - } - - /** - * Renders the rating system selection form - */ - public function render(): bool|string { - $url = $this->page->url; - $form = new rating_system_selection_form($this->capquiz, $url); - $formdata = $form->get_data(); - if ($formdata) { - $registry = new capquiz_rating_system_registry(); - $loader = new capquiz_rating_system_loader($this->capquiz); - $loader->set_rating_system($registry->rating_systems()[$formdata->rating_system]); - redirect($this->url); - } - return $this->renderer->render_from_template('capquiz/rating_system_selection', [ - 'form' => $form->render(), - ]); - } - -} diff --git a/classes/output/renderer.php b/classes/output/renderer.php index 16c09db..1713dbf 100755 --- a/classes/output/renderer.php +++ b/classes/output/renderer.php @@ -25,120 +25,28 @@ namespace mod_capquiz\output; -use core_renderer; use mod_capquiz\capquiz; -use mod_capquiz\capquiz_urls; -use moodle_url; -use renderer_base; -use tabobject; defined('MOODLE_INTERNAL') || die(); -require_once($CFG->dirroot . '/mod/capquiz/classes/output/basic_renderer.php'); -require_once($CFG->dirroot . '/mod/capquiz/classes/output/classlist_renderer.php'); -require_once($CFG->dirroot . '/mod/capquiz/classes/output/question_list_renderer.php'); -require_once($CFG->dirroot . '/mod/capquiz/classes/output/question_bank_renderer.php'); -require_once($CFG->dirroot . '/mod/capquiz/classes/output/question_attempt_renderer.php'); -require_once($CFG->dirroot . '/mod/capquiz/classes/output/unauthorized_view_renderer.php'); -require_once($CFG->dirroot . '/mod/capquiz/classes/output/question_list_creator_renderer.php'); -require_once($CFG->dirroot . '/mod/capquiz/classes/output/instructor_dashboard_renderer.php'); -require_once($CFG->dirroot . '/mod/capquiz/classes/output/matchmaking_configuration_renderer.php'); -require_once($CFG->dirroot . '/mod/capquiz/classes/output/grading_configuration_renderer.php'); -require_once($CFG->dirroot . '/mod/capquiz/classes/output/matchmaking_strategy_selection_renderer.php'); - /** * Main plugin renderer for capquiz * * @package mod_capquiz * @author Aleksander Skrede - * @copyright 2018 NTNU + * @copyright 2018 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class renderer extends \plugin_renderer_base { - - /** - * Returns a reference to the current renderer - */ - public function output_renderer(): core_renderer|renderer_base { - return $this->output; - } - - /** - * Creates a tab - * - * @param string $name Name of the tab - * @param string $title Title of the tab - * @param moodle_url $link Link - */ - private function tab(string $name, string $title, moodle_url $link): tabobject { - return new tabobject($name, $link, get_string($title, 'capquiz')); - } - - /** - * Creates all tabs - * - * @param string $activetab The currently active cab - */ - private function tabs(string $activetab): bool|string { - $tabs = [ - $this->tab('view_dashboard', 'dashboard', capquiz_urls::view_url()), - $this->tab('view_rating_system', 'rating_system', capquiz_urls::view_rating_system_url()), - $this->tab('view_questions', 'questions', capquiz_urls::view_question_list_url()), - $this->tab('view_grading', 'grading', capquiz_urls::view_grading_url()), - $this->tab('view_classlist', 'classlist', capquiz_urls::view_classlist_url()), - $this->tab('view_import', 'other_question_lists', capquiz_urls::view_import_url()), - $this->tab('view_report', 'reports', capquiz_urls::view_report_url()), - ]; - return print_tabs([$tabs], $activetab, null, null, true); - } - - /** - * Display a tabbed view - * - * @param string $view - * @param string $activetab - */ - public function display_tabbed_view(string $view, string $activetab): void { - echo $this->output->header(); - echo $this->tabs($activetab); - echo $view; - echo $this->output->footer(); - } - - /** - * Display multiple tabbed views - * - * @param string[] $views The renderers to render the tabs - * @param string $activetab The currently active tab - */ - public function display_tabbed_views(array $views, string $activetab): void { - echo $this->output->header(); - echo $this->tabs($activetab); - foreach ($views as $view) { - echo $view; - } - echo $this->output->footer(); - } - - /** - * Display view. - * - * @param string $view - */ - public function display_view(string $view): void { - echo $this->output->header(); - echo $view; - echo $this->output->footer(); - } - /** * Display the question attempt view * * @param capquiz $capquiz */ public function display_question_attempt_view(capquiz $capquiz): void { - $renderer = new question_attempt_renderer($capquiz, $this); - $this->display_view($renderer->render()); + echo $this->output->header(); + echo (new question_attempt_renderer($capquiz, $this))->render(); + echo $this->output->footer(); } /** @@ -147,8 +55,9 @@ public function display_question_attempt_view(capquiz $capquiz): void { * @param capquiz $capquiz */ public function display_instructor_dashboard(capquiz $capquiz): void { - $renderer = new instructor_dashboard_renderer($capquiz, $this); - $this->display_tabbed_view($renderer->render(), 'view_dashboard'); + echo $this->output->header(); + echo (new instructor_dashboard_renderer($capquiz, $this))->render(); + echo $this->output->footer(); } /** @@ -157,24 +66,18 @@ public function display_instructor_dashboard(capquiz $capquiz): void { * @param capquiz $capquiz */ public function display_question_list_create_view(capquiz $capquiz): void { - $renderer = new question_list_creator_renderer($capquiz, $this); - $this->display_view($renderer->render()); + echo $this->output->header(); + echo (new question_list_creator_renderer($capquiz, $this))->render(); + echo $this->output->footer(); } /** * Display the choose question list view */ - public function display_choose_question_list_view(): void { - $renderer = new question_list_selection_renderer($this); - $this->display_view($renderer->render()); - } - - /** - * Display the unauthorized view - */ - public function display_unauthorized_view(): void { - $renderer = new unauthorized_view_renderer($this); - $this->display_view($renderer->render()); + public function display_choose_question_list_view(int $cmid): void { + echo $this->output->header(); + echo (new question_list_selection_renderer($this))->render($cmid); + echo $this->output->footer(); } /** @@ -183,25 +86,14 @@ public function display_unauthorized_view(): void { * @param capquiz $capquiz */ public function display_question_list_view(capquiz $capquiz): void { - $r1 = new question_list_renderer($capquiz, $this); - $r2 = new question_bank_renderer($capquiz); - $html = '
' . $r1->render() . '
'; - $html .= '
' . $r2->render() . '
'; - $this->display_tabbed_view($html, 'view_questions'); - } - - /** - * Display the rating system configuration - * - * @param capquiz $capquiz - */ - public function display_rating_system_configuration(capquiz $capquiz): void { - $this->display_tabbed_views([ - (new matchmaking_strategy_selection_renderer($capquiz, $this))->render(), - (new matchmaking_configuration_renderer($capquiz, $this))->render(), - (new rating_system_selection_renderer($capquiz, $this))->render(), - (new rating_system_configuration_renderer($capquiz, $this))->render(), - ], 'view_rating_system'); + echo $this->output->header(); + echo '
'; + echo (new question_list_renderer($capquiz, $this))->render(); + echo '
'; + echo '
'; + echo (new question_bank_renderer($capquiz))->render(); + echo '
'; + echo $this->output->footer(); } @@ -211,8 +103,9 @@ public function display_rating_system_configuration(capquiz $capquiz): void { * @param capquiz $capquiz */ public function display_leaderboard(capquiz $capquiz): void { - $renderer = new classlist_renderer($capquiz, $this); - $this->display_tabbed_view($renderer->render(), 'view_classlist'); + echo $this->output->header(); + echo (new classlist_renderer($capquiz, $this))->render(); + echo $this->output->footer(); } /** @@ -221,18 +114,9 @@ public function display_leaderboard(capquiz $capquiz): void { * @param capquiz $capquiz */ public function display_import(capquiz $capquiz): void { - $renderer = new import_renderer($capquiz, $this); - $this->display_tabbed_view($renderer->render(), 'view_import'); - } - - /** - * Display the grading configuration view - * - * @param capquiz $capquiz - */ - public function display_grading_configuration(capquiz $capquiz): void { - $renderer = new grading_configuration_renderer($capquiz, $this); - $this->display_tabbed_view($renderer->render(), 'view_grading'); + echo $this->output->header(); + echo (new import_renderer($capquiz, $this))->render(); + echo $this->output->footer(); } /** @@ -241,7 +125,8 @@ public function display_grading_configuration(capquiz $capquiz): void { * @param capquiz $capquiz */ public function display_report(capquiz $capquiz): void { - $renderer = new report_renderer($capquiz); - $this->display_tabbed_view($renderer->render(), 'view_report'); + echo $this->output->header(); + echo (new report_renderer($capquiz, $this))->render(); + echo $this->output->footer(); } } diff --git a/classes/output/report_renderer.php b/classes/output/report_renderer.php index f6936ed..7519b48 100644 --- a/classes/output/report_renderer.php +++ b/classes/output/report_renderer.php @@ -25,11 +25,11 @@ namespace mod_capquiz\output; -use capquiz_exception; use mod_capquiz\capquiz; use mod_capquiz\capquiz_urls; use mod_capquiz\report\capquiz_report_factory; -use moodle_page; +use moodle_exception; +use moodle_url; use tabobject; defined('MOODLE_INTERNAL') || die(); @@ -49,9 +49,6 @@ class report_renderer { /** @var capquiz $capquiz */ private capquiz $capquiz; - /** @var moodle_page $page */ - private moodle_page $page; - /** * Constructor. * @@ -59,28 +56,31 @@ class report_renderer { */ public function __construct(capquiz $capquiz) { $this->capquiz = $capquiz; - $this->page = $capquiz->get_page(); } /** * Renders report */ public function render(): string { - global $CFG; + global $CFG, $PAGE; $html = ''; $download = optional_param('download', '', PARAM_RAW); $mode = optional_param('mode', '', PARAM_ALPHA); - $reportlist = capquiz_report_list($this->capquiz->context()); + $reportlist = capquiz_report_list($PAGE->cm->context); if (empty($reportlist)) { return get_string('noreports', 'capquiz'); } if ($mode === '') { // Default to first accessible report and redirect. - capquiz_urls::redirect_to_url(capquiz_urls::view_report_url(reset($reportlist))); + redirect(new moodle_url('/mod/capquiz/view.php', [ + 'id' => $PAGE->cm->id, + 'page' => 'report', + 'mode' => reset($reportlist), + ])); } if (!in_array($mode, $reportlist)) { - throw new capquiz_exception('erroraccessingreport', 'capquiz', + throw new moodle_exception('erroraccessingreport', 'capquiz', $CFG->wwwroot . '/mod/capquiz/view.php?id=' . $this->capquiz->course()->id); } $report = capquiz_report_factory::make($mode); @@ -88,7 +88,11 @@ public function render(): string { $row = []; foreach ($reportlist as $rep) { - $url = capquiz_urls::view_report_url($rep); + $url = new moodle_url('/mod/capquiz/view.php', [ + 'id' => $PAGE->cm->id, + 'page' => 'report', + 'mode' => $rep, + ]); $row[] = new tabobject('capquiz_' . $rep, $url, get_string('pluginname', 'capquizreport_' . $rep)); } $tabs[] = $row; @@ -105,7 +109,8 @@ public function render(): string { * Sets pagelayout to "report" */ private function setup_report(): void { - $this->page->set_pagelayout('report'); + global $PAGE; + $PAGE->set_pagelayout('report'); } } diff --git a/classes/output/unauthorized_view_renderer.php b/classes/output/unauthorized_view_renderer.php deleted file mode 100755 index 2ea302e..0000000 --- a/classes/output/unauthorized_view_renderer.php +++ /dev/null @@ -1,57 +0,0 @@ -. - -/** - * This file defines a class used to render the unauthorized view - * - * @package mod_capquiz - * @author Sebastian S. Gundersen - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz\output; - -/** - * Class unauthorized_view_renderer - * - * @package mod_capquiz - * @author Sebastian S. Gundersen - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class unauthorized_view_renderer { - - /** @var renderer */ - private renderer $renderer; - - /** - * Constructor. - * - * @param renderer $renderer - */ - public function __construct(renderer $renderer) { - $this->renderer = $renderer; - } - - /** - * Renders the "unauthorized" view - */ - public function render(): bool|string { - return $this->renderer->render_from_template('capquiz/unauthorized', []); - } - -} diff --git a/classes/privacy/provider.php b/classes/privacy/provider.php index 1e34a07..8be9db3 100644 --- a/classes/privacy/provider.php +++ b/classes/privacy/provider.php @@ -41,8 +41,8 @@ * Privacy Subsystem implementation for mod_capquiz. * * @author André Storhaug - * @author Sebastian Søviknes Gundersen - * @copyright 2019 NTNU + * @author Sebastian Gundersen + * @copyright 2019 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ class provider implements diff --git a/classes/rating_system/capquiz_rating_system_registry.php b/classes/rating_system/capquiz_rating_system_registry.php deleted file mode 100755 index efdd352..0000000 --- a/classes/rating_system/capquiz_rating_system_registry.php +++ /dev/null @@ -1,138 +0,0 @@ -. - -/** - * This file defines a class used as a registry for the rating system - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz; - -use coding_exception; -use moodle_url; -use stdClass; - -defined('MOODLE_INTERNAL') || die(); - -require_once($CFG->dirroot . '/mod/capquiz/classes/rating_system/elo_rating/elo_rating_system.php'); -require_once($CFG->dirroot . '/mod/capquiz/classes/rating_system/elo_rating/elo_rating_system_form.php'); - -/** - * Class capquiz_rating_system_registry - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class capquiz_rating_system_registry { - - /** @var callable[][] $systems */ - private array $systems; - - /** - * Constructor. - */ - public function __construct() { - $this->register_rating_systems(); - } - - /** - * Returns rating system - * - * @param string $system - */ - public function rating_system(string $system): capquiz_rating_system { - $value = $this->systems[$system]; - if (!$value) { - $this->throw_rating_system_exception($system); - } - return array_values($value)[0](); - } - - /** - * Returns configuration form - * - * @param string $system - * @param stdClass $configuration - * @param moodle_url $url - */ - public function configuration_form(string $system, stdClass $configuration, moodle_url $url) { - $value = $this->systems[$system]; - if ($value) { - $configfunc = array_values($value)[1]; - return $configfunc($url, $configuration); - } - $this->throw_rating_system_exception($system); - } - - /** - * Checks if this instance has a rating system - * - * @param string $system - */ - public function has_rating_system(string $system): bool { - return isset($this->systems[$system]); - } - - /** - * Returns the default rating system - */ - public function default_rating_system(): string { - // Default rating system is added first. - // Modify caquiz_rating_system_registry::register_rating_systems() to change this. - $ratingsystems = $this->rating_systems(); - return reset($ratingsystems); - } - - /** - * Returns the names of all rating systems. - * - * @return string[] - */ - public function rating_systems(): array { - return array_keys($this->systems); - } - - /** - * Registers rating systems - */ - private function register_rating_systems(): void { - // The first listed will be selected by default when creating a new activity. - $this->systems = [ - 'Elo' => [ - fn() => new elo_rating_system(), - fn(moodle_url $url, stdClass $config) => new elo_rating_system_form($config, $url), - ], - ]; - } - - /** - * Creates and throws exception - * - * @param string $system - */ - private function throw_rating_system_exception(string $system) { - $msg = "The specified rating system '$system' does not exist."; - $msg .= " Options are {'" . implode("', '", $this->rating_systems()); - $msg .= "'}. This issue must be fixed by a programmer"; - throw new coding_exception($msg); - } -} diff --git a/classes/rating_system/elo_rating/elo_rating_system.php b/classes/rating_system/elo_rating/elo_rating_system.php deleted file mode 100755 index b888d38..0000000 --- a/classes/rating_system/elo_rating/elo_rating_system.php +++ /dev/null @@ -1,121 +0,0 @@ -. - -/** - * This file defines a class used as a registry for the rating system - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz; - -use stdClass; - -/** - * Class elo_rating_system - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class elo_rating_system extends capquiz_rating_system { - - /** @var float $studentkfactor */ - private float $studentkfactor; - - /** @var float $questionkfactor */ - private float $questionkfactor; - - /** - * Configures the rating system - * - * @param stdClass $config - */ - public function configure(stdClass $config): void { - if ($config->student_k_factor) { - $this->studentkfactor = $config->student_k_factor; - } - if ($config->question_k_factor) { - $this->questionkfactor = $config->question_k_factor; - } - } - - /** - * Returns the current configuration - */ - public function configuration(): stdClass { - $config = new stdClass; - $config->student_k_factor = $this->studentkfactor; - $config->question_k_factor = $this->questionkfactor; - return $config; - } - - /** - * Returns the default configuration - */ - public function default_configuration(): stdClass { - $config = new stdClass; - $config->student_k_factor = 32; - $config->question_k_factor = 8; - return $config; - } - - /** - * Updates the users rating - * - * @param capquiz_user $user - * @param capquiz_question $question - * @param float $score - */ - public function update_user_rating(capquiz_user $user, capquiz_question $question, float $score): void { - $current = $user->rating(); - $factor = $this->studentkfactor; - $newrating = $current + $factor * ($score - $this->expected_result($current, $question->rating())); - $user->set_rating($newrating); - } - - /** - * Updates the winning and losing questions ratings - * - * @param capquiz_question $winner - * @param capquiz_question $loser - */ - public function question_victory_ratings(capquiz_question $winner, capquiz_question $loser): void { - $loserating = $loser->rating(); - $winrating = $winner->rating(); - $factor = $this->questionkfactor; - $newloserating = $loserating + $factor * (0 - $this->expected_result($loserating, $winrating)); - $newwinrating = $winrating + $factor * (1 - $this->expected_result($winrating, $loserating)); - $loser->set_rating($newloserating); - $winner->set_rating($newwinrating); - } - - /** - * Calculates the expected score in favour of the player with rating $a, - * against a player with rating $b - * - * @param float $a - * @param float $b - */ - private function expected_result(float $a, float $b): float { - $exponent = ($b - $a) / 400.0; - return 1.0 / (1.0 + pow(10.0, $exponent)); - } -} diff --git a/classes/rating_system/elo_rating/elo_rating_system_form.php b/classes/rating_system/elo_rating/elo_rating_system_form.php deleted file mode 100755 index 05f35d2..0000000 --- a/classes/rating_system/elo_rating/elo_rating_system_form.php +++ /dev/null @@ -1,81 +0,0 @@ -. - -/** - * This file defines a class used to represent an elo rating system form - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz; - -use moodle_url; -use stdClass; - -defined('MOODLE_INTERNAL') || die(); - -require_once($CFG->libdir . '/formslib.php'); - -/** - * Class elo_rating_system_form - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class elo_rating_system_form extends \moodleform { - - /** @var stdClass $config */ - private stdClass $config; - - /** - * Constructor. - * - * @param stdClass $config - * @param moodle_url $url - */ - public function __construct(stdClass $config, moodle_url $url) { - $this->config = $config; - parent::__construct($url); - } - - /** - * Defines rating system form - */ - public function definition(): void { - $form = $this->_form; - - $form->addElement('text', 'student_k_factor', get_string('student_k_factor', 'capquiz')); - $form->setType('student_k_factor', PARAM_INT); - $form->addRule('student_k_factor', get_string('student_k_factor_specified_rule', 'capquiz'), 'required', null, 'client'); - $form->addRule('student_k_factor', get_string('k_factor_numeric_rule', 'capquiz'), 'numeric', null, 'client'); - $form->setDefault('student_k_factor', $this->config->student_k_factor); - $form->addHelpButton('student_k_factor', 'student_k_factor', 'capquiz'); - - $form->addElement('text', 'question_k_factor', get_string('question_k_factor', 'capquiz')); - $form->setType('question_k_factor', PARAM_INT); - $form->addRule('question_k_factor', get_string('question_k_factor_specified_rule', 'capquiz'), 'required', null, 'client'); - $form->addRule('question_k_factor', get_string('k_factor_numeric_rule', 'capquiz'), 'numeric', null, 'client'); - $form->setDefault('question_k_factor', $this->config->question_k_factor); - $form->addHelpButton('question_k_factor', 'question_k_factor', 'capquiz'); - - $this->add_action_buttons(false); - } -} diff --git a/db/access.php b/db/access.php index 0c22e13..cff5c07 100755 --- a/db/access.php +++ b/db/access.php @@ -18,8 +18,8 @@ * File to define access policies * * @package mod_capquiz - * @author Sebastian S. Gundersen - * @copyright 2018 NTNU + * @author Sebastian Gundersen + * @copyright 2018 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ diff --git a/db/install.xml b/db/install.xml index b5ca101..b62bf97 100755 --- a/db/install.xml +++ b/db/install.xml @@ -1,5 +1,5 @@ - @@ -17,6 +17,12 @@ + + + + + + @@ -138,29 +144,5 @@ - - - - - - - - - - - -
- - - - - - - - - - - -
diff --git a/db/upgrade.php b/db/upgrade.php index bb69056..fd31c34 100755 --- a/db/upgrade.php +++ b/db/upgrade.php @@ -18,12 +18,11 @@ * File to keep track of upgrades to the capquiz plugin * * @package mod_capquiz - * @author Sebastian S. Gundersen - * @copyright 2018 NTNU + * @author Sebastian Gundersen + * @copyright 2024 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -/** /** * Function to upgrade mod_capquiz * @@ -182,16 +181,11 @@ function xmldb_capquiz_upgrade($oldversion) { $dbman->add_key($atable, $aprevqprevrkey); } - $aurfield = new xmldb_field( - 'user_rating_id', XMLDB_TYPE_INTEGER, 11, null, null, null, null); - $aurkey = new xmldb_key( - 'user_rating_id', XMLDB_KEY_FOREIGN, ['user_rating_id'], 'capquiz_user_rating', ['id']); - $aprevurfield = new xmldb_field( - 'user_prev_rating_id', - XMLDB_TYPE_INTEGER, 11, null, null, null, null); - $aprevurkey = new xmldb_key( - 'user_prev_rating_id', - XMLDB_KEY_FOREIGN, ['user_prev_rating_id'], 'capquiz_user_rating', ['id']); + $aurfield = new xmldb_field('user_rating_id', XMLDB_TYPE_INTEGER, 11, null, null, null, null); + $aurkey = new xmldb_key('user_rating_id', XMLDB_KEY_FOREIGN, ['user_rating_id'], 'capquiz_user_rating', ['id']); + $aprevurfield = new xmldb_field('user_prev_rating_id', XMLDB_TYPE_INTEGER, 11, null, null, null, null); + $aprevurkey = new xmldb_key('user_prev_rating_id', XMLDB_KEY_FOREIGN, + ['user_prev_rating_id'], 'capquiz_user_rating', ['id']); if (!$dbman->field_exists($atable, $aurfield)) { $dbman->add_field($atable, $aurfield); @@ -357,5 +351,66 @@ function xmldb_capquiz_upgrade($oldversion) { // Capquiz savepoint reached. upgrade_mod_savepoint(true, 2021021100, 'capquiz'); } + if ($oldversion < 2024101300) { + $table = new xmldb_table('capquiz'); + $field = new xmldb_field('questionselectiontype', XMLDB_TYPE_TEXT, null, null, XMLDB_NOTNULL, null, 'nclosest'); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + $field = new xmldb_field('numquestioncandidates', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '10'); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + $field = new xmldb_field('minquestionsuntilreappearance', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, '0'); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + $field = new xmldb_field('userwinprobability', XMLDB_TYPE_NUMBER, '10, 2', null, XMLDB_NOTNULL, null, '0.75'); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + $field = new xmldb_field('userkfactor', XMLDB_TYPE_NUMBER, '10, 2', null, XMLDB_NOTNULL, null, '32'); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + $field = new xmldb_field('questionkfactor', XMLDB_TYPE_NUMBER, '10, 2', null, XMLDB_NOTNULL, null, '8'); + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + foreach ($DB->get_records('capquiz') as $capquiz) { + $selection = $DB->get_record('capquiz_question_selection', ['capquiz_id' => $capquiz->id]); + if ($selection) { + $config = json_decode($selection->configuration); + if ($selection->strategy === 'N-closest') { + $capquiz->numquestioncandidates = (int)$config->number_of_questions_to_select; + $capquiz->minquestionsuntilreappearance = (int)$config->prevent_same_question_for_turns; + $capquiz->userwinprobability = (float)$config->user_win_probability; + $capquiz->questionselectiontype = 'nclosest'; + } else { + $capquiz->questionselectiontype = 'chronological'; + } + $DB->update_record('capquiz', $capquiz); + $DB->delete_records('capquiz_question_selection', ['id' => $selection->id]); + } + $ratingsystem = $DB->get_record('capquiz_rating_system', ['capquiz_id' => $capquiz->id]); + if ($ratingsystem) { + $config = json_decode($ratingsystem->configuration); + $capquiz->userkfactor = (float)$config->student_k_factor; + $capquiz->questionkfactor = (float)$config->question_k_factor; + $DB->update_record('capquiz', $capquiz); + $DB->delete_records('capquiz_rating_system', ['id' => $ratingsystem->id]); + } + } + $table = new xmldb_table('capquiz_question_selection'); + if ($dbman->table_exists($table)) { + $dbman->drop_table($table); + } + $table = new xmldb_table('capquiz_rating_system'); + if ($dbman->table_exists($table)) { + $dbman->drop_table($table); + } + + upgrade_mod_savepoint(true, 2024101300, 'capquiz'); + } return true; } diff --git a/edit.php b/edit.php deleted file mode 100755 index 7d34ce5..0000000 --- a/edit.php +++ /dev/null @@ -1,42 +0,0 @@ -. - -/** - * Edit capquiz - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz; - -require_once("../../config.php"); -require_once($CFG->libdir . '/formslib.php'); -require_once($CFG->dirroot . '/mod/capquiz/lib.php'); - -$cmid = capquiz_urls::require_course_module_id_param(); -$cm = get_coursemodule_from_id('capquiz', $cmid, 0, false, MUST_EXIST); -require_login($cm->course, false, $cm); -$context = \context_module::instance($cmid); -require_capability('mod/capquiz:instructor', $context); - -$capquiz = new capquiz($cmid); -capquiz_urls::set_page_url($capquiz, capquiz_urls::$urledit); -$bankrenderer = new output\question_bank_renderer($capquiz, $capquiz->renderer()); -$bankview = $bankrenderer->create_view(); -$capquiz->renderer()->display_question_list_view($capquiz); diff --git a/error.php b/error.php deleted file mode 100755 index 9ae7e08..0000000 --- a/error.php +++ /dev/null @@ -1,46 +0,0 @@ -. - -/** - * Error page - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz; - -require_once("../../config.php"); -require_once($CFG->libdir . '/formslib.php'); -require_once($CFG->dirroot . '/mod/capquiz/lib.php'); - -$cmid = capquiz_urls::require_course_module_id_param(); -$cm = get_coursemodule_from_id('capquiz', $cmid, 0, false, MUST_EXIST); -require_login($cm->course, false, $cm); - -$cmid = capquiz_urls::require_course_module_id_param(); -$capquiz = new capquiz($cmid); -if (!$capquiz) { - capquiz_urls::redirect_to_front_page(); -} - -capquiz_urls::set_page_url($capquiz, capquiz_urls::$urlerror); - -echo $capquiz->renderer()->output_renderer()->header(); -echo 'Something went wrong.'; -echo $capquiz->renderer()->output_renderer()->footer(); diff --git a/grade.php b/grade.php deleted file mode 100644 index a4e2e80..0000000 --- a/grade.php +++ /dev/null @@ -1,48 +0,0 @@ -. - -/** - * Displays the entry page into the capquiz - * - * @package mod_capquiz - * @author Sebastian S. Gundersen - * @copyright 2019 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz; - -require_once('../../config.php'); - -require_login(); - -$cmid = required_param('id', PARAM_INT); -$cm = get_coursemodule_from_id('capquiz', $cmid, 0, false, MUST_EXIST); -require_login($cm->course, false, $cm); - -$cmid = capquiz_urls::require_course_module_id_param(); -$capquiz = new capquiz($cmid); -if (!$capquiz) { - capquiz_urls::redirect_to_front_page(); -} - -capquiz_urls::set_page_url($capquiz, capquiz_urls::$urlview); - -if (has_capability('mod/capquiz:instructor', $capquiz->context())) { - redirect(capquiz_urls::view_classlist_url()); -} - -redirect(capquiz_urls::view_url()); diff --git a/index.php b/index.php index 69a1cf8..ff76245 100755 --- a/index.php +++ b/index.php @@ -19,7 +19,7 @@ * * @package mod_capquiz * @author Aleksander Skrede - * @copyright 2018 NTNU + * @copyright 2018 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -32,5 +32,5 @@ $courseid = required_param('id', PARAM_INT); $course = $DB->get_record('course', ['id' => $courseid]); if ($course) { - $PAGE->set_url(capquiz_urls::create_view_url(capquiz_urls::$urlview)); + $PAGE->set_url(capquiz_urls::create_view_url('/mod/capquiz/view.php')); } diff --git a/lang/en/capquiz.php b/lang/en/capquiz.php index 587e20c..d12854e 100755 --- a/lang/en/capquiz.php +++ b/lang/en/capquiz.php @@ -19,7 +19,8 @@ * * @package mod_capquiz * @author Aleksander Skrede - * @copyright 2018 NTNU + * @author Sebastian Gundersen + * @copyright 2024 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ diff --git a/lib.php b/lib.php index a89e2f6..12be9de 100755 --- a/lib.php +++ b/lib.php @@ -20,7 +20,7 @@ * @package mod_capquiz * @author Aleksander Skrede * @author André Storhaug - * @copyright 2018 NTNU + * @copyright 2018 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ @@ -121,11 +121,53 @@ function capquiz_cron(): bool { function capquiz_extend_settings_navigation(settings_navigation $settings, navigation_node $capquiznode): void { global $PAGE, $CFG; + $context = $settings->get_page()->cm->context; + if (!has_capability('mod/capquiz:instructor', $context)) { + return; + } + $cmid = $PAGE->cm->id; + + $capquiznode->add_node(navigation_node::create( + get_string('questions', 'capquiz'), + new moodle_url('/mod/capquiz/view.php', ['id' => $cmid, 'page' => 'questions']), + navigation_node::TYPE_SETTING, + null, + 'capquiz_viewquestionlist', + new pix_icon('i/report', '') + )); + + $capquiznode->add_node(navigation_node::create( + get_string('reports', 'capquiz'), + new moodle_url('/mod/capquiz/view.php', ['id' => $cmid, 'page' => 'report']), + navigation_node::TYPE_SETTING, + null, + 'capquiz_viewreports', + new pix_icon('i/report', '') + )); + + $capquiznode->add_node(navigation_node::create( + get_string('classlist', 'capquiz'), + new moodle_url('/mod/capquiz/view.php', ['id' => $cmid, 'page' => 'classlist']), + navigation_node::TYPE_SETTING, + null, + 'capquiz_viewclasslist', + new pix_icon('i/report', '') + )); + // Require {@link https://github.com/moodle/moodle/blob/master/lib/questionlib.php} // Included here as we only ever want to include this file if we really need to. require_once($CFG->libdir . '/questionlib.php'); question_extend_settings_navigation($capquiznode, $PAGE->cm->context)->trim_if_empty(); + + $capquiznode->add_node(navigation_node::create( + get_string('other_question_lists', 'capquiz'), + new moodle_url('/mod/capquiz/view.php', ['id' => $cmid, 'page' => 'import']), + navigation_node::TYPE_SETTING, + null, + 'capquiz_viewotherquestionlists', + new pix_icon('i/report', '') + )); } /** diff --git a/locallib.php b/locallib.php deleted file mode 100644 index 2d46b01..0000000 --- a/locallib.php +++ /dev/null @@ -1,34 +0,0 @@ -. - -/** - * Library of internal classes and functions for module CAPQuiz - * - * @package mod_capquiz - * @author André Storhaug - * @copyright 2019 Norwegian University of Science and Technology (NTNU) - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -/** - * Base class for all the types of exception we throw. - * - * @author André Storhaug - * @copyright 2019 Norwegian University of Science and Technology (NTNU) - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class capquiz_exception extends moodle_exception { -} diff --git a/mod_form.php b/mod_form.php index bbf9629..67cb068 100755 --- a/mod_form.php +++ b/mod_form.php @@ -19,10 +19,12 @@ * * @package mod_capquiz * @author Aleksander Skrede - * @copyright 2018 NTNU + * @copyright 2018 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +use mod_capquiz\capquiz; + defined('MOODLE_INTERNAL') || die(); require_once($CFG->dirroot . '/course/moodleform_mod.php'); @@ -41,20 +43,180 @@ class mod_capquiz_mod_form extends moodleform_mod { */ public function definition(): void { $form = $this->_form; + $form->addElement('text', 'name', get_string('name'), ['size' => '64']); $form->setType('name', PARAM_TEXT); $form->addRule('name', null, 'required', null, 'client'); $form->addRule('name', get_string('maximumchars', '', 255), 'maxlength', 255, 'client'); - $form->addElement('text', 'default_user_rating', get_string('default_user_rating', 'capquiz')); - $form->setType('default_user_rating', PARAM_INT); - $form->addRule('default_user_rating', get_string('default_rating_specified_rule', 'capquiz'), 'required', null, 'client'); - $form->addRule('default_user_rating', get_string('default_rating_numeric_rule', 'capquiz'), 'numeric', null, 'client'); - $form->setDefault('default_user_rating', 1200); - $this->standard_intro_elements(get_string('description')); + + if (!empty($this->_cm)) { + $capquiz = new capquiz((int)$this->_cm->id); + + // Grading + $form->addElement('header', 'configuregradingheader', get_string('grading', 'capquiz')); + + $form->addElement('text', 'default_user_rating', get_string('default_user_rating', 'capquiz')); + $form->setType('default_user_rating', PARAM_INT); + $form->setDefault('default_user_rating', $capquiz->get('default_user_rating')); + $form->addRule('default_user_rating', get_string('default_user_rating_required', 'capquiz'), 'required', null, 'client'); + $form->addRule('default_user_rating', get_string('default_rating_numeric_rule', 'capquiz'), 'numeric', null, 'client'); + + $qlist = $capquiz->question_list(); + if ($qlist) { + for ($star = 1; $star <= $qlist->max_stars(); $star++) { + $input = "star_rating_$star"; + $text = get_string('level_rating', 'capquiz', $star); + $elements = []; + $elements[] = $form->createElement('text', $input, $text); + if ($star > 1) { + $elements[] = $form->createElement('submit', "delstarbutton$star", get_string('delete_star', 'capquiz')); + } + $form->addGroup($elements, "star_group_$star", $text, [''], false); + $form->setType($input, PARAM_INT); + $form->setDefault($input, $qlist->star_rating($star)); + } + $form->addElement('submit', 'addstarbutton', get_string('add_star', 'capquiz')); + } + + $form->addElement('text', 'starstopass', get_string('stars_to_pass', 'capquiz')); + $form->setType('starstopass', PARAM_INT); + $form->setDefault('starstopass', $capquiz->get('stars_to_pass')); + $form->addRule('starstopass', get_string('stars_to_pass_required', 'capquiz'), 'required', null, 'client'); + + $form->addElement('date_time_selector', 'timedue', get_string('due_time_grading', 'capquiz')); + $form->setType('timedue', PARAM_INT); + $oneweek = 60 * 60 * 24 * 7; + $form->setDefault('timedue', $capquiz->get('timedue') ?: time() + $oneweek); + + // Question selection + $form->addElement('header', 'configurematchmakingstrategyheader', get_string('matchmaking', 'capquiz')); + $selectedindex = match ($capquiz->record->questionselectiontype) { + 'nclosest' => 0, + 'chronological' => 1, + default => -1, + }; + $questionselectiontypes = [ + $form->createElement('radio', 'questionselectiontype', '', get_string('n_closest', 'capquiz'), 0, ['N-closest']), + $form->createElement('radio', 'questionselectiontype', '', get_string('chronological', 'capquiz'), 1, ['Chronological']), + ]; + $form->addGroup($questionselectiontypes, 'radioar', '', '
', false); + if ($selectedindex >= 0) { + $form->setDefault('questionselectiontype', $selectedindex); + } + // N-closest + $form->addElement('text', 'numquestioncandidates', get_string('number_of_questions_to_select', 'capquiz')); + $form->setType('numquestioncandidates', PARAM_INT); + $form->setDefault('numquestioncandidates', $capquiz->record->numquestioncandidates); + $form->addRule('numquestioncandidates', get_string('number_of_questions_to_select_required', 'capquiz'), 'required', null, 'client'); + $form->addHelpButton('numquestioncandidates', 'number_of_questions_to_select', 'capquiz'); + $form->hideIf('numquestioncandidates', 'questionselectiontype', 'ne', 0); + + $form->addElement('text', 'minquestionsuntilreappearance', get_string('prevent_question_n_times', 'capquiz')); + $form->setType('minquestionsuntilreappearance', PARAM_INT); + $form->setDefault('minquestionsuntilreappearance', $capquiz->record->minquestionsuntilreappearance); + $form->addRule('minquestionsuntilreappearance', get_string('field_required', 'capquiz'), 'required', null, 'client'); + $form->addHelpButton('minquestionsuntilreappearance', 'prevent_question_n_times', 'capquiz'); + $form->hideIf('minquestionsuntilreappearance', 'questionselectiontype', 'ne', 0); + + $form->addElement('text', 'userwinprobability', get_string('user_win_probability', 'capquiz')); + $form->setType('userwinprobability', PARAM_FLOAT); + $form->setDefault('userwinprobability', $capquiz->record->userwinprobability); + $form->addRule('userwinprobability', get_string('user_win_probability_required', 'capquiz'), 'required', null, 'client'); + $form->addHelpButton('userwinprobability', 'user_win_probability', 'capquiz'); + $form->hideIf('userwinprobability', 'questionselectiontype', 'ne', 0); + + // Rating system + $form->addElement('header', 'configureratingsystemheader', get_string('rating_system', 'capquiz')); + + $form->addElement('text', 'userkfactor', get_string('student_k_factor', 'capquiz')); + $form->setType('userkfactor', PARAM_FLOAT); + $form->addRule('userkfactor', get_string('student_k_factor_specified_rule', 'capquiz'), 'required', null, 'client'); + $form->addRule('userkfactor', get_string('k_factor_numeric_rule', 'capquiz'), 'numeric', null, 'client'); + $form->setDefault('userkfactor', $capquiz->record->userkfactor); + $form->addHelpButton('userkfactor', 'student_k_factor', 'capquiz'); + + $form->addElement('text', 'questionkfactor', get_string('question_k_factor', 'capquiz')); + $form->setType('questionkfactor', PARAM_FLOAT); + $form->addRule('questionkfactor', get_string('question_k_factor_specified_rule', 'capquiz'), 'required', null, 'client'); + $form->addRule('questionkfactor', get_string('k_factor_numeric_rule', 'capquiz'), 'numeric', null, 'client'); + $form->setDefault('questionkfactor', $capquiz->record->questionkfactor); + $form->addHelpButton('questionkfactor', 'question_k_factor', 'capquiz'); + } + $this->standard_coursemodule_elements(); $this->add_action_buttons(); } + /** + * Validate the data from the form. + * + * @param array $data array of ("fieldname"=>value) of submitted data + * @param array $files array of uploaded files "element_name"=>tmp_file_path + * @return array of "element_name"=>"error_description" if there are errors, + * or an empty array if everything is OK (true allowed for backwards compatibility too). + */ + public function validation($data, $files): array { + $errors = parent::validation($data, $files); + if (empty($data['default_user_rating'])) { + $errors['default_user_rating'] = get_string('default_user_rating_required', 'capquiz'); + } + if (empty($data['starstopass']) || $data['starstopass'] < 0 || $data['starstopass'] > 5) { + $errors['starstopass'] = get_string('stars_to_pass_required', 'capquiz'); + } + $nclosestvalidation = $data['strategy'] === 0; + if ($nclosestvalidation) { + if (empty($data['user_win_probability'])) { + $errors['user_win_probability'] = get_string('user_win_probability_required', 'capquiz'); + } + if (empty($data['number_of_questions'])) { + $errors['number_of_questions'] = get_string('number_of_questions_to_select_required', 'capquiz'); + } + if (empty($data['prevent_same_question_for_turns'])) { + $errors['prevent_same_question_for_turns'] = get_string('field_required', 'capquiz'); + } + } + return $errors; + } + + /** + * Process data after default module form processing. + * + * @param stdClass $data + * @return void + */ + public function data_postprocessing($data): void { + parent::data_postprocessing($data); + if (empty($this->_cm->id)) { + return; + } + $capquiz = new capquiz((int)$this->_cm->id); + $star = 1; + $ratings = []; + while (isset($data->{"star_rating_$star"})) { + if (!isset($data->{"delstarbutton$star"})) { + $ratings[] = (int)$data->{"star_rating_$star"}; + } + $star++; + } + if (isset($data->addstarbutton)) { + $ratings[] = end($ratings) + 100; + } + if ($data->default_user_rating) { + $capquiz->set('default_user_rating', $data->default_user_rating); + $capquiz->save(); + } + $qlist = $capquiz->question_list(); + $qlist?->set_star_ratings($ratings); + if ($data->starstopass) { + $capquiz->set('stars_to_pass', $data->starstopass); + $capquiz->save(); + } + if ($data->timedue) { + $capquiz->set('timedue', $data->timedue); + $capquiz->save(); + } + } + } diff --git a/report/attempts/report.php b/report/attempts/report.php index 285f79e..3e687c7 100644 --- a/report/attempts/report.php +++ b/report/attempts/report.php @@ -79,9 +79,9 @@ public function display($capquiz, $cm, $course, $download): bool { $this->options, $studentsjoins, $questions, $this->options->get_url()); $filename = capquiz_report_download_filename(get_string('attemptsfilename', 'capquizreport_attempts'), - $courseshortname, $capquiz->name()); + $courseshortname, $capquiz->get('name')); - $table->is_downloading($this->options->download, $filename, $courseshortname . ' ' . format_string($capquiz->name())); + $table->is_downloading($this->options->download, $filename, $courseshortname . ' ' . format_string($capquiz->get('name'))); if ($table->is_downloading()) { raise_memory_limit(MEMORY_EXTRA); @@ -101,7 +101,7 @@ public function display($capquiz, $cm, $course, $download): bool { // $this->process_actions($capquiz, $cm, $studentsjoins, $this->options->get_url()); // phpcs:enable - $hasquestions = capquiz_has_questions($capquiz->id()); + $hasquestions = capquiz_has_questions($capquiz->get('id')); // Start output. if (!$table->is_downloading()) { // Only print headers if not asked to download data. diff --git a/report/attemptsreport.php b/report/attemptsreport.php index 9fe5bef..aed56fc 100644 --- a/report/attemptsreport.php +++ b/report/attemptsreport.php @@ -132,7 +132,7 @@ protected function print_standard_header_and_messages(stdClass $cm, stdClass $co if (!$hasquestions) { echo capquiz_no_questions_message($capquiz, $cm, $this->context); - } else if (!$capquiz->is_published()) { + } else if (!$capquiz->get('published')) { echo capquiz_not_published_message($capquiz, $cm, $this->context); } else if (!$hasstudents) { echo $OUTPUT->notification(get_string('nostudentsyet')); diff --git a/report/attemptsreport_table.php b/report/attemptsreport_table.php index f474ea4..7295464 100644 --- a/report/attemptsreport_table.php +++ b/report/attemptsreport_table.php @@ -27,7 +27,6 @@ use coding_exception; use core\context; -use core\context\module; use core\dml\sql_join; use html_writer; use moodle_url; diff --git a/report/questions/questions_table.php b/report/questions/questions_table.php index 6f39675..3e65a27 100644 --- a/report/questions/questions_table.php +++ b/report/questions/questions_table.php @@ -27,11 +27,9 @@ use core\context; use core\dml\sql_join; -use mod_capquiz\report\capquiz_attempts_report; use mod_capquiz\report\capquiz_attempts_report_options; use mod_capquiz\report\capquiz_attempts_report_table; use moodle_url; -use quiz_responses_options; use stdClass; defined('MOODLE_INTERNAL') || die(); diff --git a/report/questions/report.php b/report/questions/report.php index 6305e22..9a5e986 100644 --- a/report/questions/report.php +++ b/report/questions/report.php @@ -82,9 +82,9 @@ public function display($capquiz, $cm, $course, $download): bool { $this->options, $studentsjoins, $questions, $this->options->get_url()); $filename = capquiz_report_download_filename(get_string('questionsfilename', 'capquizreport_questions'), - $courseshortname, $capquiz->name()); + $courseshortname, $capquiz->get('name')); - $table->is_downloading($this->options->download, $filename, $courseshortname . ' ' . format_string($capquiz->name())); + $table->is_downloading($this->options->download, $filename, $courseshortname . ' ' . format_string($capquiz->get('name'))); if ($table->is_downloading()) { raise_memory_limit(MEMORY_EXTRA); } @@ -98,7 +98,7 @@ public function display($capquiz, $cm, $course, $download): bool { $hasstudents = $DB->record_exists_sql($sql, $studentsjoins->params); } - $hasquestions = capquiz_has_questions($capquiz->id()); + $hasquestions = capquiz_has_questions($capquiz->get('id')); // Start output. if (!$table->is_downloading()) { // Only print headers if not asked to download data. @@ -181,7 +181,7 @@ protected function print_standard_header_and_messages($cm, $course, capquiz $cap $this->print_header_and_tabs($cm, $course, $capquiz, $this->mode); if (!$hasquestions) { echo capquiz_no_questions_message($capquiz, $cm, $this->context); - } else if (!$capquiz->is_published()) { + } else if (!$capquiz->get('published')) { echo capquiz_not_published_message($capquiz, $cm, $this->context); } else if (!$hasstudents) { echo $OUTPUT->notification(get_string('nostudentsyet')); diff --git a/report/report.php b/report/report.php index 61ac4fd..a9faeda 100644 --- a/report/report.php +++ b/report/report.php @@ -75,7 +75,7 @@ public function canview(context_module $contextmodule): bool { */ public function print_header_and_tabs(stdClass $cm, stdClass $course, capquiz $capquiz, string $reportmode = 'attempts'): void { global $PAGE, $OUTPUT; - $PAGE->set_title($capquiz->name()); + $PAGE->set_title($capquiz->get('name')); $PAGE->set_heading($course->fullname); $context = context_module::instance($cm->id); echo $OUTPUT->heading(format_string( diff --git a/report/reportfactory.php b/report/reportfactory.php index 8a49ab5..b5ecebd 100644 --- a/report/reportfactory.php +++ b/report/reportfactory.php @@ -25,8 +25,6 @@ namespace mod_capquiz\report; -use capquiz_exception; - defined('MOODLE_INTERNAL') || die(); require_once($CFG->dirroot . '/mod/capquiz/locallib.php'); @@ -62,13 +60,11 @@ protected static function class_for_type(string $type): string { $file = $CFG->dirroot . '/mod/capquiz/report/' . $type . '/report.php'; $class = "capquizreport_{$typelc}\\capquizreport_{$typelc}_report"; if (!is_readable($file)) { - throw new capquiz_exception('capquiz_report_factory: unknown report type ' . $type); + throw new \moodle_exception("capquiz_report_factory: unknown report type $type"); } include_once($file); - if (!class_exists($class)) { - throw new capquiz_exception('capquiz_report_factory: report type ' . $type . - ' does not define the expected class ' . $class); + throw new \moodle_exception("capquiz_report_factory: report type $type doesn't define expected class $class"); } return $class; } diff --git a/report/reportlib.php b/report/reportlib.php index f7480e6..25fe580 100644 --- a/report/reportlib.php +++ b/report/reportlib.php @@ -110,7 +110,7 @@ function capquiz_report_get_questions(capquiz $capquiz): array { AND q.length > 0 ORDER BY ca.slot'; - $qsbyslot = $DB->get_records_sql($sql, [$capquiz->id()]); + $qsbyslot = $DB->get_records_sql($sql, [$capquiz->get('id')]); $number = 1; foreach ($qsbyslot as $question) { $question->number = $number; @@ -151,7 +151,7 @@ function capquiz_report_num_attempt(capquiz $capquiz): int { JOIN {question_usages} qu ON qu.id = cu.question_usage_id JOIN {question_attempts} qa ON qa.questionusageid = qu.id AND qa.slot = ca.slot JOIN {capquiz_question} cq ON cq.id = ca.question_id'; - return $DB->count_records_sql($sql, ['capquizid' => $capquiz->id()]); + return $DB->count_records_sql($sql, ['capquizid' => $capquiz->get('id')]); } /** @@ -167,7 +167,8 @@ function capquiz_no_questions_message($quiz, $cm, module $context): string { global $OUTPUT; $output = $OUTPUT->notification(get_string('noquestions', 'quiz')); if (has_capability('mod/capquiz:manage', $context)) { - $output .= $OUTPUT->single_button(capquiz_urls::view_question_list_url(), get_string('editquiz', 'quiz'), 'get'); + $url = new moodle_url('/mod/capquiz/view.php', ['id' => $cm->id, 'page' => 'questions']); + $output .= $OUTPUT->single_button($url, get_string('editquiz', 'quiz'), 'get'); } return $output; } @@ -185,7 +186,7 @@ function capquiz_not_published_message($quiz, $cm, module $context): string { global $OUTPUT; $output = $OUTPUT->notification(get_string('question_list_not_published', 'capquiz')); if (has_capability('mod/capquiz:manage', $context)) { - $output .= $OUTPUT->single_button(capquiz_urls::view_url(), get_string('question_list_settings', 'capquiz'), 'get'); + $output .= $OUTPUT->single_button(new moodle_url('/mod/capquiz/view.php', ['id' => $cm->id]), get_string('question_list_settings', 'capquiz'), 'get'); } return $output; } diff --git a/templates/button.mustache b/templates/button.mustache deleted file mode 100755 index f685762..0000000 --- a/templates/button.mustache +++ /dev/null @@ -1,31 +0,0 @@ -{{! - 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 . -}} -{{! - @template mod_capquiz/button - - Example context (json): - { - "method": "post", - "url": "", - "primary": true, - "tooltip": "A button.", - "label": "Button" - } -}} -{{#button}} - {{>core/single_button}} -{{/button}} diff --git a/templates/configure_grading.mustache b/templates/configure_grading.mustache deleted file mode 100644 index 4f41bc7..0000000 --- a/templates/configure_grading.mustache +++ /dev/null @@ -1,26 +0,0 @@ -{{! - 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 . -}} -{{! - @template mod_capquiz/configure_badge_rating - - Example context (json): - { - "form": "raw html for the form" - } -}} -

{{#str}} configure_grading, capquiz {{/str}}

-{{{ rating_form }}} diff --git a/templates/matchmaking_configuration.mustache b/templates/matchmaking_configuration.mustache deleted file mode 100755 index 3e52043..0000000 --- a/templates/matchmaking_configuration.mustache +++ /dev/null @@ -1,27 +0,0 @@ -{{! - 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 . -}} -{{! - @template mod_capquiz/matchmaking_configuration - - Example context (json): - { - "strategy": "N-closest", - "form": "raw html for the form" - } -}} -

{{#str}} configure, capquiz {{/str}} {{strategy}}

-{{{ form }}} diff --git a/templates/matchmaking_selection_strategy.mustache b/templates/matchmaking_selection_strategy.mustache deleted file mode 100755 index e364c1b..0000000 --- a/templates/matchmaking_selection_strategy.mustache +++ /dev/null @@ -1,26 +0,0 @@ -{{! - 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 . -}} -{{! - @template mod_capquiz/matchmaking_selection_strategy - - Example context (json): - { - "form": "raw html for the configuration form" - } -}} -

{{#str}} choose_selection_strategy, capquiz {{/str}}

-{{{ form }}} diff --git a/templates/rating_system_configuration.mustache b/templates/rating_system_configuration.mustache deleted file mode 100755 index 0e93152..0000000 --- a/templates/rating_system_configuration.mustache +++ /dev/null @@ -1,27 +0,0 @@ -{{! - 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 . -}} -{{! - @template mod_capquiz/rating_system_configuration - - Example context (json): - { - "strategy": "Elo", - "form": "raw html for the form" - } -}} -

{{#str}} configure, capquiz {{/str}} {{strategy}}

-{{{ form }}} diff --git a/templates/rating_system_selection.mustache b/templates/rating_system_selection.mustache deleted file mode 100755 index 777b8af..0000000 --- a/templates/rating_system_selection.mustache +++ /dev/null @@ -1,26 +0,0 @@ -{{! - 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 . -}} -{{! - @template mod_capquiz/rating_system_selection - - Example context (json): - { - "form": "raw html for the form" - } -}} -

{{#str}} choose_rating_system, capquiz {{/str}}

-{{{ form }}} diff --git a/templates/unauthorized.mustache b/templates/unauthorized.mustache deleted file mode 100755 index 49a74aa..0000000 --- a/templates/unauthorized.mustache +++ /dev/null @@ -1,24 +0,0 @@ -{{! - 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 . -}} -{{! - @template mod_capquiz/unauthorized - - Example context (json): - { - } -}} -

{{#str}}need_to_log_in, capquiz{{/str}}

\ No newline at end of file diff --git a/version.php b/version.php index 1568516..4df50d4 100755 --- a/version.php +++ b/version.php @@ -19,17 +19,17 @@ * * @package mod_capquiz * @author Aleksander Skrede - * @author Sebastian S. Gundersen + * @author Sebastian Gundersen * @author André Storhaug - * @copyright 2019 NTNU + * @copyright 2024 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ defined('MOODLE_INTERNAL') || die(); -$plugin->version = 2024100700; +$plugin->version = 2024101300; $plugin->requires = 2022041901; // 4.0 $plugin->cron = 0; $plugin->component = 'mod_capquiz'; $plugin->maturity = MATURITY_STABLE; -$plugin->release = '0.8.0'; +$plugin->release = '0.9.0'; diff --git a/view.php b/view.php index 6755019..5f5314a 100755 --- a/view.php +++ b/view.php @@ -18,37 +18,82 @@ * Displays the correct view, depending on your role, acts as an entrypoint to the capquiz * * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU + * @author Sebastian Gundersen + * @copyright 2024 Norwegian University of Science and Technology (NTNU) * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ -namespace mod_capquiz; +use mod_capquiz\capquiz; +use mod_capquiz\capquiz_question_engine; +use mod_capquiz\output\renderer; -require_once('../../config.php'); +require_once(__DIR__ . '/../../config.php'); + +global $CFG; + +require_once($CFG->libdir . '/formslib.php'); require_once($CFG->dirroot . '/question/editlib.php'); require_once($CFG->dirroot . '/mod/capquiz/lib.php'); -$cmid = capquiz_urls::require_course_module_id_param(); +global $PAGE; + +$currentpage = optional_param('page', null, PARAM_ALPHA); +$cmid = optional_param('id', 0, PARAM_INT); +if (!$cmid) { + $cmid = required_param('cmid', PARAM_INT); +} $cm = get_coursemodule_from_id('capquiz', $cmid, 0, false, MUST_EXIST); require_login($cm->course, false, $cm); +$context = context_module::instance($cmid); -$capquiz = new capquiz($cmid); -$renderer = $capquiz->renderer(); +$PAGE->set_context($context); +$PAGE->set_cm($cm); +$PAGE->set_pagelayout('incourse'); +$pageurl = new moodle_url('/mod/capquiz/view.php', ['id' => $cmid]); +if (!empty($currentpage)) { + $pageurl->param('page', $currentpage); +} +$PAGE->set_url($pageurl); + +/** @var renderer $renderer */ +$renderer = $PAGE->get_renderer('mod_capquiz'); -capquiz_urls::set_page_url($capquiz, capquiz_urls::$urlview); +if (has_capability('mod/capquiz:instructor', $context)) { + $capquiz = new capquiz($cmid); + switch ($currentpage) { + case 'createquestionlist': + if ($capquiz->question_list()) { + redirect(new moodle_url('/mod/capquiz/view.php', ['id' => $cmid])); + } + $renderer->display_question_list_create_view($capquiz); + break; + case 'questions': + $renderer->display_question_list_view($capquiz); + break; + case 'report': + $renderer->display_report($capquiz); + break; + case 'classlist': + $renderer->display_leaderboard($capquiz); + break; + case 'import': + $renderer->display_import($capquiz); + break; + default: + if ($capquiz->question_list()) { + $renderer->display_instructor_dashboard($capquiz); + } else { + $renderer->display_choose_question_list_view($cmid); + } + break; + } +} -if (has_capability('mod/capquiz:instructor', $capquiz->context())) { - if ($capquiz->has_question_list()) { - $renderer->display_instructor_dashboard($capquiz); - } else { - $renderer->display_choose_question_list_view(); +if (has_capability('mod/capquiz:student', $context)) { + $capquiz = new capquiz($cmid); + (new capquiz_question_engine($capquiz))->delete_invalid_attempt($capquiz->user()); + if (!$capquiz->is_grading_completed()) { + capquiz_update_grades($capquiz->to_record()); } -} else { - require_capability('mod/capquiz:student', $capquiz->context()); - // Question engine is null if the quiz is not published. - $qengine = $capquiz->question_engine($capquiz->user()); - $qengine?->delete_invalid_attempt($capquiz->user()); - $capquiz->update_grades(); $renderer->display_question_attempt_view($capquiz); } diff --git a/view_classlist.php b/view_classlist.php deleted file mode 100755 index e28c451..0000000 --- a/view_classlist.php +++ /dev/null @@ -1,40 +0,0 @@ -. - -/** - * Display leaderboard - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz; - -require_once('../../config.php'); -require_once($CFG->dirroot . '/question/editlib.php'); -require_once($CFG->dirroot . '/mod/capquiz/lib.php'); - -$cmid = capquiz_urls::require_course_module_id_param(); -$cm = get_coursemodule_from_id('capquiz', $cmid, 0, false, MUST_EXIST); -require_login($cm->course, false, $cm); -$context = \context_module::instance($cmid); -require_capability('mod/capquiz:instructor', $context); - -$capquiz = new capquiz($cmid); -capquiz_urls::set_page_url($capquiz, capquiz_urls::$urledit); -$capquiz->renderer()->display_leaderboard($capquiz); diff --git a/view_create_question_list.php b/view_create_question_list.php deleted file mode 100755 index e7d9cd8..0000000 --- a/view_create_question_list.php +++ /dev/null @@ -1,47 +0,0 @@ -. - -/** - * Displays the create_question_list view - * - * @package mod_capquiz - * @author Sebastian S. Gundersen - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz; - -require_once('../../config.php'); -require_once($CFG->dirroot . '/question/editlib.php'); -require_once($CFG->dirroot . '/mod/capquiz/lib.php'); - -$cmid = capquiz_urls::require_course_module_id_param(); -$cm = get_coursemodule_from_id('capquiz', $cmid, 0, false, MUST_EXIST); -require_login($cm->course, false, $cm); -$context = \context_module::instance($cmid); -require_capability('mod/capquiz:instructor', $context); - -$cmid = capquiz_urls::require_course_module_id_param(); -$capquiz = new capquiz($cmid); -capquiz_urls::set_page_url($capquiz, capquiz_urls::$urlviewcreateqlist); - -$renderer = $capquiz->renderer(); -if ($capquiz->has_question_list()) { - $renderer->display_instructor_dashboard($capquiz); -} else { - $renderer->display_question_list_create_view($capquiz); -} diff --git a/view_grading.php b/view_grading.php deleted file mode 100644 index 8863fc6..0000000 --- a/view_grading.php +++ /dev/null @@ -1,40 +0,0 @@ -. - -/** - * Displays the grading view - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz; - -require_once('../../config.php'); -require_once($CFG->dirroot . '/question/editlib.php'); -require_once($CFG->dirroot . '/mod/capquiz/lib.php'); - -$cmid = capquiz_urls::require_course_module_id_param(); -$cm = get_coursemodule_from_id('capquiz', $cmid, 0, false, MUST_EXIST); -require_login($cm->course, false, $cm); -$context = \context_module::instance($cmid); -require_capability('mod/capquiz:instructor', $context); - -$capquiz = new capquiz($cmid); -capquiz_urls::set_page_url($capquiz, capquiz_urls::$urlviewgrading); -$capquiz->renderer()->display_grading_configuration($capquiz); diff --git a/view_import.php b/view_import.php deleted file mode 100644 index 922cf25..0000000 --- a/view_import.php +++ /dev/null @@ -1,40 +0,0 @@ -. - -/** - * Displays the import view - * - * @package mod_capquiz - * @author Sebastian S. Gundersen - * @copyright 2019 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz; - -require_once('../../config.php'); -require_once($CFG->dirroot . '/question/editlib.php'); -require_once($CFG->dirroot . '/mod/capquiz/lib.php'); - -$cmid = capquiz_urls::require_course_module_id_param(); -$cm = get_coursemodule_from_id('capquiz', $cmid, 0, false, MUST_EXIST); -require_login($cm->course, false, $cm); -$context = \context_module::instance($cmid); -require_capability('mod/capquiz:instructor', $context); - -$capquiz = new capquiz($cmid); -capquiz_urls::set_page_url($capquiz, capquiz_urls::$urledit); -$capquiz->renderer()->display_import($capquiz); diff --git a/view_rating_system.php b/view_rating_system.php deleted file mode 100644 index 7d005ad..0000000 --- a/view_rating_system.php +++ /dev/null @@ -1,40 +0,0 @@ -. - -/** - * Displays the rating_system view - * - * @package mod_capquiz - * @author Aleksander Skrede - * @copyright 2018 NTNU - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz; - -require_once('../../config.php'); -require_once($CFG->dirroot . '/question/editlib.php'); -require_once($CFG->dirroot . '/mod/capquiz/lib.php'); - -$cmid = capquiz_urls::require_course_module_id_param(); -$cm = get_coursemodule_from_id('capquiz', $cmid, 0, false, MUST_EXIST); -require_login($cm->course, false, $cm); -$context = \context_module::instance($cmid); -require_capability('mod/capquiz:instructor', $context); - -$capquiz = new capquiz($cmid); -capquiz_urls::set_page_url($capquiz, capquiz_urls::$urlviewratingsystemconfig); -$capquiz->renderer()->display_rating_system_configuration($capquiz); diff --git a/view_report.php b/view_report.php deleted file mode 100644 index e20aca5..0000000 --- a/view_report.php +++ /dev/null @@ -1,41 +0,0 @@ -. - -/** - * Displays capquiz report - * - * @package mod_capquiz - * @author André Storhaug - * @copyright 2019 Norwegian University of Science and Technology (NTNU) - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace mod_capquiz; - -require_once('../../config.php'); -require_once($CFG->dirroot . '/question/editlib.php'); -require_once($CFG->dirroot . '/mod/capquiz/lib.php'); - -$cmid = capquiz_urls::require_course_module_id_param(); -$cm = get_coursemodule_from_id('capquiz', $cmid, 0, false, MUST_EXIST); -require_login($cm->course, false, $cm); -$context = \context_module::instance($cmid); -require_capability('mod/capquiz:instructor', $context); - -$cmid = capquiz_urls::require_course_module_id_param(); -$capquiz = new capquiz($cmid); -capquiz_urls::set_page_url($capquiz, capquiz_urls::$urledit); -$capquiz->renderer()->display_report($capquiz);