first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+228
View File
@@ -0,0 +1,228 @@
<?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/>.
/**
* Contains class mod_h5pactivity\output\reportlink
*
* @package mod_h5pactivity
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_h5pactivity\output;
defined('MOODLE_INTERNAL') || die();
use mod_h5pactivity\local\attempt as activity_attempt;
use renderable;
use templatable;
use renderer_base;
use moodle_url;
use user_picture;
use stdClass;
/**
* Class to help display report link in mod_h5pactivity.
*
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class attempt implements renderable, templatable {
/** @var activity_attempt attempt */
public $attempt;
/** @var stdClass user record */
public $user;
/** @var int courseid necesary to present user picture */
public $courseid;
/**
* Constructor.
*
* @param activity_attempt $attempt the attempt object
* @param stdClass $user a user record (default null).
* @param int $courseid optional course id (default null).
*/
public function __construct(activity_attempt $attempt, stdClass $user = null, int $courseid = null) {
$this->attempt = $attempt;
$this->user = $user;
$this->courseid = $courseid;
}
/**
* Export this data so it can be used as the context for a mustache template.
*
* @param renderer_base $output
* @return stdClass
*/
public function export_for_template(renderer_base $output) {
$attempt = $this->attempt;
$data = (object)[
'id' => $attempt->get_id(),
'h5pactivityid' => $attempt->get_h5pactivityid(),
'userid' => $attempt->get_userid(),
'timecreated' => $attempt->get_timecreated(),
'timemodified' => $attempt->get_timemodified(),
'attempt' => $attempt->get_attempt(),
'rawscore' => $attempt->get_rawscore(),
'maxscore' => $attempt->get_maxscore(),
'duration' => '-',
'durationcompact' => '-',
'completion' => $attempt->get_completion(),
'completionicon' => $this->completion_icon($output, $attempt->get_completion()),
'completiontext' => $this->completion_icon($output, $attempt->get_completion(), true),
'success' => $attempt->get_success(),
'successicon' => $this->success_icon($output, $attempt->get_success()),
'successtext' => $this->success_icon($output, $attempt->get_success(), true),
'scaled' => $attempt->get_scaled(),
'reporturl' => new moodle_url('/mod/h5pactivity/report.php', [
'a' => $attempt->get_h5pactivityid(), 'attemptid' => $attempt->get_id()
]),
];
if ($attempt->get_duration() !== null) {
$data->durationvalue = $attempt->get_duration();
$duration = $this->extract_duration($data->durationvalue);
$data->duration = $this->format_duration($duration);
$data->durationcompact = $this->format_duration_short($duration);
}
if (!empty($data->maxscore)) {
$data->score = get_string('score_out_of', 'mod_h5pactivity', $data);
}
if ($this->user) {
$data->user = $this->user;
$userpicture = new user_picture($this->user);
$userpicture->courseid = $this->courseid;
$data->user->picture = $output->render($userpicture);
$data->user->fullname = fullname($this->user);
}
return $data;
}
/**
* Return a completion icon HTML.
*
* @param renderer_base $output the renderer base object
* @param int|null $completion the current completion value
* @param bool $showtext if the icon must have a text or only icon
* @return string icon HTML
*/
private function completion_icon(renderer_base $output, int $completion = null, bool $showtext = false): string {
if ($completion === null) {
return '';
}
if ($completion) {
$alt = get_string('attempt_completion_yes', 'mod_h5pactivity');
$icon = 'i/completion-auto-y';
} else {
$alt = get_string('attempt_completion_no', 'mod_h5pactivity');
$icon = 'i/completion-auto-n';
}
$text = '';
if ($showtext) {
$text = $alt;
$alt = '';
}
return $output->pix_icon($icon, $alt).$text;
}
/**
* Return a success icon
* @param renderer_base $output the renderer base object
* @param int|null $success the current success value
* @param bool $showtext if the icon must have a text or only icon
* @return string icon HTML
*/
private function success_icon(renderer_base $output, int $success = null, bool $showtext = false): string {
if ($success === null) {
$alt = get_string('attempt_success_unknown', 'mod_h5pactivity');
if ($showtext) {
return $alt;
}
$icon = 'i/empty';
} else if ($success) {
$alt = get_string('attempt_success_pass', 'mod_h5pactivity');
$icon = 'i/checkedcircle';
} else {
$alt = get_string('attempt_success_fail', 'mod_h5pactivity');
$icon = 'i/uncheckedcircle';
}
$text = '';
if ($showtext) {
$text = $alt;
$alt = '';
}
return $output->pix_icon($icon, $alt).$text;
}
/**
* Return the duration in long format (localized)
*
* @param stdClass $duration object with (h)hours, (m)minutes and (s)seconds
* @return string the long format duration
*/
private function format_duration(stdClass $duration): string {
$result = [];
if ($duration->h) {
$result[] = get_string('numhours', 'moodle', $duration->h);
}
if ($duration->m) {
$result[] = get_string('numminutes', 'moodle', $duration->m);
}
if ($duration->s) {
$result[] = get_string('numseconds', 'moodle', $duration->s);
}
return implode(' ', $result);
}
/**
* Return the duration en short format (for example: 145' 43'')
*
* Note: this method is used to make duration responsive.
*
* @param stdClass $duration object with (h)hours, (m)minutes and (s)seconds
* @return string the short format duration
*/
private function format_duration_short(stdClass $duration): string {
$result = [];
if ($duration->h || $duration->m) {
$result[] = ($duration->h * 60 + $duration->m)."'";
}
if ($duration->s) {
$result[] = $duration->s."''";
}
return implode(' ', $result);
}
/**
* Extract hours and minutes from second duration.
*
* Note: this function is used to generate the param for format_duration
* and format_duration_short
*
* @param int $seconds number of second
* @return stdClass with (h)hours, (m)minutes and (s)seconds
*/
private function extract_duration(int $seconds): stdClass {
$h = floor($seconds / 3600);
$m = floor(($seconds - $h * 3600) / 60);
$s = $seconds - ($h * 3600 + $m * 60);
return (object)['h' => $h, 'm' => $m, 's' => $s];
}
}
@@ -0,0 +1,118 @@
<?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/>.
/**
* Contains class mod_h5pactivity\output\report\attempts
*
* @package mod_h5pactivity
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_h5pactivity\output;
defined('MOODLE_INTERNAL') || die();
use mod_h5pactivity\local\attempt;
use mod_h5pactivity\output\attempt as output_attempt;
use renderable;
use templatable;
use renderer_base;
use user_picture;
use stdClass;
/**
* Class to output an attempts report on mod_h5pactivity.
*
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class reportattempts implements renderable, templatable {
/** @var attempt[] attempts */
public $attempts;
/** @var stdClass user record */
public $user;
/** @var int courseid necesary to present user picture */
public $courseid;
/** @var attempt scored attempt */
public $scored;
/** @var string scored attempt title */
public $title;
/**
* Constructor.
*
* The "scored attempt" is the attempt used for grading. By default it is the max score attempt
* but this could be defined in the activity settings. In some cases this scored attempts does not
* exists at all, this is the reason why it's an optional param.
*
* @param array $attempts an array of attempts
* @param stdClass $user a user record
* @param int $courseid course id
* @param string|null $title title to display on the scored attempt (null if none attempt is the scored one)
* @param attempt|null $scored the scored attempt (null if none)
*/
public function __construct(array $attempts, stdClass $user, int $courseid, string $title = null, attempt $scored = null) {
$this->attempts = $attempts;
$this->user = $user;
$this->courseid = $courseid;
$this->title = $title;
$this->scored = $scored;
}
/**
* Export this data so it can be used as the context for a mustache template.
*
* @param renderer_base $output
* @return stdClass
*/
public function export_for_template(renderer_base $output) {
global $USER;
$data = (object)['attempts' => [], 'user' => $this->user];
foreach ($this->attempts as $attempt) {
$outputattempt = new output_attempt($attempt);
$data->attempts[] = $outputattempt->export_for_template($output);
}
$data->attemptscount = count($data->attempts);
$userpicture = new user_picture($this->user);
$userpicture->courseid = $this->courseid;
$data->user->fullname = fullname($this->user);
$data->user->picture = $output->render($userpicture);
if ($USER->id == $this->user->id) {
$data->title = get_string('myattempts', 'mod_h5pactivity');
}
if (!empty($this->title)) {
$scored = (object)[
'title' => $this->title,
'attempts' => [],
];
$outputattempt = new output_attempt($this->scored);
$scored->attempts[] = $outputattempt->export_for_template($output);
$data->scored = $scored;
}
return $data;
}
}
@@ -0,0 +1,68 @@
<?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/>.
/**
* Contains class mod_h5pactivity\output\reportlink
*
* @package mod_h5pactivity
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_h5pactivity\output;
defined('MOODLE_INTERNAL') || die();
use renderable;
use templatable;
use renderer_base;
use moodle_url;
/**
* Class to help display report link in mod_h5pactivity.
*
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class reportlink implements renderable, templatable {
/** @var H5P factory */
public $url;
/** @var H5P library list */
public $message;
/**
* Constructor.
*
* @param moodle_url $url the destination url
* @param string $message the link message
*/
public function __construct(moodle_url $url, string $message) {
$this->url = $url;
$this->message = $message;
}
/**
* Export this data so it can be used as the context for a mustache template.
*
* @param renderer_base $output
* @return stdClass
*/
public function export_for_template(renderer_base $output) {
return $this;
}
}
@@ -0,0 +1,92 @@
<?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/>.
/**
* Contains class mod_h5pactivity\output\reportresults
*
* @package mod_h5pactivity
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_h5pactivity\output;
defined('MOODLE_INTERNAL') || die();
use mod_h5pactivity\local\attempt;
use mod_h5pactivity\output\attempt as output_attempt;
use mod_h5pactivity\output\result as output_result;
use renderable;
use templatable;
use renderer_base;
use stdClass;
/**
* Class to display the result report in mod_h5pactivity.
*
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class reportresults implements renderable, templatable {
/** @var attempt the header attempt */
public $attempt;
/** @var stdClass user record */
public $user;
/** @var int courseid necesary to present user picture */
public $courseid;
/**
* Constructor.
*
* @param attempt $attempt the current attempt
* @param stdClass $user a user record
* @param int $courseid course id
*/
public function __construct(attempt $attempt, stdClass $user, int $courseid) {
$this->attempt = $attempt;
$this->user = $user;
$this->courseid = $courseid;
}
/**
* Export this data so it can be used as the context for a mustache template.
*
* @param renderer_base $output
* @return stdClass
*/
public function export_for_template(renderer_base $output) {
$outputattempt = new output_attempt($this->attempt, $this->user, $this->courseid);
$data = (object)[
'attempt' => $outputattempt->export_for_template($output),
];
$results = $this->attempt->get_results();
$data->results = [];
foreach ($results as $key => $result) {
$outputresult = output_result::create_from_record($result);
if ($outputresult) {
$data->results[] = $outputresult->export_for_template($output);
}
}
return $data;
}
}
+300
View File
@@ -0,0 +1,300 @@
<?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/>.
/**
* Contains class mod_h5pactivity\output\result
*
* @package mod_h5pactivity
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_h5pactivity\output;
defined('MOODLE_INTERNAL') || die();
use renderable;
use templatable;
use renderer_base;
use stdClass;
/**
* Class to display an attempt tesult in mod_h5pactivity.
*
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class result implements renderable, templatable {
/** Correct answer state. */
const CORRECT = 1;
/** Incorrect answer state. */
const INCORRECT = 2;
/** Checked answer state. */
const CHECKED = 3;
/** Unchecked answer state. */
const UNCHECKED = 4;
/** Pass answer state. */
const PASS = 5;
/** Pass answer state. */
const FAIL = 6;
/** Unknown answer state. */
const UNKNOWN = 7;
/** Text answer state. */
const TEXT = 8;
/** @var stdClass result record */
protected $result;
/** @var mixed additional decoded data */
protected $additionals;
/** @var mixed response decoded data */
protected $response;
/** @var mixed correctpattern decoded data */
protected $correctpattern = [];
/**
* Constructor.
*
* @param stdClass $result a h5pactivity_attempts_results record
*/
protected function __construct(stdClass $result) {
$this->result = $result;
if (empty($result->additionals)) {
$this->additionals = new stdClass();
} else {
$this->additionals = json_decode($result->additionals);
}
$this->response = $this->decode_response($result->response);
if (!empty($result->correctpattern)) {
$correctpattern = json_decode($result->correctpattern);
foreach ($correctpattern as $pattern) {
$this->correctpattern[] = $this->decode_response($pattern);
}
}
}
/**
* return the correct result output depending on the interactiontype
*
* @param stdClass $result h5pactivity_attempts_results record
* @return result|null the result output class if any
*/
public static function create_from_record(stdClass $result): ?self {
// Compound result track is omitted from the report.
if ($result->interactiontype == 'compound') {
return null;
}
$classname = "mod_h5pactivity\\output\\result\\{$result->interactiontype}";
$classname = str_replace('-', '', $classname);
if (class_exists($classname)) {
return new $classname($result);
}
return new self($result);
}
/**
* Return a decoded response structure.
*
* @param string $value the current response structure
* @return array an array of reponses
*/
private function decode_response(string $value): array {
// If [,] means a list of elements.
$list = explode('[,]', $value);
// Inside a list element [.] means sublist (pair) and [:] a range.
foreach ($list as $key => $item) {
if (strpos($item, '[.]') !== false) {
$list[$key] = explode('[.]', $item);
} else if (strpos($item, '[:]') !== false) {
$list[$key] = explode('[:]', $item);
}
}
return $list;
}
/**
* Export this data so it can be used as the context for a mustache template.
*
* @param renderer_base $output
* @return stdClass
*/
public function export_for_template(renderer_base $output): stdClass {
$result = $this->result;
$data = (object)[
'id' => $result->id,
'attemptid' => $result->attemptid,
'subcontent' => $result->subcontent,
'timecreated' => $result->timecreated,
'interactiontype' => $result->interactiontype,
'description' => strip_tags($result->description),
'rawscore' => $result->rawscore,
'maxscore' => $result->maxscore,
'duration' => $result->duration,
'completion' => $result->completion,
'success' => $result->success,
];
$result;
$options = $this->export_options();
if (!empty($options)) {
$data->hasoptions = true;
$data->optionslabel = $this->get_optionslabel();
$data->correctlabel = $this->get_correctlabel();
$data->answerlabel = $this->get_answerlabel();
$data->options = array_values($options);
$data->track = true;
}
if (!empty($result->maxscore)) {
$data->score = get_string('score_out_of', 'mod_h5pactivity', $result);
}
return $data;
}
/**
* Return the options data structure.
*
* Result types have to override this method generate a specific options report.
*
* An option is an object with:
* - id: the option ID
* - description: option description text
* - useranswer (optional): what the user answer (see get_answer method)
* - correctanswer (optional): the correct answer (see get_answer method)
*
* @return array of options
*/
protected function export_options(): ?array {
return [];
}
/**
* Return a label for result user options/choices.
*
* Specific result types can override this method to customize
* the result options table header.
*
* @return string to use in options table
*/
protected function get_optionslabel(): string {
return get_string('choice', 'mod_h5pactivity');
}
/**
* Return a label for result user correct answer.
*
* Specific result types can override this method to customize
* the result options table header.
*
* @return string to use in options table
*/
protected function get_correctlabel(): string {
return get_string('correct_answer', 'mod_h5pactivity');
}
/**
* Return a label for result user attempt answer.
*
* Specific result types can override this method to customize
* the result options table header.
*
* @return string to use in options table
*/
protected function get_answerlabel(): string {
return get_string('attempt_answer', 'mod_h5pactivity');
}
/**
* Extract descriptions from array.
*
* @param array $data additional attribute to parse
* @return string[] the resulting strings
*/
protected function get_descriptions(array $data): array {
$result = [];
foreach ($data as $key => $value) {
$description = $this->get_description($value);
$index = $value->id ?? $key;
$index = trim($index);
if (is_numeric($index)) {
$index = intval($index);
}
$result[$index] = (object)['description' => $description, 'id' => $index];
}
ksort($result);
return $result;
}
/**
* Extract description from data element.
*
* @param stdClass $data additional attribute to parse
* @return string the resulting string
*/
protected function get_description(stdClass $data): string {
if (!isset($data->description)) {
return '';
}
$translations = (array) $data->description;
if (empty($translations)) {
return '';
}
// By default, H5P packages only send "en-US" descriptions.
$result = $translations['en-US'] ?? array_shift($translations);
return trim($result);
}
/**
* Return an answer data to show results.
*
* @param int $state the answer state
* @param string $answer the extra text to display (default null)
* @return stdClass with "answer" text and the state attribute to be displayed
*/
protected function get_answer(int $state, string $answer = null): stdClass {
$states = [
self::CORRECT => 'correct',
self::INCORRECT => 'incorrect',
self::CHECKED => 'checked',
self::UNCHECKED => 'unchecked',
self::PASS => 'pass',
self::FAIL => 'fail',
self::UNKNOWN => 'unknown',
self::TEXT => 'text',
];
$state = $states[$state] ?? self::UNKNOWN;
if ($answer === null) {
$answer = get_string('answer_'.$state, 'mod_h5pactivity');
}
$result = (object)[
'answer' => $answer,
$state => true,
];
return $result;
}
}
@@ -0,0 +1,101 @@
<?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/>.
/**
* Contains class mod_h5pactivity\output\result\choice
*
* @package mod_h5pactivity
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_h5pactivity\output\result;
defined('MOODLE_INTERNAL') || die();
use mod_h5pactivity\output\result;
use renderer_base;
/**
* Class to display H5P choice result.
*
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class choice extends result {
/**
* Return the options data structure.
*
* @return array of options
*/
protected function export_options(): ?array {
// Suppose H5P choices have only a single list of valid answers.
$correctpattern = reset($this->correctpattern);
if (empty($correctpattern)) {
$correctpattern = [];
}
$additionals = $this->additionals;
// H5P has a special extension for long choices.
$extensions = (array) $additionals->extensions ?? [];
$filter = isset($extensions['https://h5p.org/x-api/line-breaks']) ? true : false;
if (isset($additionals->choices)) {
$options = $this->get_descriptions($additionals->choices);
} else {
$options = [];
}
// Some H5P activities like Find the Words don't user the standard CMI format delimiter
// and don't use propper choice additionals. In those cases the report needs to fix this
// using the correct pattern as choices and using a non standard delimiter.
if (empty($options)) {
if (count($correctpattern) == 1) {
$correctpattern = explode(',', reset($correctpattern));
}
foreach ($correctpattern as $value) {
$option = (object)[
'id' => $value,
'description' => $value,
];
$options[$value] = $option;
}
}
foreach ($options as $key => $value) {
$correctstate = (in_array($key, $correctpattern)) ? parent::CHECKED : parent::UNCHECKED;
if (in_array($key, $this->response)) {
$answerstate = ($correctstate == parent::CHECKED) ? parent::PASS : parent::FAIL;
// In some cases, like Branching scenario H5P activity, no correct Pattern is provided
// so any answer is just a check.
if (empty($correctpattern)) {
$answerstate = parent::CHECKED;
}
$value->useranswer = $this->get_answer($answerstate);
}
$value->correctanswer = $this->get_answer($correctstate);
if ($filter && $correctstate == parent::UNCHECKED && !isset($value->useranswer)) {
unset($options[$key]);
}
}
return $options;
}
}
@@ -0,0 +1,127 @@
<?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/>.
/**
* Contains class mod_h5pactivity\output\result\fillin
*
* @package mod_h5pactivity
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_h5pactivity\output\result;
defined('MOODLE_INTERNAL') || die();
use mod_h5pactivity\output\result;
use renderer_base;
use stdClass;
/**
* Class to display H5P fill-in result.
*
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class fillin extends result {
/**
* Return the options data structure.
*
* @return array of options
*/
protected function export_options(): ?array {
$correctpatterns = $this->correctpattern;
$additionals = $this->additionals;
$extensions = (array) $additionals->extensions ?? [];
// There are two way in which H5P could force case sensitivity, with extensions
// or using options in the correctpatterns. By default it is case sensible.
$casesensitive = $extensions['https://h5p.org/x-api/case-sensitivity'] ?? true;
if ((!empty($this->result->correctpattern)
&& strpos($this->result->correctpattern, '{case_matters=false}') !== false)) {
$casesensitive = false;
}
$values = [];
// Add all possibilities from $additionals.
if (isset($extensions['https://h5p.org/x-api/alternatives'])) {
foreach ($extensions['https://h5p.org/x-api/alternatives'] as $key => $value) {
if (!is_array($value)) {
$value = [$value];
}
$values[$key] = ($casesensitive) ? $value : array_change_key_case($value);
}
}
// Add possibilities from correctpattern.
foreach ($correctpatterns as $correctpattern) {
foreach ($correctpattern as $key => $pattern) {
// The xAPI admits more params a part form values.
// For now this extra information is not used in reporting
// but it is posible future H5P types need them.
$value = preg_replace('/\{.+=.*\}/', '', $pattern);
$value = ($casesensitive) ? $value : strtolower($value);
if (!isset($values[$key])) {
$values[$key] = [];
}
if (!in_array($value, $values[$key])) {
array_unshift($values[$key], $value);
}
}
}
// Generate options.
$options = [];
$num = 1;
foreach ($values as $key => $value) {
$option = (object)[
'id' => $key,
'description' => get_string('result_fill-in_gap', 'mod_h5pactivity', $num),
];
$gapresponse = $this->response[$key] ?? null;
$gapresponse = ($casesensitive) ? $gapresponse : strtolower($gapresponse);
if ($gapresponse !== null && in_array($gapresponse, $value)) {
$state = parent::CORRECT;
} else {
$state = parent::INCORRECT;
}
$option->useranswer = $this->get_answer($state, $this->response[$key]);
$option->correctanswer = $this->get_answer(parent::TEXT, implode(' / ', $value));
$options[] = $option;
$num++;
}
return $options;
}
/**
* Return a label for result user options/choices
*
* Specific result types can override this method to customize
* the result options table header.
*
* @return string to use in options table
*/
protected function get_optionslabel(): string {
return get_string('result_matching', 'mod_h5pactivity');
}
}
@@ -0,0 +1,58 @@
<?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/>.
/**
* Contains class mod_h5pactivity\output\result\longfillin
*
* @package mod_h5pactivity
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_h5pactivity\output\result;
defined('MOODLE_INTERNAL') || die();
use mod_h5pactivity\output\result;
use renderer_base;
use stdClass;
/**
* Class to display H5P long fill in result.
*
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class longfillin extends result {
/**
* Export this data so it can be used as the context for a mustache template.
*
* @param renderer_base $output
* @return stdClass
*/
public function export_for_template(renderer_base $output): stdClass {
$data = parent::export_for_template($output);
$userresponse = reset($this->response);
$data->content = format_text($userresponse, FORMAT_PLAIN);
$data->track = true;
// Long fill-in is used for Essay type exercices. H5P adds
// extra characters to the description in all fill-in interactions
// but in the essay questions is unnecesary.
$data->description = preg_replace('/__________$/', '', $data->description);
return $data;
}
}
@@ -0,0 +1,139 @@
<?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/>.
/**
* Contains class mod_h5pactivity\output\result\matching
*
* @package mod_h5pactivity
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_h5pactivity\output\result;
defined('MOODLE_INTERNAL') || die();
use mod_h5pactivity\output\result;
/**
* Class to display H5P matching result.
*
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class matching extends result {
/**
* Return the options data structure.
*
* @return array|null of options
*/
protected function export_options(): ?array {
// Suppose H5P choices have only list of valid answers.
$correctpattern = reset($this->correctpattern);
$additionals = $this->additionals;
// Get sources (options).
if (isset($additionals->source)) {
$sources = $this->get_descriptions($additionals->source);
} else {
$sources = [];
}
// Get targets.
if (isset($additionals->target)) {
$targets = $this->get_descriptions($additionals->target);
} else {
$targets = [];
}
// Create original options array.
$options = array_map(function ($source) {
$cloneddraggable = clone $source;
$cloneddraggable->correctanswers = [];
return $cloneddraggable;
}, $sources);
// Fill options with correct answers flags if they exist.
foreach ($correctpattern as $pattern) {
if (!is_array($pattern) || count($pattern) != 2) {
continue;
}
// We assume here that the activity is following the convention sets in:
// https://github.com/h5p/h5p-php-report/blob/master/type-processors/matching-processor.class.php
// i.e. source is index 1 and dropzone is index 0.
if (isset($sources[$pattern[1]]) && isset($targets[$pattern[0]])) {
$target = $targets[$pattern[0]];
$source = $sources[$pattern[1]];
$currentoption = $options[$source->id];
$currentoption->correctanswers[$target->id] = $target->description;
}
}
// Fill in user responses.
foreach ($this->response as $response) {
if (!is_array($response) || count($response) != 2) {
continue;
}
if (isset($sources[$response[1]]) && isset($targets[$response[0]])) {
$source = $sources[$response[1]];
$target = $targets[$response[0]];
$answer = $response[0];
$option = $options[$source->id] ?? null;
if ($option) {
if (isset($option->correctanswers[$answer])) {
$state = parent::CORRECT;
} else {
$state = parent::INCORRECT;
}
$option->useranswer = $this->get_answer($state, $target->description);
}
}
}
// Fill in unchecked options.
foreach ($options as $option) {
if (!isset($option->useranswer)) {
if (!empty($option->correctanswers)) {
$option->useranswer = $this->get_answer(parent::INCORRECT,
get_string('answer_noanswer', 'mod_h5pactivity'));
} else {
$option->useranswer = $this->get_answer(parent::CORRECT,
get_string('answer_noanswer', 'mod_h5pactivity'));
}
}
}
// Now flattern correct answers.
foreach ($options as $option) {
$option->correctanswer = $this->get_answer( parent::TEXT, join(', ', $option->correctanswers));
unset($option->correctanswers);
}
return array_values($options);
}
/**
* Return a label for result user options/choices
*
* Specific result types can override this method to customize
* the result options table header.
*
* @return string to use in options table
*/
protected function get_optionslabel(): string {
return get_string('result_matching', 'mod_h5pactivity');
}
}
@@ -0,0 +1,54 @@
<?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/>.
/**
* Contains class mod_h5pactivity\output\result\other
*
* @package mod_h5pactivity
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_h5pactivity\output\result;
defined('MOODLE_INTERNAL') || die();
use mod_h5pactivity\output\result;
use renderer_base;
use stdClass;
/**
* Class to display H5P other result.
*
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class other extends result {
/**
* Export this data so it can be used as the context for a mustache template.
*
* @param renderer_base $output
* @return stdClass
*/
public function export_for_template(renderer_base $output): stdClass {
$data = parent::export_for_template($output);
if (empty($data->description)) {
$data->description = get_string('result_other', 'mod_h5pactivity');
}
return $data;
}
}
@@ -0,0 +1,102 @@
<?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/>.
/**
* Contains class mod_h5pactivity\output\result\sequencing
*
* @package mod_h5pactivity
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_h5pactivity\output\result;
defined('MOODLE_INTERNAL') || die();
use mod_h5pactivity\output\result;
use renderer_base;
/**
* Class to display H5P sequencing result.
*
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class sequencing extends result {
/**
* Return the options data structure.
*
* @return array of options
*/
protected function export_options(): ?array {
$correctpattern = reset($this->correctpattern);
$additionals = $this->additionals;
$response = $this->response;
if (isset($additionals->choices)) {
$choices = $this->get_descriptions($additionals->choices);
} else {
$choices = [];
}
$options = [];
$num = 1;
foreach ($correctpattern as $key => $pattern) {
if (!isset($choices[$pattern])) {
continue;
}
$option = (object)[
'id' => 'true',
'description' => get_string('result_sequencing_position', 'mod_h5pactivity', $num),
'correctanswer' => $this->get_answer(parent::TEXT, $choices[$pattern]->description),
'correctanswerid' => $key,
];
if (isset($response[$key])) {
$responseid = str_replace('item_', '', $response[$key]);
$answerstate = ($responseid == $option->correctanswerid) ? parent::PASS : parent::FAIL;
} else {
$answerstate = parent::FAIL;
}
$option->useranswer = $this->get_answer($answerstate);
$options[$key] = $option;
$num ++;
}
return $options;
}
/**
* Return a label for result user options/choices.
*
* @return string to use in options table
*/
protected function get_optionslabel(): string {
return get_string('result_sequencing_choice', 'mod_h5pactivity');
}
/**
* Return a label for result user correct answer.
*
* @return string to use in options table
*/
protected function get_correctlabel(): string {
return get_string('result_sequencing_answer', 'mod_h5pactivity');
}
}
@@ -0,0 +1,78 @@
<?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/>.
/**
* Contains class mod_h5pactivity\output\result\truefalse
*
* @package mod_h5pactivity
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace mod_h5pactivity\output\result;
defined('MOODLE_INTERNAL') || die();
use mod_h5pactivity\output\result;
use renderer_base;
/**
* Class to display H5P choice result.
*
* @copyright 2020 Ferran Recio
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class truefalse extends result {
/**
* Return the options data structure.
*
* @return array of options
*/
protected function export_options(): ?array {
// This interaction type have only one entry which is the correct option.
$correctpattern = reset($this->correctpattern);
$correctpattern = filter_var(reset($correctpattern), FILTER_VALIDATE_BOOLEAN);
$correctpattern = $correctpattern ? 'true' : 'false';
$response = filter_var(reset($this->response), FILTER_VALIDATE_BOOLEAN);
$response = $response ? 'true' : 'false';
$options = [
(object)[
'id' => 'true',
'description' => get_string('true', 'mod_h5pactivity'),
],
(object)[
'id' => 'false',
'description' => get_string('false', 'mod_h5pactivity'),
],
];
foreach ($options as $value) {
$correctstate = ($value->id == $correctpattern) ? parent::CHECKED : parent::UNCHECKED;
if ($value->id == $response) {
$answerstate = ($correctstate == parent::CHECKED) ? parent::PASS : parent::FAIL;
$value->useranswer = $this->get_answer($answerstate);
}
$value->correctanswer = $this->get_answer($correctstate);
}
return $options;
}
}