Differences Between: [Versions 311 and 401] [Versions 311 and 402] [Versions 311 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 * Contains class mod_h5pactivity\output\result 19 * 20 * @package mod_h5pactivity 21 * @copyright 2020 Ferran Recio 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 namespace mod_h5pactivity\output; 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 use renderable; 30 use templatable; 31 use renderer_base; 32 use stdClass; 33 34 /** 35 * Class to display an attempt tesult in mod_h5pactivity. 36 * 37 * @copyright 2020 Ferran Recio 38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 39 */ 40 class result implements renderable, templatable { 41 42 /** Correct answer state. */ 43 const CORRECT = 1; 44 45 /** Incorrect answer state. */ 46 const INCORRECT = 2; 47 48 /** Checked answer state. */ 49 const CHECKED = 3; 50 51 /** Unchecked answer state. */ 52 const UNCHECKED = 4; 53 54 /** Pass answer state. */ 55 const PASS = 5; 56 57 /** Pass answer state. */ 58 const FAIL = 6; 59 60 /** Unkown answer state. */ 61 const UNKNOWN = 7; 62 63 /** Text answer state. */ 64 const TEXT = 8; 65 66 /** @var stdClass result record */ 67 protected $result; 68 69 /** @var mixed additional decoded data */ 70 protected $additionals; 71 72 /** @var mixed response decoded data */ 73 protected $response; 74 75 /** @var mixed correctpattern decoded data */ 76 protected $correctpattern = []; 77 78 /** 79 * Constructor. 80 * 81 * @param stdClass $result a h5pactivity_attempts_results record 82 */ 83 protected function __construct(stdClass $result) { 84 $this->result = $result; 85 if (empty($result->additionals)) { 86 $this->additionals = new stdClass(); 87 } else { 88 $this->additionals = json_decode($result->additionals); 89 } 90 $this->response = $this->decode_response($result->response); 91 if (!empty($result->correctpattern)) { 92 $correctpattern = json_decode($result->correctpattern); 93 foreach ($correctpattern as $pattern) { 94 $this->correctpattern[] = $this->decode_response($pattern); 95 } 96 } 97 } 98 99 /** 100 * return the correct result output depending on the interactiontype 101 * 102 * @param stdClass $result h5pactivity_attempts_results record 103 * @return result|null the result output class if any 104 */ 105 public static function create_from_record(stdClass $result): ?self { 106 // Compound result track is omitted from the report. 107 if ($result->interactiontype == 'compound') { 108 return null; 109 } 110 $classname = "mod_h5pactivity\\output\\result\\{$result->interactiontype}"; 111 $classname = str_replace('-', '', $classname); 112 if (class_exists($classname)) { 113 return new $classname($result); 114 } 115 return new self($result); 116 } 117 118 /** 119 * Return a decoded response structure. 120 * 121 * @param string $value the current response structure 122 * @return array an array of reponses 123 */ 124 private function decode_response(string $value): array { 125 // If [,] means a list of elements. 126 $list = explode('[,]', $value); 127 // Inside a list element [.] means sublist (pair) and [:] a range. 128 foreach ($list as $key => $item) { 129 if (strpos($item, '[.]') !== false) { 130 $list[$key] = explode('[.]', $item); 131 } else if (strpos($item, '[:]') !== false) { 132 $list[$key] = explode('[:]', $item); 133 } 134 } 135 return $list; 136 } 137 138 /** 139 * Export this data so it can be used as the context for a mustache template. 140 * 141 * @param renderer_base $output 142 * @return stdClass 143 */ 144 public function export_for_template(renderer_base $output): stdClass { 145 $result = $this->result; 146 147 $data = (object)[ 148 'id' => $result->id, 149 'attemptid' => $result->attemptid, 150 'subcontent' => $result->subcontent, 151 'timecreated' => $result->timecreated, 152 'interactiontype' => $result->interactiontype, 153 'description' => strip_tags($result->description), 154 'rawscore' => $result->rawscore, 155 'maxscore' => $result->maxscore, 156 'duration' => $result->duration, 157 'completion' => $result->completion, 158 'success' => $result->success, 159 ]; 160 $result; 161 162 $options = $this->export_options(); 163 164 if (!empty($options)) { 165 $data->hasoptions = true; 166 $data->optionslabel = $this->get_optionslabel(); 167 $data->correctlabel = $this->get_correctlabel(); 168 $data->answerlabel = $this->get_answerlabel(); 169 $data->options = array_values($options); 170 $data->track = true; 171 } 172 173 if (!empty($result->maxscore)) { 174 $data->score = get_string('score_out_of', 'mod_h5pactivity', $result); 175 } 176 return $data; 177 } 178 179 /** 180 * Return the options data structure. 181 * 182 * Result types have to override this method generate a specific options report. 183 * 184 * An option is an object with: 185 * - id: the option ID 186 * - description: option description text 187 * - useranswer (optional): what the user answer (see get_answer method) 188 * - correctanswer (optional): the correct answer (see get_answer method) 189 * 190 * @return array of options 191 */ 192 protected function export_options(): ?array { 193 return []; 194 } 195 196 /** 197 * Return a label for result user options/choices. 198 * 199 * Specific result types can override this method to customize 200 * the result options table header. 201 * 202 * @return string to use in options table 203 */ 204 protected function get_optionslabel(): string { 205 return get_string('choice', 'mod_h5pactivity'); 206 } 207 208 /** 209 * Return a label for result user correct answer. 210 * 211 * Specific result types can override this method to customize 212 * the result options table header. 213 * 214 * @return string to use in options table 215 */ 216 protected function get_correctlabel(): string { 217 return get_string('correct_answer', 'mod_h5pactivity'); 218 } 219 220 /** 221 * Return a label for result user attempt answer. 222 * 223 * Specific result types can override this method to customize 224 * the result options table header. 225 * 226 * @return string to use in options table 227 */ 228 protected function get_answerlabel(): string { 229 return get_string('attempt_answer', 'mod_h5pactivity'); 230 } 231 232 /** 233 * Extract descriptions from array. 234 * 235 * @param array $data additional attribute to parse 236 * @return string[] the resulting strings 237 */ 238 protected function get_descriptions(array $data): array { 239 $result = []; 240 foreach ($data as $key => $value) { 241 $description = $this->get_description($value); 242 $index = $value->id ?? $key; 243 $index = trim($index); 244 if (is_numeric($index)) { 245 $index = intval($index); 246 } 247 $result[$index] = (object)['description' => $description, 'id' => $index]; 248 } 249 ksort($result); 250 return $result; 251 } 252 253 /** 254 * Extract description from data element. 255 * 256 * @param stdClass $data additional attribute to parse 257 * @return string the resulting string 258 */ 259 protected function get_description(stdClass $data): string { 260 if (!isset($data->description)) { 261 return ''; 262 } 263 $translations = (array) $data->description; 264 if (empty($translations)) { 265 return ''; 266 } 267 // By default, H5P packages only send "en-US" descriptions. 268 $result = $translations['en-US'] ?? array_shift($translations); 269 return trim($result); 270 } 271 272 /** 273 * Return an answer data to show results. 274 * 275 * @param int $state the answer state 276 * @param string $answer the extra text to display (default null) 277 * @return stdClass with "answer" text and the state attribute to be displayed 278 */ 279 protected function get_answer(int $state, string $answer = null): stdClass { 280 $states = [ 281 self::CORRECT => 'correct', 282 self::INCORRECT => 'incorrect', 283 self::CHECKED => 'checked', 284 self::UNCHECKED => 'unchecked', 285 self::PASS => 'pass', 286 self::FAIL => 'fail', 287 self::UNKNOWN => 'unkown', 288 self::TEXT => 'text', 289 ]; 290 $state = $states[$state] ?? self::UNKNOWN; 291 if ($answer === null) { 292 $answer = get_string('answer_'.$state, 'mod_h5pactivity'); 293 } 294 $result = (object)[ 295 'answer' => $answer, 296 $state => true, 297 ]; 298 return $result; 299 } 300 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body