Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 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
    /** Unknown 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 => 'unknown',
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
}