Search moodle.org's
Developer Documentation

See Release Notes
Long Term Support Release

  • Bug fixes for general core bugs in 4.1.x will end 13 November 2023 (12 months).
  • Bug fixes for security issues in 4.1.x will end 10 November 2025 (36 months).
  • PHP version: minimum PHP 7.4.0 Note: minimum PHP version has increased since Moodle 4.0. PHP 8.0.x is supported too.

Differences Between: [Versions 401 and 402] [Versions 401 and 403]

   1  <?php
   2  // This file is part of Moodle - http://moodle.org/
   3  //
   4  // Moodle is free software: you can redistribute it and/or modify
   5  // it under the terms of the GNU General Public License as published by
   6  // the Free Software Foundation, either version 3 of the License, or
   7  // (at your option) any later version.
   8  //
   9  // Moodle is distributed in the hope that it will be useful,
  10  // but WITHOUT ANY WARRANTY; without even the implied warranty of
  11  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12  // GNU General Public License for more details.
  13  //
  14  // You should have received a copy of the GNU General Public License
  15  // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
  16  
  17  /**
  18   * This is the external method for getting the information needed to present a results report.
  19   *
  20   * @package    mod_h5pactivity
  21   * @since      Moodle 3.9
  22   * @copyright  2020 Ferran Recio <ferran@moodle.com>
  23   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  24   */
  25  
  26  namespace mod_h5pactivity\external;
  27  
  28  defined('MOODLE_INTERNAL') || die();
  29  
  30  require_once($CFG->libdir . '/externallib.php');
  31  
  32  use mod_h5pactivity\local\manager;
  33  use mod_h5pactivity\local\report\results as report_results;
  34  use external_api;
  35  use external_function_parameters;
  36  use external_value;
  37  use external_multiple_structure;
  38  use external_single_structure;
  39  use external_warnings;
  40  use moodle_exception;
  41  use context_module;
  42  use stdClass;
  43  
  44  /**
  45   * This is the external method for getting the information needed to present a results report.
  46   *
  47   * @copyright  2020 Ferran Recio <ferran@moodle.com>
  48   * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  49   */
  50  class get_results extends external_api {
  51  
  52      /**
  53       * Webservice parameters.
  54       *
  55       * @return external_function_parameters
  56       */
  57      public static function execute_parameters(): external_function_parameters {
  58          return new external_function_parameters(
  59              [
  60                  'h5pactivityid' => new external_value(PARAM_INT, 'h5p activity instance id'),
  61                  'attemptids' => new external_multiple_structure(
  62                      new external_value(PARAM_INT, 'The attempt id'),
  63                      'Attempt ids', VALUE_DEFAULT, []
  64                  ),
  65              ]
  66          );
  67      }
  68  
  69      /**
  70       * Return user attempts results information in a h5p activity.
  71       *
  72       * In case an empty array of attempt ids is passed, the method will load all
  73       * activity attempts from the current user.
  74       *
  75       * @throws  moodle_exception if the user cannot see the report
  76       * @param  int $h5pactivityid The h5p activity id
  77       * @param  int[] $attemptids The attempt ids
  78       * @return stdClass report data
  79       */
  80      public static function execute(int $h5pactivityid, array $attemptids = []): stdClass {
  81          global $USER;
  82  
  83          $params = external_api::validate_parameters(self::execute_parameters(), [
  84              'h5pactivityid' => $h5pactivityid,
  85              'attemptids' => $attemptids,
  86          ]);
  87          $h5pactivityid = $params['h5pactivityid'];
  88          $attemptids = $params['attemptids'];
  89  
  90          $warnings = [];
  91  
  92          // Request and permission validation.
  93          list ($course, $cm) = get_course_and_cm_from_instance($h5pactivityid, 'h5pactivity');
  94  
  95          $context = context_module::instance($cm->id);
  96          self::validate_context($context);
  97  
  98          $manager = manager::create_from_coursemodule($cm);
  99  
 100          if (empty($attemptids)) {
 101              $attemptids = [];
 102              foreach ($manager->get_user_attempts($USER->id) as $attempt) {
 103                  $attemptids[] = $attempt->get_id();
 104              }
 105          }
 106  
 107          $attempts = [];
 108          foreach ($attemptids as $attemptid) {
 109              $report = $manager->get_report(null, $attemptid);
 110  
 111              if ($report && $report instanceof report_results) {
 112                  $attempts[] = self::export_attempt($report);
 113              } else {
 114                  $warnings[] = [
 115                      'item' => 'h5pactivity_attempts',
 116                      'itemid' => $attemptid,
 117                      'warningcode' => '1',
 118                      'message' => "Cannot access attempt",
 119                  ];
 120              }
 121          }
 122  
 123          $result = (object)[
 124              'activityid' => $h5pactivityid,
 125              'attempts' => $attempts,
 126              'warnings' => $warnings,
 127          ];
 128  
 129          return $result;
 130      }
 131  
 132      /**
 133       * Return a data object from an attempt.
 134       *
 135       * @param report_results $report the attempt data
 136       * @return stdClass a WS compatible version of the attempt
 137       */
 138      private static function export_attempt(report_results $report): stdClass {
 139  
 140          $data = $report->export_data_for_external();
 141  
 142          $attemptdata = $data->attempt;
 143  
 144          $attempt = (object)[
 145              'id' => $attemptdata->id,
 146              'h5pactivityid' => $attemptdata->h5pactivityid,
 147              'userid' => $attemptdata->userid,
 148              'timecreated' => $attemptdata->timecreated,
 149              'timemodified' => $attemptdata->timemodified,
 150              'attempt' => $attemptdata->attempt,
 151              'rawscore' => $attemptdata->rawscore,
 152              'maxscore' => $attemptdata->maxscore,
 153              'duration' => (empty($attemptdata->durationvalue)) ? 0 : $attemptdata->durationvalue,
 154              'scaled' => (empty($attemptdata->scaled)) ? 0 : $attemptdata->scaled,
 155              'results' => [],
 156          ];
 157          if (isset($attemptdata->completion) && $attemptdata->completion !== null) {
 158              $attempt->completion = $attemptdata->completion;
 159          }
 160          if (isset($attemptdata->success) && $attemptdata->success !== null) {
 161              $attempt->success = $attemptdata->success;
 162          }
 163          foreach ($data->results as $result) {
 164              $attempt->results[] = self::export_result($result);
 165          }
 166          return $attempt;
 167      }
 168  
 169      /**
 170       * Return a data object from a result.
 171       *
 172       * @param stdClass $data the result data
 173       * @return stdClass a WS compatible version of the result
 174       */
 175      private static function export_result(stdClass $data): stdClass {
 176          $result = (object)[
 177              'id' => $data->id,
 178              'attemptid' => $data->attemptid,
 179              'subcontent' => $data->subcontent,
 180              'timecreated' => $data->timecreated,
 181              'interactiontype' => $data->interactiontype,
 182              'description' => $data->description,
 183              'rawscore' => $data->rawscore,
 184              'maxscore' => $data->maxscore,
 185              'duration' => $data->duration,
 186              'optionslabel' => $data->optionslabel ?? get_string('choice', 'mod_h5pactivity'),
 187              'correctlabel' => $data->correctlabel ?? get_string('correct_answer', 'mod_h5pactivity'),
 188              'answerlabel' => $data->answerlabel ?? get_string('attempt_answer', 'mod_h5pactivity'),
 189              'track' => $data->track ?? false,
 190          ];
 191          if (isset($data->completion) && $data->completion !== null) {
 192              $result->completion = $data->completion;
 193          }
 194          if (isset($data->success) && $data->success !== null) {
 195              $result->success = $data->success;
 196          }
 197          if (isset($data->options)) {
 198              $result->options = $data->options;
 199          }
 200          if (isset($data->content)) {
 201              $result->content = $data->content;
 202          }
 203          return $result;
 204      }
 205  
 206      /**
 207       * Describes the get_h5pactivity_access_information return value.
 208       *
 209       * @return external_single_structure
 210       */
 211      public static function execute_returns(): external_single_structure {
 212          return new external_single_structure([
 213              'activityid' => new external_value(PARAM_INT, 'Activity course module ID'),
 214              'attempts' => new external_multiple_structure(
 215                  self::get_attempt_returns(), 'The complete attempts list'
 216              ),
 217              'warnings' => new external_warnings(),
 218          ], 'Activity attempts results data');
 219      }
 220  
 221      /**
 222       * Return the external structure of an attempt
 223       * @return external_single_structure
 224       */
 225      private static function get_attempt_returns(): external_single_structure {
 226  
 227          $result = new external_single_structure([
 228              'id'    => new external_value(PARAM_INT, 'ID of the context'),
 229              'h5pactivityid' => new external_value(PARAM_INT, 'ID of the H5P activity'),
 230              'userid' => new external_value(PARAM_INT, 'ID of the user'),
 231              'timecreated' => new external_value(PARAM_INT, 'Attempt creation'),
 232              'timemodified' => new external_value(PARAM_INT, 'Attempt modified'),
 233              'attempt' => new external_value(PARAM_INT, 'Attempt number'),
 234              'rawscore' => new external_value(PARAM_INT, 'Attempt score value'),
 235              'maxscore' => new external_value(PARAM_INT, 'Attempt max score'),
 236              'duration' => new external_value(PARAM_INT, 'Attempt duration in seconds'),
 237              'completion' => new external_value(PARAM_INT, 'Attempt completion', VALUE_OPTIONAL),
 238              'success' => new external_value(PARAM_INT, 'Attempt success', VALUE_OPTIONAL),
 239              'scaled' => new external_value(PARAM_FLOAT, 'Attempt scaled'),
 240              'results' => new external_multiple_structure(
 241                  self::get_result_returns(),
 242                  'The results of the attempt', VALUE_OPTIONAL
 243              ),
 244          ], 'The attempt general information');
 245          return $result;
 246      }
 247  
 248      /**
 249       * Return the external structure of a result
 250       * @return external_single_structure
 251       */
 252      private static function get_result_returns(): external_single_structure {
 253  
 254          $result = new external_single_structure([
 255              'id'    => new external_value(PARAM_INT, 'ID of the context'),
 256              'attemptid' => new external_value(PARAM_INT, 'ID of the H5P attempt'),
 257              'subcontent' => new external_value(PARAM_NOTAGS, 'Subcontent identifier'),
 258              'timecreated' => new external_value(PARAM_INT, 'Result creation'),
 259              'interactiontype' => new external_value(PARAM_NOTAGS, 'Interaction type'),
 260              'description' => new external_value(PARAM_RAW, 'Result description'),
 261              'content' => new external_value(PARAM_RAW, 'Result extra content', VALUE_OPTIONAL),
 262              'rawscore' => new external_value(PARAM_INT, 'Result score value'),
 263              'maxscore' => new external_value(PARAM_INT, 'Result max score'),
 264              'duration' => new external_value(PARAM_INT, 'Result duration in seconds', VALUE_OPTIONAL, 0),
 265              'completion' => new external_value(PARAM_INT, 'Result completion', VALUE_OPTIONAL),
 266              'success' => new external_value(PARAM_INT, 'Result success', VALUE_OPTIONAL),
 267              'optionslabel' => new external_value(PARAM_NOTAGS, 'Label used for result options', VALUE_OPTIONAL),
 268              'correctlabel' => new external_value(PARAM_NOTAGS, 'Label used for correct answers', VALUE_OPTIONAL),
 269              'answerlabel' => new external_value(PARAM_NOTAGS, 'Label used for user answers', VALUE_OPTIONAL),
 270              'track' => new external_value(PARAM_BOOL, 'If the result has valid track information', VALUE_OPTIONAL),
 271              'options' => new external_multiple_structure(
 272                  new external_single_structure([
 273                      'description'    => new external_value(PARAM_RAW, 'Option description', VALUE_OPTIONAL),
 274                      'id' => new external_value(PARAM_TEXT, 'Option string identifier', VALUE_OPTIONAL),
 275                      'correctanswer' => self::get_answer_returns('The option correct answer', VALUE_OPTIONAL),
 276                      'useranswer' => self::get_answer_returns('The option user answer', VALUE_OPTIONAL),
 277                  ]),
 278                  'The statement options', VALUE_OPTIONAL
 279              ),
 280          ], 'A single result statement tracking information');
 281          return $result;
 282      }
 283  
 284      /**
 285       * Return the external structure of an answer or correctanswer
 286       *
 287       * @param string $description the return description
 288       * @param int $required the return required value
 289       * @return external_single_structure
 290       */
 291      private static function get_answer_returns(string $description, int $required = VALUE_REQUIRED): external_single_structure {
 292  
 293          $result = new external_single_structure([
 294              'answer' => new external_value(PARAM_NOTAGS, 'Option text value', VALUE_OPTIONAL),
 295              'correct' => new external_value(PARAM_BOOL, 'If has to be displayed as correct', VALUE_OPTIONAL),
 296              'incorrect' => new external_value(PARAM_BOOL, 'If has to be displayed as incorrect', VALUE_OPTIONAL),
 297              'text' => new external_value(PARAM_BOOL, 'If has to be displayed as simple text', VALUE_OPTIONAL),
 298              'checked' => new external_value(PARAM_BOOL, 'If has to be displayed as a checked option', VALUE_OPTIONAL),
 299              'unchecked' => new external_value(PARAM_BOOL, 'If has to be displayed as a unchecked option', VALUE_OPTIONAL),
 300              'pass' => new external_value(PARAM_BOOL, 'If has to be displayed as passed', VALUE_OPTIONAL),
 301              'fail' => new external_value(PARAM_BOOL, 'If has to be displayed as failed', VALUE_OPTIONAL),
 302          ], $description, $required);
 303          return $result;
 304      }
 305  }