Search moodle.org's
Developer Documentation

See Release Notes

  • Bug fixes for general core bugs in 4.3.x will end 7 October 2024 (12 months).
  • Bug fixes for security issues in 4.3.x will end 21 April 2025 (18 months).
  • PHP version: minimum PHP 8.0.0 Note: minimum PHP version has increased since Moodle 4.1. PHP 8.2.x is supported too.

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