diff --git a/lib/emitter/src/Controller.php b/lib/emitter/src/Controller.php index 93b6271606d899b37563b684014e1fe02b2dfa1f..7bd641ff739bd58eafa4cafc6807f2af8e61324b 100755 --- a/lib/emitter/src/Controller.php +++ b/lib/emitter/src/Controller.php @@ -29,7 +29,8 @@ class Controller extends PhpObj { 'module_viewed' => 'ModuleViewed', 'attempt_started' => 'AttemptStarted', 'attempt_abandoned' => 'AttemptCompleted', - 'attempt_completed' => 'AttemptCompleted', + 'attempt_submitted' => 'AttemptCompleted', + 'attempt_reviewed' => 'AttemptReviewed', 'attempt_question_completed' => 'QuestionAnswered', 'user_loggedin' => 'UserLoggedin', 'user_loggedout' => 'UserLoggedout', diff --git a/lib/emitter/src/Events/AttemptReviewed.php b/lib/emitter/src/Events/AttemptReviewed.php new file mode 100644 index 0000000000000000000000000000000000000000..8a061efa5a1d0f813c54ca8bf5b2557d6710a725 --- /dev/null +++ b/lib/emitter/src/Events/AttemptReviewed.php @@ -0,0 +1,60 @@ +<?php +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see <http://www.gnu.org/licenses/>. + +namespace XREmitter\Events; + +defined('MOODLE_INTERNAL') || die(); + +class AttemptReviewed extends Event { + protected static $verbdisplay = [ + 'en' => 'reviewed' + ]; + + /** + * Reads data for an event. + * @param [String => Mixed] $opts + * @return [String => Mixed] + * @override Event + */ + public function read(array $opts) { + return array_merge_recursive(parent::read($opts), [ + 'verb' => [ + 'id' => 'http://activitystrea.ms/schema/1.0/review', + 'display' => $this->read_verb_display($opts), + ], + 'object' => [ + 'id' => $opts['attempt_url'], + 'definition' => [ + 'type' => $opts['attempt_type'], + 'name' => [ + $opts['context_lang'] => $opts['attempt_name'], + ], + 'extensions' => [ + $opts['attempt_ext_key'] => $opts['attempt_ext'] + ], + ], + ], + 'context' => [ + 'contextActivities' => [ + 'grouping' => [ + $this->read_course($opts), + $this->read_module($opts), + ], + ], + ], + ]); + } +} diff --git a/lib/expander/src/Controller.php b/lib/expander/src/Controller.php index 8a4f5b678ea446a4e0ecb19a8b2a4eb944fc7ba9..a4484726f663ed0217c3f6a7183e733639ad2b09 100644 --- a/lib/expander/src/Controller.php +++ b/lib/expander/src/Controller.php @@ -50,6 +50,7 @@ class Controller extends PhpObj { '\mod_quiz\event\attempt_preview_started' => 'AttemptEvent', '\mod_quiz\event\attempt_reviewed' => 'AttemptEvent', '\mod_quiz\event\attempt_viewed' => 'AttemptEvent', + '\mod_quiz\event\attempt_submitted' => 'AttemptEvent', '\core\event\user_loggedin' => 'Event', '\core\event\user_loggedout' => 'Event', '\mod_assign\event\submission_graded' => 'AssignmentGraded', diff --git a/lib/translator/src/Controller.php b/lib/translator/src/Controller.php index 5fc142a208e19fc84ee95776cc4acb6df57b4d8a..41b5960814ec9a04ab863e381df669f7c868b8fe 100644 --- a/lib/translator/src/Controller.php +++ b/lib/translator/src/Controller.php @@ -51,6 +51,7 @@ class Controller extends PhpObj { '\mod_quiz\event\attempt_preview_started' => 'AttemptStarted', '\mod_quiz\event\attempt_reviewed' => ['AttemptReviewed', 'QuestionSubmitted'], '\mod_quiz\event\attempt_viewed' => 'ModuleViewed', + '\mod_quiz\event\attempt_submitted' => ['AttemptSubmitted', 'QuestionSubmitted'], '\core\event\user_loggedin' => 'UserLoggedin', '\core\event\user_loggedout' => 'UserLoggedout', '\mod_assign\event\submission_graded' => 'AssignmentGraded', diff --git a/lib/translator/src/Events/AttemptSubmitted.php b/lib/translator/src/Events/AttemptSubmitted.php new file mode 100644 index 0000000000000000000000000000000000000000..44b042e6d46c5d229bce4309422476f9b7e894da --- /dev/null +++ b/lib/translator/src/Events/AttemptSubmitted.php @@ -0,0 +1,83 @@ +<?php +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see <http://www.gnu.org/licenses/>. + +namespace MXTranslator\Events; + +defined('MOODLE_INTERNAL') || die(); + +class AttemptSubmitted extends AttemptStarted { + /** + * Reads data for an event. + * @param [String => Mixed] $opts + * @return [String => Mixed] + * @override AttemtStarted + */ + public function read(array $opts) { + if (isset($opts['attempt']->timefinish)) { + $seconds = $opts['attempt']->timefinish - $opts['attempt']->timestart; + $duration = "PT".(string) $seconds."S"; + } else { + $duration = "PT0S"; + } + + $scoreraw = isset($opts['attempt']->sumgrades) ? $opts['attempt']->sumgrades : 0; + $scoremin = (float) ($opts['grade_items']->grademin ?: 0); + $scoremax = (float) ($opts['grade_items']->grademax ?: 0); + $scorepass = (float) ($opts['grade_items']->gradepass ?: null); + $success = false; + // If there is no passing score then success is unknown. + if ($scorepass == null) { + $success = null; + } else if ($scoreraw >= $scorepass) { + $success = true; + } + + // It's possible to configure Moodle quizzes such that you can score higher than the maximum grade. + // This is not allowed by xAPI, so cap the raw at the min/max. + if ($scoreraw > $scoremax) { + $scoreraw = $scoremax; + } + if ($scoreraw < $scoremin) { + $scoreraw = $scoremin; + } + + // Calculate scaled score as the distance from zero towards the max (or min for negative scores). + if ($scoreraw >= 0) { + $scorescaled = $scoreraw / $scoremax; + } else { + $scorescaled = $scoreraw / $scoremin; + } + + // Determine if the attempt was marked finished. + if (isset($opts['attempt']->state)) { + $completedstate = $opts['attempt']->state === 'finished'; + } else { + $completedstate = false; + } + + return [array_merge(parent::read($opts)[0], [ + 'recipe' => 'attempt_completed', + 'attempt_score_raw' => $scoreraw, + 'attempt_score_min' => $scoremin, + 'attempt_score_max' => $scoremax, + 'attempt_score_scaled' => $scorescaled, + 'attempt_success' => $success, + 'attempt_completed' => $completedstate, + 'attempt_duration' => $duration, + ])]; + } + +} diff --git a/tests/attempt_submitted_test.php b/tests/attempt_submitted_test.php new file mode 100644 index 0000000000000000000000000000000000000000..33614bda7b1df3ca1aa464e92d24e609dba0302a --- /dev/null +++ b/tests/attempt_submitted_test.php @@ -0,0 +1,29 @@ +<?php +// This file is part of Moodle - http://moodle.org/ +// +// Moodle is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, either version 3 of the License, or +// (at your option) any later version. +// +// Moodle is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License for more details. +// +// You should have received a copy of the GNU General Public License +// along with Moodle. If not, see <http://www.gnu.org/licenses/>. + +namespace Tests; + +defined('MOODLE_INTERNAL') || die(); + +require_once(__DIR__ . '/quiz_attempt_testcase.php'); + +class attempt_submitted_test extends quiz_attempt_testcase { + protected function construct_input() { + return array_merge(parent::construct_input(), [ + 'eventname' => '\mod_quiz\event\attempt_submitted', + ]); + } +}