MDL-67734 core_xapi: add xAPI statement support webservice
This commit is contained in:
@@ -0,0 +1,26 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Strings for xapi library, language 'en'
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
$string['eventxapipost'] = 'Post xAPI statement';
|
||||
$string['privacy:metadata'] = 'The xAPI library does not store any personal data.';
|
||||
+2
-1
@@ -108,6 +108,7 @@
|
||||
"timezones": null,
|
||||
"user": "user",
|
||||
"userkey": "lib\/userkey",
|
||||
"webservice": "webservice"
|
||||
"webservice": "webservice",
|
||||
"xapi": "lib\/xapi"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2744,6 +2744,16 @@ $functions = array(
|
||||
'capabilities' => '',
|
||||
'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE],
|
||||
],
|
||||
'core_xapi_statement_post' => [
|
||||
'classname' => 'core_xapi\external\post_statement',
|
||||
'methodname' => 'execute',
|
||||
'classpath' => '',
|
||||
'description' => 'Post an xAPI statement.',
|
||||
'type' => 'write',
|
||||
'ajax' => 'true',
|
||||
'capabilities' => '',
|
||||
'services' => [MOODLE_OFFICIAL_MOBILE_SERVICE],
|
||||
],
|
||||
);
|
||||
|
||||
$services = array(
|
||||
|
||||
@@ -36,7 +36,7 @@ class core_component_testcase extends advanced_testcase {
|
||||
* this is defined here to annoy devs that try to add more without any thinking,
|
||||
* always verify that it does not collide with any existing add-on modules and subplugins!!!
|
||||
*/
|
||||
const SUBSYSTEMCOUNT = 69;
|
||||
const SUBSYSTEMCOUNT = 70;
|
||||
|
||||
public function setUp() {
|
||||
$psr0namespaces = new ReflectionProperty('core_component', 'psr0namespaces');
|
||||
|
||||
+178
@@ -0,0 +1,178 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* This is the external API for generic xAPI handling.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @since Moodle 3.9
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi\external;
|
||||
|
||||
use core_xapi\local\statement;
|
||||
use core_xapi\handler;
|
||||
use core_xapi\xapi_exception;
|
||||
use external_api;
|
||||
use external_function_parameters;
|
||||
use external_value;
|
||||
use external_single_structure;
|
||||
use external_multiple_structure;
|
||||
use external_warnings;
|
||||
use core_component;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once($CFG->libdir .'/externallib.php');
|
||||
|
||||
/**
|
||||
* This is the external API for generic xAPI handling.
|
||||
*
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class post_statement extends external_api {
|
||||
|
||||
/**
|
||||
* Parameters for execute
|
||||
*
|
||||
* @return external_function_parameters
|
||||
*/
|
||||
public static function execute_parameters() {
|
||||
return new external_function_parameters(
|
||||
[
|
||||
'component' => new external_value(PARAM_COMPONENT, 'Component name', VALUE_REQUIRED),
|
||||
'requestjson' => new external_value(PARAM_RAW, 'json object with all the statements to post', VALUE_REQUIRED)
|
||||
]
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a statement post request.
|
||||
*
|
||||
* @param string $component component name (frankenstyle)
|
||||
* @param string $requestjson json object with all the statements to post
|
||||
* @return bool[] storing acceptance of every statement
|
||||
*/
|
||||
public static function execute(string $component, string $requestjson): array {
|
||||
|
||||
$params = self::validate_parameters(self::execute_parameters(), array(
|
||||
'component' => $component,
|
||||
'requestjson' => $requestjson,
|
||||
));
|
||||
$component = $params['component'];
|
||||
$requestjson = $params['requestjson'];
|
||||
|
||||
static::validate_component($component);
|
||||
|
||||
$handler = handler::create($component);
|
||||
|
||||
$statements = self::get_statements_from_json($requestjson);
|
||||
|
||||
if (!self::check_statements_users($statements, $handler)) {
|
||||
throw new xapi_exception('Statements actor is not the current user');
|
||||
}
|
||||
|
||||
$result = $handler->process_statements($statements);
|
||||
|
||||
// In case no statement is processed, an error must be returned.
|
||||
if (count(array_filter($result)) == 0) {
|
||||
throw new xapi_exception('No statement can be processed.');
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return for execute.
|
||||
*/
|
||||
public static function execute_returns() {
|
||||
return new external_multiple_structure(
|
||||
new external_value(PARAM_BOOL, 'If the statement is accepted'),
|
||||
'List of statements storing acceptance results'
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Check component name.
|
||||
*
|
||||
* Note: this function is separated mainly for testing purposes to
|
||||
* be overridden to fake components.
|
||||
*
|
||||
* @throws xapi_exception if component is not available
|
||||
* @param string $component component name
|
||||
*/
|
||||
protected static function validate_component(string $component): void {
|
||||
// Check that $component is a real component name.
|
||||
$dir = core_component::get_component_directory($component);
|
||||
if (!$dir) {
|
||||
throw new xapi_exception("Component $component not available.");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert mulitple types of statement request into an array of statements.
|
||||
*
|
||||
* @throws xapi_exception if JSON cannot be parsed
|
||||
* @param string $requestjson json encoded statements structure
|
||||
* @return statement[] array of statements
|
||||
*/
|
||||
private static function get_statements_from_json(string $requestjson): array {
|
||||
$request = json_decode($requestjson);
|
||||
if ($request === null) {
|
||||
throw new xapi_exception('JSON error: '.json_last_error_msg());
|
||||
}
|
||||
$result = [];
|
||||
if (is_array($request)) {
|
||||
foreach ($request as $data) {
|
||||
$result[] = statement::create_from_data($data);
|
||||
}
|
||||
} else {
|
||||
$result[] = statement::create_from_data($request);
|
||||
}
|
||||
if (empty($result)) {
|
||||
throw new xapi_exception('No statements detected');
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check that $USER is actor in all statements.
|
||||
*
|
||||
* @param statement[] $statements array of statements
|
||||
* @param handler $handler specific xAPI handler
|
||||
* @return bool if $USER is actor in all statements
|
||||
*/
|
||||
private static function check_statements_users(array $statements, handler $handler): bool {
|
||||
global $USER;
|
||||
|
||||
foreach ($statements as $statement) {
|
||||
if ($handler->supports_group_actors()) {
|
||||
$users = $statement->get_all_users();
|
||||
if (!isset($users[$USER->id])) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
$user = $statement->get_user();
|
||||
if ($user->id != $USER->id) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -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/>.
|
||||
|
||||
/**
|
||||
* The core_xapi statement validation and tansformation.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @since Moodle 3.9
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi;
|
||||
|
||||
use core_xapi\local\statement;
|
||||
use core_xapi\xapi_exception;
|
||||
use stdClass;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Class handler handles basic xapi statements.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
*/
|
||||
abstract class handler {
|
||||
|
||||
/** @var string component name in frankenstyle. */
|
||||
protected $component;
|
||||
|
||||
/**
|
||||
* Constructor for a xAPI handler base class.
|
||||
*
|
||||
* @param string $component the component name
|
||||
*/
|
||||
final protected function __construct(string $component) {
|
||||
$this->component = $component;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the xAPI handler of a specific component.
|
||||
*
|
||||
* @param string $component the component name in frankenstyle.
|
||||
* @return handler|null a handler object or null if none found.
|
||||
* @throws xapi_exception
|
||||
*/
|
||||
final public static function create(string $component): self {
|
||||
$classname = "\\$component\\xapi\\handler";
|
||||
if (class_exists($classname)) {
|
||||
return new $classname($component);
|
||||
}
|
||||
throw new xapi_exception('Unknown handler');
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert a statement object into a Moodle xAPI Event.
|
||||
*
|
||||
* If a statement is accepted by validate_statement the component must provide a event
|
||||
* to handle that statement, otherwise the statement will be rejected.
|
||||
*
|
||||
* Note: this method must be overridden by the plugins which want to use xAPI.
|
||||
*
|
||||
* @param statement $statement
|
||||
* @return \core\event\base|null a Moodle event to trigger
|
||||
*/
|
||||
abstract public function statement_to_event(statement $statement): ?\core\event\base;
|
||||
|
||||
/**
|
||||
* Return true if group actor is enabled.
|
||||
*
|
||||
* Note: this method must be overridden by the plugins which want to
|
||||
* use groups in statements.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_group_actors(): bool {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process a bunch of statements sended to a specific component.
|
||||
*
|
||||
* @param statement[] $statements an array with all statement to process.
|
||||
* @return int[] return an specifying what statements are being stored.
|
||||
*/
|
||||
public function process_statements(array $statements): array {
|
||||
$result = [];
|
||||
foreach ($statements as $key => $statement) {
|
||||
try {
|
||||
// Ask the plugin to convert into an event.
|
||||
$event = $this->statement_to_event($statement);
|
||||
if ($event) {
|
||||
$event->trigger();
|
||||
$result[$key] = true;
|
||||
} else {
|
||||
$result[$key] = false;
|
||||
}
|
||||
} catch (\Exception $e) {
|
||||
$result[$key] = false;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* xAPI LRS IRI values generator.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @since Moodle 3.9
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace core_xapi;
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
use stdClass;
|
||||
use moodle_url;
|
||||
|
||||
/**
|
||||
* Class to translate Moodle objects to xAPI elements.
|
||||
*
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @since Moodle 3.9
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class iri {
|
||||
|
||||
/**
|
||||
* Generate a valid IRI element from a $value and an optional $type.
|
||||
*
|
||||
* Verbs and Objects in xAPI are in IRI format. This function could get
|
||||
* a valid IRI value (and will return without modifiyng it) or a simple
|
||||
* string and a type and generate a fake IRI valir for any xAPI statement.
|
||||
*
|
||||
* @param string $value a valid IRI value or any string
|
||||
* @param string|null $type if none passed $type will be 'element'
|
||||
* @return string a valid IRI value
|
||||
*/
|
||||
public static function generate(string $value, string $type = null): string {
|
||||
if (self::check($value)) {
|
||||
return $value;
|
||||
}
|
||||
if (empty($type)) {
|
||||
$type = 'element';
|
||||
}
|
||||
return (new moodle_url("/xapi/$type/$value"))->out(false);
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to extract the original value from an IRI.
|
||||
*
|
||||
* If a real IRI value is passed, it will return it without any change. If a
|
||||
* fake IRI is passed (generated by iri::generate)
|
||||
* it will try to extract the original value.
|
||||
*
|
||||
* @param string $value the currewnt IRI value.
|
||||
* @param string|null $type if $value is a fake IRI, the $type must be provided.
|
||||
* @return string the original value used in iri::generate.
|
||||
*/
|
||||
public static function extract(string $value, string $type = null): string {
|
||||
if (empty($type)) {
|
||||
$type = 'element';
|
||||
}
|
||||
$xapibase = (new moodle_url("/xapi/$type/"))->out(false);
|
||||
if (strpos($value, $xapibase) === 0) {
|
||||
return substr($value, strlen($xapibase));
|
||||
}
|
||||
return $value;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a $value could be a valid IRI or not.
|
||||
*
|
||||
* @param string $value the currewnt IRI value.
|
||||
* @return bool if the $value could be an IRI.
|
||||
*/
|
||||
public static function check(string $value): bool {
|
||||
$iri = new moodle_url($value);
|
||||
return in_array($iri->get_scheme(), ['http', 'https']);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,401 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Statement base object for xAPI structure checking and validation.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi\local;
|
||||
|
||||
use core_xapi\local\statement\item;
|
||||
use core_xapi\local\statement\item_actor;
|
||||
use core_xapi\local\statement\item_object;
|
||||
use core_xapi\local\statement\item_verb;
|
||||
use core_xapi\xapi_exception;
|
||||
use JsonSerializable;
|
||||
use stdClass;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for core_xapi implementing null_provider.
|
||||
*
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class statement implements JsonSerializable {
|
||||
|
||||
/** @var actor The statement actor. */
|
||||
protected $actor = null;
|
||||
|
||||
/** @var verb The statement verb. */
|
||||
protected $verb = null;
|
||||
|
||||
/** @var object The statement object. */
|
||||
protected $object = null;
|
||||
|
||||
/** @var result The statement result. */
|
||||
protected $result = null;
|
||||
|
||||
/** @var context The statement context. */
|
||||
protected $context = null;
|
||||
|
||||
/** @var timestamp The statement timestamp. */
|
||||
protected $timestamp = null;
|
||||
|
||||
/** @var stored The statement stored. */
|
||||
protected $stored = null;
|
||||
|
||||
/** @var authority The statement authority. */
|
||||
protected $authority = null;
|
||||
|
||||
/** @var version The statement version. */
|
||||
protected $version = null;
|
||||
|
||||
/** @var attachments The statement attachments. */
|
||||
protected $attachments = null;
|
||||
|
||||
/** @var additionalfields list of additional fields. */
|
||||
private static $additionalsfields = [
|
||||
'context', 'result', 'timestamp', 'stored', 'authority', 'version', 'attachments'
|
||||
];
|
||||
|
||||
/**
|
||||
* Function to create a full statement from xAPI statement data.
|
||||
*
|
||||
* @param stdClass $data the original xAPI statement
|
||||
* @return statement statement object
|
||||
*/
|
||||
public static function create_from_data(stdClass $data): self {
|
||||
|
||||
$result = new self();
|
||||
|
||||
$requiredfields = ['actor', 'verb', 'object'];
|
||||
foreach ($requiredfields as $required) {
|
||||
if (!isset($data->$required)) {
|
||||
throw new xapi_exception("Missing '{$required}'");
|
||||
}
|
||||
}
|
||||
$result->set_actor(item_actor::create_from_data($data->actor));
|
||||
$result->set_verb(item_verb::create_from_data($data->verb));
|
||||
$result->set_object(item_object::create_from_data($data->object));
|
||||
|
||||
// Store other generic xAPI statement fields.
|
||||
foreach (self::$additionalsfields as $additional) {
|
||||
if (isset($data->$additional)) {
|
||||
$method = 'set_'.$additional;
|
||||
$result->$method(item::create_from_data($data->$additional));
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the data to serialize in case JSON statement is needed.
|
||||
*
|
||||
* @return stdClass the statement data structure
|
||||
*/
|
||||
public function jsonSerialize(): stdClass {
|
||||
$result = (object) [
|
||||
'actor' => $this->actor,
|
||||
'verb' => $this->verb,
|
||||
'object' => $this->object,
|
||||
];
|
||||
foreach (self::$additionalsfields as $additional) {
|
||||
if (!empty($this->$additional)) {
|
||||
$result->$additional = $this->$additional;
|
||||
}
|
||||
}
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a minified version of a given statement.
|
||||
*
|
||||
* The returned structure is suitable to store in the "other" field
|
||||
* of logstore. xAPI standard specifies a list of attributes that can be calculated
|
||||
* instead of stored literally. This function get rid of these attributes.
|
||||
*
|
||||
* Note: it also converts stdClass to assoc array to make it compatible
|
||||
* with "other" field in the logstore
|
||||
*
|
||||
* @return array the minimal statement needed to be stored a part from logstore data
|
||||
*/
|
||||
public function minify(): ?array {
|
||||
$result = [];
|
||||
$fields = ['verb', 'object', 'context', 'result', 'authority', 'attachments'];
|
||||
foreach ($fields as $field) {
|
||||
if (!empty($this->$field)) {
|
||||
$result[$field] = $this->$field;
|
||||
}
|
||||
}
|
||||
return json_decode(json_encode($result), true);
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the statement actor.
|
||||
*
|
||||
* @param item_actor $actor actor item
|
||||
*/
|
||||
public function set_actor(item_actor $actor): void {
|
||||
$this->actor = $actor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the statement verb.
|
||||
*
|
||||
* @param item_verb $verb verb element
|
||||
*/
|
||||
public function set_verb(item_verb $verb): void {
|
||||
$this->verb = $verb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the statement object.
|
||||
*
|
||||
* @param item_object $object compatible object item
|
||||
*/
|
||||
public function set_object(item_object $object): void {
|
||||
$this->object = $object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the statement context.
|
||||
*
|
||||
* @param item $context context item element
|
||||
*/
|
||||
public function set_context(item $context): void {
|
||||
$this->context = $context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the statement result.
|
||||
*
|
||||
* @param item $result result item element
|
||||
*/
|
||||
public function set_result(item $result): void {
|
||||
$this->result = $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the statement timestamp.
|
||||
*
|
||||
* @param item $timestamp timestamp item element
|
||||
*/
|
||||
public function set_timestamp(item $timestamp): void {
|
||||
$this->timestamp = $timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the statement stored.
|
||||
*
|
||||
* @param item $stored stored item element
|
||||
*/
|
||||
public function set_stored(item $stored): void {
|
||||
$this->stored = $stored;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the statement authority.
|
||||
*
|
||||
* @param item $authority authority item element
|
||||
*/
|
||||
public function set_authority(item $authority): void {
|
||||
$this->authority = $authority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the statement version.
|
||||
*
|
||||
* @param item $version version item element
|
||||
*/
|
||||
public function set_version(item $version): void {
|
||||
$this->version = $version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the statement attachments.
|
||||
*
|
||||
* @param item $attachments attachments item element
|
||||
*/
|
||||
public function set_attachments(item $attachments): void {
|
||||
$this->attachments = $attachments;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the moodle user represented by this statement actor.
|
||||
*
|
||||
* @throws xapi_exception if it's a group statement
|
||||
* @return stdClass user record
|
||||
*/
|
||||
public function get_user(): stdClass {
|
||||
if (!$this->actor) {
|
||||
throw new xapi_exception("No actor defined");
|
||||
}
|
||||
return $this->actor->get_user();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all moodle users represented by this statement actor.
|
||||
*
|
||||
* @return array user records
|
||||
*/
|
||||
public function get_all_users(): array {
|
||||
if (!$this->actor) {
|
||||
throw new xapi_exception("No actor defined");
|
||||
}
|
||||
return $this->actor->get_all_users();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the moodle group represented by this statement actor.
|
||||
*
|
||||
* @throws xapi_exception if it is not a group statement
|
||||
* @return stdClass a group record
|
||||
*/
|
||||
public function get_group(): stdClass {
|
||||
if (!$this->actor) {
|
||||
throw new xapi_exception("No actor defined");
|
||||
}
|
||||
if (method_exists($this->actor, 'get_group')) {
|
||||
return $this->actor->get_group();
|
||||
}
|
||||
throw new xapi_exception("Method not valid on this actor");
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the statement verb ID.
|
||||
*
|
||||
* @throws xapi_exception in case the item is no yet defined
|
||||
* @return string verb ID
|
||||
*/
|
||||
public function get_verb_id(): string {
|
||||
if (!$this->verb) {
|
||||
throw new xapi_exception("No verb defined");
|
||||
}
|
||||
return $this->verb->get_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the statement activity ID.
|
||||
*
|
||||
* @throws xapi_exception in case the item is no yet defined
|
||||
* @return string activity ID
|
||||
*/
|
||||
public function get_activity_id(): string {
|
||||
if (!$this->object) {
|
||||
throw new xapi_exception("No object defined");
|
||||
}
|
||||
if (method_exists($this->object, 'get_id')) {
|
||||
return $this->object->get_id();
|
||||
}
|
||||
throw new xapi_exception("Method not valid on this object");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the statement actor if it is defined.
|
||||
*
|
||||
* @return item_actor|null
|
||||
*/
|
||||
public function get_actor(): ?item_actor {
|
||||
return $this->actor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the statement verb if it is defined.
|
||||
*
|
||||
* @return item_verb|null
|
||||
*/
|
||||
public function get_verb(): ?item_verb {
|
||||
return $this->verb;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the statement object if it is defined.
|
||||
*
|
||||
* @return item_object|null
|
||||
*/
|
||||
public function get_object(): ?item_object {
|
||||
return $this->object;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the statement context if it is defined.
|
||||
*
|
||||
* @return item|null
|
||||
*/
|
||||
public function get_context(): ?item {
|
||||
return $this->context;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the statement result if it is defined.
|
||||
*
|
||||
* @return item|null
|
||||
*/
|
||||
public function get_result(): ?item {
|
||||
return $this->result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the statement timestamp if it is defined.
|
||||
*
|
||||
* @return item|null
|
||||
*/
|
||||
public function get_timestamp(): ?item {
|
||||
return $this->timestamp;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the statement stored if it is defined.
|
||||
*
|
||||
* @return item|null
|
||||
*/
|
||||
public function get_stored(): ?item {
|
||||
return $this->stored;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the statement authority if it is defined.
|
||||
*
|
||||
* @return item|null
|
||||
*/
|
||||
public function get_authority(): ?item {
|
||||
return $this->authority;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the statement version if it is defined.
|
||||
*
|
||||
* @return item|null
|
||||
*/
|
||||
public function get_version(): ?item {
|
||||
return $this->version;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the statement attachments if it is defined.
|
||||
*
|
||||
* @return item|null
|
||||
*/
|
||||
public function get_attachments(): ?item {
|
||||
return $this->attachments;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Statement base object for xAPI structure checking and usage.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi\local\statement;
|
||||
use stdClass;
|
||||
use JsonSerializable;
|
||||
use core_xapi\iri;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Item class used for xAPI statement elements without validation.
|
||||
*
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class item implements JsonSerializable {
|
||||
|
||||
/** @var stdClass the item structure. */
|
||||
protected $data;
|
||||
|
||||
/**
|
||||
* Item constructor.
|
||||
*
|
||||
* @param stdClass $data from the specific xAPI element
|
||||
*/
|
||||
protected function __construct(stdClass $data) {
|
||||
$this->data = $data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to create an item from part of the xAPI statement.
|
||||
*
|
||||
* @param stdClass $data the original xAPI element
|
||||
* @return item the xAPI item generated
|
||||
*/
|
||||
public static function create_from_data(stdClass $data): item {
|
||||
return new self($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the data to serialize in case JSON statement is needed.
|
||||
*
|
||||
* @return stdClass the original data structure
|
||||
*/
|
||||
public function jsonSerialize(): stdClass {
|
||||
return $this->get_data();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the original data from this item.
|
||||
*
|
||||
* @return stdClass the original data structure
|
||||
*/
|
||||
public function get_data(): stdClass {
|
||||
return $this->data;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Statement activity object for xAPI structure checking and usage.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi\local\statement;
|
||||
|
||||
use core_xapi\xapi_exception;
|
||||
use core_xapi\iri;
|
||||
use stdClass;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Class that implements a xAPI activity compatible with xAPI object.
|
||||
*
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class item_activity extends item_object {
|
||||
|
||||
/** @var string Activity ID. */
|
||||
protected $id;
|
||||
|
||||
/** @var item_definition Definition object. */
|
||||
protected $definition;
|
||||
|
||||
/**
|
||||
* Item activity constructor.
|
||||
*
|
||||
* An xAPI activity is mainly an IRI ID and an optional definition.
|
||||
*
|
||||
* @param stdClass $data from the specific xAPI element
|
||||
* @param item_definition $definition option definition item
|
||||
*/
|
||||
protected function __construct(stdClass $data, item_definition $definition = null) {
|
||||
parent::__construct($data);
|
||||
$this->id = iri::extract($data->id, 'activity');
|
||||
$this->definition = $definition;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to create an item from part of the xAPI statement.
|
||||
*
|
||||
* @param stdClass $data the original xAPI element
|
||||
* @return item item_activity xAPI generated
|
||||
*/
|
||||
public static function create_from_data(stdClass $data): item {
|
||||
if (!isset($data->objectType)) {
|
||||
throw new xapi_exception('Missing activity objectType');
|
||||
}
|
||||
if ($data->objectType != 'Activity') {
|
||||
throw new xapi_exception('Activity objectType must be "Activity"');
|
||||
}
|
||||
if (empty($data->id)) {
|
||||
throw new xapi_exception("Missing Activity id");
|
||||
}
|
||||
if (!iri::check($data->id)) {
|
||||
throw new xapi_exception("Activity id $data->id is not a valid IRI");
|
||||
}
|
||||
|
||||
$definition = null;
|
||||
if (!empty($data->definition)) {
|
||||
$definition = item_definition::create_from_data($data->definition);
|
||||
}
|
||||
|
||||
return new self($data, $definition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a valid item_activity from a simple ID string and an optional definition.
|
||||
*
|
||||
* @param string $id any string that will converted into a valid IRI
|
||||
* @param item_definition|null $definition optional item_definition
|
||||
* @return item_activity
|
||||
*/
|
||||
public static function create_from_id(string $id, item_definition $definition = null): item_activity {
|
||||
$data = (object) [
|
||||
'objectType' => 'Activity',
|
||||
'id' => iri::generate($id, 'activity'),
|
||||
];
|
||||
|
||||
if (!empty($definition)) {
|
||||
$data->definition = $definition->get_data();
|
||||
}
|
||||
|
||||
return new self($data, $definition);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the activity ID.
|
||||
*
|
||||
* If the ID was generated by iri::generate this function will return
|
||||
* the iri:extract value.
|
||||
*
|
||||
* @return string the activity ID
|
||||
*/
|
||||
public function get_id(): string {
|
||||
return $this->id;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the item_definition of this item.
|
||||
*
|
||||
* @return item_definition|null the item definition if available
|
||||
*/
|
||||
public function get_definition(): ?item_definition {
|
||||
return $this->definition;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Statement actor (user or group) object for xAPI structure checking and usage.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi\local\statement;
|
||||
|
||||
use core_xapi\xapi_exception;
|
||||
use stdClass;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Abstract xAPI actor class.
|
||||
*
|
||||
* This class extends from item_object instead of basic item
|
||||
* because both actors (agent and group) could be used as
|
||||
* statement actor or object.
|
||||
*
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class item_actor extends item_object {
|
||||
|
||||
/**
|
||||
* Function to create an actor from part of the xAPI statement.
|
||||
*
|
||||
* @param stdClass $data the original xAPI element
|
||||
* @return item item_agent|item_grou|item_activity xAPI generated
|
||||
*/
|
||||
public static function create_from_data(stdClass $data): item {
|
||||
if (!isset($data->objectType)) {
|
||||
$data->objectType = 'Agent';
|
||||
}
|
||||
switch ($data->objectType) {
|
||||
case 'Agent':
|
||||
return item_agent::create_from_data($data);
|
||||
break;
|
||||
case 'Group':
|
||||
return item_group::create_from_data($data);
|
||||
break;
|
||||
default:
|
||||
throw new xapi_exception("Unknown Actor type '{$data->objectType}'");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the moodle user represented by this item.
|
||||
*
|
||||
* @return stdClass user record
|
||||
*/
|
||||
abstract public function get_user(): stdClass;
|
||||
|
||||
/**
|
||||
* Return all moodle users represented by this item.
|
||||
*
|
||||
* @return array user records
|
||||
*/
|
||||
abstract public function get_all_users(): array;
|
||||
}
|
||||
@@ -0,0 +1,142 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Statement agent (user) object for xAPI structure checking and usage.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi\local\statement;
|
||||
|
||||
use core_xapi\xapi_exception;
|
||||
use core_user;
|
||||
use stdClass;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Agent xAPI statement element representing a Moodle user.
|
||||
*
|
||||
* Agents can be used either as actor or object in a statement.
|
||||
*
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class item_agent extends item_actor {
|
||||
|
||||
/** @var stdClass The user record of this actor. */
|
||||
protected $user;
|
||||
|
||||
/**
|
||||
* Function to create an agent (user) from part of the xAPI statement.
|
||||
*
|
||||
* @param stdClass $data the original xAPI element
|
||||
* @param stdClass $user user record
|
||||
*/
|
||||
protected function __construct(stdClass $data, stdClass $user) {
|
||||
parent::__construct($data);
|
||||
$this->user = $user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to create an item from part of the xAPI statement.
|
||||
*
|
||||
* @param stdClass $data the original xAPI element
|
||||
* @return item item_agentxAPI generated
|
||||
*/
|
||||
public static function create_from_data(stdClass $data): item {
|
||||
global $CFG;
|
||||
if (!isset($data->objectType)) {
|
||||
throw new xapi_exception('Missing agent objectType');
|
||||
}
|
||||
if ($data->objectType != 'Agent') {
|
||||
throw new xapi_exception("Agent objectType must be 'Agent'");
|
||||
}
|
||||
if (isset($data->account) && isset($data->mbox)) {
|
||||
throw new xapi_exception("Agent cannot have more than one identifier");
|
||||
}
|
||||
$user = null;
|
||||
if (!empty($data->account)) {
|
||||
if ($data->account->homePage != $CFG->wwwroot) {
|
||||
throw new xapi_exception("Invalid agent homePage '{$data->account->homePage}'");
|
||||
}
|
||||
if (!is_numeric($data->account->name)) {
|
||||
throw new xapi_exception("Agent account name must be integer '{$data->account->name}' found");
|
||||
}
|
||||
$user = core_user::get_user($data->account->name);
|
||||
if (empty($user)) {
|
||||
throw new xapi_exception("Inexistent agent '{$data->account->name}'");
|
||||
}
|
||||
}
|
||||
if (!empty($data->mbox)) {
|
||||
$mbox = str_replace('mailto:', '', $data->mbox);
|
||||
$user = core_user::get_user_by_email($mbox);
|
||||
if (empty($user)) {
|
||||
throw new xapi_exception("Inexistent agent '{$data->mbox}'");
|
||||
}
|
||||
}
|
||||
if (empty($user)) {
|
||||
throw new xapi_exception("Unsupported agent definition");
|
||||
}
|
||||
return new self($data, $user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a item_agent from a existing user.
|
||||
*
|
||||
* @param stdClass $user A user record.
|
||||
* @return item_agent
|
||||
*/
|
||||
public static function create_from_user(stdClass $user): item_agent {
|
||||
global $CFG;
|
||||
|
||||
if (!isset($user->id)) {
|
||||
throw new xapi_exception("Missing user id");
|
||||
}
|
||||
$data = (object) [
|
||||
'objectType' => 'Agent',
|
||||
'account' => (object) [
|
||||
'homePage' => $CFG->wwwroot,
|
||||
'name' => $user->id,
|
||||
],
|
||||
];
|
||||
return new self($data, $user);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the moodle user represented by this item.
|
||||
*
|
||||
* @return stdClass user record
|
||||
*/
|
||||
public function get_user(): stdClass {
|
||||
return $this->user;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all users represented by this item.
|
||||
*
|
||||
* In this case the item is an agent so a single element array
|
||||
* will be returned always.
|
||||
*
|
||||
* @return array list of users
|
||||
*/
|
||||
public function get_all_users(): array {
|
||||
return [$this->user->id => $this->user];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,93 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Statement definition object for xAPI structure checking and usage.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi\local\statement;
|
||||
|
||||
use core_xapi\xapi_exception;
|
||||
use core_xapi\iri;
|
||||
use stdClass;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Validation and usage of xAPI definition.
|
||||
*
|
||||
* Definition contains extra information about user interaction with
|
||||
* questions and other activities inside a xAPI statement. For now
|
||||
* it performs a basic validation on the provided data.
|
||||
*
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class item_definition extends item {
|
||||
|
||||
/** @var string The statement. */
|
||||
protected $interactiontype;
|
||||
|
||||
/**
|
||||
* Function to create a definition from part of the xAPI statement.
|
||||
*
|
||||
* @param stdClass $data the original xAPI element.
|
||||
*/
|
||||
protected function __construct(stdClass $data) {
|
||||
parent::__construct($data);
|
||||
$this->interactiontype = $data->interactionType ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to create an item from part of the xAPI statement.
|
||||
*
|
||||
* @param stdClass $data the original xAPI element
|
||||
* @return item item_definition xAPI generated
|
||||
*/
|
||||
public static function create_from_data(stdClass $data): item {
|
||||
// Interaction Type is a optopnal param.
|
||||
if (!empty($data->interactionType)) {
|
||||
$posiblevalues = [
|
||||
'choice' => true,
|
||||
'fill-in' => true,
|
||||
'long-fill-in' => true,
|
||||
'true-false' => true,
|
||||
'matching' => true,
|
||||
'performance' => true,
|
||||
'sequencing' => true,
|
||||
'likert' => true,
|
||||
'numeric' => true,
|
||||
'other' => true,
|
||||
'compound' => true,
|
||||
];
|
||||
if (!isset($posiblevalues[$data->interactionType])) {
|
||||
throw new xapi_exception("Invalid definition \"{$data->interactionType}\"");
|
||||
}
|
||||
}
|
||||
return new self($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the definition interaction type.
|
||||
*/
|
||||
public function get_interactiontype(): ?string {
|
||||
return $this->interactiontype;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Statement group object for xAPI structure checking and usage.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi\local\statement;
|
||||
|
||||
use core_xapi\xapi_exception;
|
||||
use stdClass;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Group item inside a xAPI statement.
|
||||
*
|
||||
* Only named groups are accepted (all groups must be real groups in the
|
||||
* platform) so anonymous groups will be rejected on creation. Groups can
|
||||
* be used as actor or as object inside a xAPI statement.
|
||||
*
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class item_group extends item_actor {
|
||||
|
||||
/** @var timestamp The statement timestamp. */
|
||||
protected $users;
|
||||
|
||||
/** @var timestamp The statement timestamp. */
|
||||
protected $group;
|
||||
|
||||
/**
|
||||
* Function to create an group from part of the xAPI statement.
|
||||
*
|
||||
* @param stdClass $data the original xAPI element
|
||||
* @param stdClass $group group record
|
||||
*/
|
||||
protected function __construct(stdClass $data, stdClass $group) {
|
||||
parent::__construct($data);
|
||||
$this->group = $group;
|
||||
$this->users = groups_get_members($group->id);
|
||||
if (!$this->users) {
|
||||
$this->users = [];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to create an item from part of the xAPI statement.
|
||||
*
|
||||
* @param stdClass $data the original xAPI element
|
||||
* @return item item_group xAPI item generated
|
||||
*/
|
||||
public static function create_from_data(stdClass $data): item {
|
||||
global $CFG;
|
||||
if (!isset($data->objectType)) {
|
||||
throw new xapi_exception('Missing group objectType');
|
||||
}
|
||||
if ($data->objectType != 'Group') {
|
||||
throw new xapi_exception("Group objectType must be 'Group'");
|
||||
}
|
||||
if (!isset($data->account)) {
|
||||
throw new xapi_exception("Missing Group account");
|
||||
}
|
||||
if ($data->account->homePage != $CFG->wwwroot) {
|
||||
throw new xapi_exception("Invalid group homePage '{$data->account->homePage}'");
|
||||
}
|
||||
if (!is_numeric($data->account->name)) {
|
||||
throw new xapi_exception("Agent account name must be integer '{$data->account->name}' found");
|
||||
}
|
||||
$group = groups_get_group($data->account->name);
|
||||
if (empty($group)) {
|
||||
throw new xapi_exception("Inexistent group '{$data->account->name}'");
|
||||
}
|
||||
return new self($data, $group);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a item_group from a existing group.
|
||||
*
|
||||
* @param stdClass $group A group record.
|
||||
* @return item_group
|
||||
*/
|
||||
public static function create_from_group(stdClass $group): item_group {
|
||||
global $CFG;
|
||||
|
||||
if (!isset($group->id)) {
|
||||
throw new xapi_exception("Missing group id");
|
||||
}
|
||||
$data = (object) [
|
||||
'objectType' => 'Group',
|
||||
'account' => (object) [
|
||||
'homePage' => $CFG->wwwroot,
|
||||
'name' => $group->id,
|
||||
],
|
||||
];
|
||||
return new self($data, $group);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the moodle user represented by this item.
|
||||
*
|
||||
* This is a group item. To avoid security problems this method
|
||||
* thorws an exception when is called from a item_group class.
|
||||
*
|
||||
* @throws xapi_exception get_user must not be called from an item_group
|
||||
* @return stdClass user record
|
||||
*/
|
||||
public function get_user(): stdClass {
|
||||
throw new xapi_exception("Group statements cannot be used as a individual user");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return all users from the group represented by this item.
|
||||
*
|
||||
* @return array group users
|
||||
*/
|
||||
public function get_all_users(): array {
|
||||
return $this->users;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the moodle group represented by this item.
|
||||
*
|
||||
* @return stdClass a group record
|
||||
*/
|
||||
public function get_group(): stdClass {
|
||||
return $this->group;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,69 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Statement object (activity, user or group) for xAPI structure checking and usage.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi\local\statement;
|
||||
|
||||
use core_xapi\xapi_exception;
|
||||
use core_xapi\iri;
|
||||
use stdClass;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Abstract object item used in xAPI statements.
|
||||
*
|
||||
* Object represents the object in which a xAPI verb is applied. There
|
||||
* are 3 types of objects supported: agent (user), group (of users) and
|
||||
* activity (defined by every plugin).
|
||||
*
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
abstract class item_object extends item {
|
||||
|
||||
/**
|
||||
* Create a xAPI object compatible from data (Agent, Group or Activity).
|
||||
*
|
||||
* @param stdClass $data data structure from statement object
|
||||
* @return item item_group|item_agent|item_activity resulting object
|
||||
*/
|
||||
public static function create_from_data(stdClass $data): item {
|
||||
if (!isset($data->objectType)) {
|
||||
$data->objectType = 'Activity';
|
||||
}
|
||||
switch ($data->objectType) {
|
||||
case 'Agent':
|
||||
return item_agent::create_from_data($data);
|
||||
break;
|
||||
case 'Group':
|
||||
return item_group::create_from_data($data);
|
||||
break;
|
||||
case 'Activity':
|
||||
return item_activity::create_from_data($data);
|
||||
break;
|
||||
default:
|
||||
throw new xapi_exception("Unknown Object type '{$data->objectType}'");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Statement verb object for xAPI structure checking and usage.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi\local\statement;
|
||||
|
||||
use core_xapi\xapi_exception;
|
||||
use core_xapi\iri;
|
||||
use stdClass;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Verb xAPI statement item.
|
||||
*
|
||||
* Verbs represent the interaction a user/group made inside a xAPI
|
||||
* compatible plugin. Internally a xAPI verb must be representad as
|
||||
* in a valid IRI format (which is a less restrictive version of a
|
||||
* regular URL so a moodle_url out is completelly fine). To make it
|
||||
* easy for plugins to generate valid IRI, a simple string van be
|
||||
* provided and the class will convert into a valid IRI internally.
|
||||
*
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class item_verb extends item {
|
||||
|
||||
/** @var string The statement. */
|
||||
protected $id;
|
||||
|
||||
/**
|
||||
* An xAPI verb constructor based on xAPI data structure.
|
||||
*
|
||||
* @param stdClass $data from the specific xAPI element
|
||||
*/
|
||||
protected function __construct(stdClass $data) {
|
||||
parent::__construct($data);
|
||||
$this->id = iri::extract($data->id, 'verb');
|
||||
}
|
||||
|
||||
/**
|
||||
* Function to create an item from part of the xAPI statement.
|
||||
*
|
||||
* @param stdClass $data the original xAPI element
|
||||
* @return item item_verb xAPI generated
|
||||
*/
|
||||
public static function create_from_data(stdClass $data): item {
|
||||
if (empty($data->id)) {
|
||||
throw new xapi_exception("missing verb id");
|
||||
}
|
||||
if (!iri::check($data->id)) {
|
||||
throw new xapi_exception("verb id $data->id is not a valid IRI");
|
||||
}
|
||||
return new self($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a valid item_verb from a simple verb string.
|
||||
*
|
||||
* @param string $id string to convert to a valid IRI (or a valid IRI)
|
||||
* @return item_verb the resulting item_verb
|
||||
*/
|
||||
public static function create_from_id(string $id): item_verb {
|
||||
|
||||
$data = new stdClass();
|
||||
$data->id = iri::generate($id, 'verb');
|
||||
|
||||
return new self($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the id used in this item.
|
||||
*
|
||||
* Id will be extracted from the provided IRI. If it's a valid IRI
|
||||
* it will return all IRI value but if it is generate by the iri helper
|
||||
* from this library it will extract the original value.
|
||||
*
|
||||
* @return string the ID (extracted from IRI value)
|
||||
*/
|
||||
public function get_id(): string {
|
||||
return $this->id;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,46 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Privacy Subsystem implementation for core xAPI Library.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi\privacy;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Privacy Subsystem for core_xapi implementing null_provider.
|
||||
*
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class provider implements \core_privacy\local\metadata\null_provider {
|
||||
|
||||
/**
|
||||
* Get the language string identifier with the component's language
|
||||
* file to explain why this plugin stores no data.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_reason(): string {
|
||||
return 'privacy:metadata';
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* General xAPI invalid statement exception.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @since Moodle 3.9
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* General invalid xAPI exception.
|
||||
*
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class xapi_exception extends \moodle_exception {
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Coverage information for the core_xapi component.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @category test
|
||||
* @copyright 2020 Ferran Recio <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Coverage information for the core xAPI component.
|
||||
*
|
||||
* @copyright 2020 Ferran Recio <[email protected]>
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
return new class extends phpunit_coverage_info {
|
||||
/** @var array The list of folders relative to the plugin root to whitelist in coverage generation. */
|
||||
protected $whitelistfolders = [
|
||||
'classes',
|
||||
];
|
||||
|
||||
/** @var array The list of files relative to the plugin root to whitelist in coverage generation. */
|
||||
protected $whitelistfiles = [];
|
||||
|
||||
/** @var array The list of folders relative to the plugin root to excludelist in coverage generation. */
|
||||
protected $excludelistfolders = [];
|
||||
|
||||
/** @var array The list of files relative to the plugin root to excludelist in coverage generation. */
|
||||
protected $excludelistfiles = [];
|
||||
};
|
||||
+490
@@ -0,0 +1,490 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* This file contains unit test related to xAPI library.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace core_xapi\external;
|
||||
|
||||
use core_xapi\xapi_exception;
|
||||
use core_xapi\test_helper;
|
||||
use core_xapi\external\post_statement;
|
||||
use core_xapi\local\statement;
|
||||
use core_xapi\local\statement\item_agent;
|
||||
use core_xapi\local\statement\item_group;
|
||||
use core_xapi\local\statement\item_verb;
|
||||
use core_xapi\local\statement\item_activity;
|
||||
use externallib_advanced_testcase;
|
||||
use stdClass;
|
||||
use external_api;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
|
||||
|
||||
/**
|
||||
* Unit tests for xAPI statement processing webservice.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @since Moodle 3.9
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class post_statement_testcase extends externallib_advanced_testcase {
|
||||
|
||||
/** @var test_helper for generating valid xapi statements. */
|
||||
private $testhelper;
|
||||
|
||||
/**
|
||||
* Setup to ensure that fixtures are loaded.
|
||||
*/
|
||||
public static function setupBeforeClass(): void {
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot.'/lib/xapi/tests/helper.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Setup test.
|
||||
*/
|
||||
public function setUp() {
|
||||
global $CFG;
|
||||
// We disable group actors on the test xapi_handler.
|
||||
$CFG->xapitestforcegroupactors = false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a xAPI external webservice class to operate.
|
||||
*
|
||||
* The test needs to fake a component in order to test without
|
||||
* using a real one. This way if in the future any component
|
||||
* implement it's xAPI handler this test will continue working.
|
||||
*
|
||||
* @return post_statement the external class
|
||||
*/
|
||||
private function get_extenal_class(): post_statement {
|
||||
$ws = new class extends post_statement {
|
||||
protected static function validate_component(string $component): void {
|
||||
if ($component != 'fake_component') {
|
||||
parent::validate_component($component);
|
||||
}
|
||||
}
|
||||
};
|
||||
return $ws;
|
||||
}
|
||||
|
||||
/**
|
||||
* This function do all checks from a standard post_statements request.
|
||||
*
|
||||
* The reason for this function is because statements crafting (special in error
|
||||
* scenarios) is complicated to do via data providers because every test need a specific
|
||||
* testing conditions. For this reason alls tests creates a scenario and then uses this
|
||||
* function to check the results.
|
||||
*
|
||||
* @param string $component component name
|
||||
* @param mixed $data data to encode and send to post_statement
|
||||
* @param array $expected expected results (i empty an exception is expected)
|
||||
*/
|
||||
private function post_statements_data(string $component, $data, array $expected) {
|
||||
global $USER;
|
||||
|
||||
$testhelper = new test_helper();
|
||||
$testhelper->init_log();
|
||||
|
||||
// If no result is expected we will just incur in exception.
|
||||
if (empty($expected)) {
|
||||
$this->expectException(xapi_exception::class);
|
||||
} else {
|
||||
$this->preventResetByRollback(); // Logging waits till the transaction gets committed.
|
||||
}
|
||||
|
||||
$json = json_encode($data);
|
||||
|
||||
$external = $this->get_extenal_class();
|
||||
$result = $external::execute($component, $json);
|
||||
$result = external_api::clean_returnvalue($external::execute_returns(), $result);
|
||||
|
||||
// Check results.
|
||||
$this->assertCount(count($expected), $result);
|
||||
foreach ($expected as $key => $expect) {
|
||||
$this->assertEquals($expect, $result[$key]);
|
||||
}
|
||||
|
||||
// Check log entries.
|
||||
$log = $testhelper->get_last_log_entry();
|
||||
$this->assertNotEmpty($log);
|
||||
|
||||
// Validate statement information on log.
|
||||
$value = $log->get_name();
|
||||
$this->assertEquals($value, 'xAPI test statement');
|
||||
$value = $log->get_description();
|
||||
// Due to logstore limitation, event must use a real component (core_xapi).
|
||||
$this->assertEquals($value, "User '{$USER->id}' send a statement to component 'core_xapi'");
|
||||
}
|
||||
|
||||
/**
|
||||
* Return a valid statement object with the params passed.
|
||||
*
|
||||
* All tests are based on craft different types os statements. This function
|
||||
* is made to provent redundant code on the test.
|
||||
*
|
||||
* @param array $items array of overriden statement items (default [])
|
||||
* @return statement the resulting statement
|
||||
*/
|
||||
private function get_valid_statement(array $items = []): statement {
|
||||
global $USER;
|
||||
|
||||
$actor = $items['actor'] ?? item_agent::create_from_user($USER);
|
||||
$verb = $items['verb'] ?? item_verb::create_from_id('cook');
|
||||
$object = $items['object'] ?? item_activity::create_from_id('paella');
|
||||
|
||||
$statement = new statement();
|
||||
$statement->set_actor($actor);
|
||||
$statement->set_verb($verb);
|
||||
$statement->set_object($object);
|
||||
|
||||
return $statement;
|
||||
}
|
||||
|
||||
/**
|
||||
* Testing different component names on valid statements.
|
||||
*
|
||||
* @dataProvider components_provider
|
||||
* @param string $component component name
|
||||
* @param array $expected expected results
|
||||
*/
|
||||
public function test_component_names(string $component, array $expected) {
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Scenario.
|
||||
$this->setAdminUser();
|
||||
|
||||
// Perform test.
|
||||
$data = $this->get_valid_statement();
|
||||
$this->post_statements_data ($component, $data, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for the test_component_names tests.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function components_provider() : array {
|
||||
return [
|
||||
'Inexistent component' => [
|
||||
'inexistent_component', []
|
||||
],
|
||||
'Compatible component' => [
|
||||
'fake_component', [true]
|
||||
],
|
||||
'Incompatible component' => [
|
||||
'core_xapi', []
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Testing raw JSON encoding.
|
||||
*
|
||||
* This test is used for wrong json format and empty structures.
|
||||
*
|
||||
* @dataProvider invalid_json_provider
|
||||
* @param string $json json string to send
|
||||
*/
|
||||
public function test_invalid_json(string $json) {
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Scenario.
|
||||
$this->setAdminUser();
|
||||
|
||||
// Perform test.
|
||||
$testhelper = new test_helper();
|
||||
$testhelper->init_log();
|
||||
|
||||
// If no result is expected we will just incur in exception.
|
||||
$this->expectException(xapi_exception::class);
|
||||
|
||||
$external = $this->get_extenal_class();
|
||||
$result = $external::execute('fake_component', $json);
|
||||
$result = external_api::clean_returnvalue($external::execute_returns(), $result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for the test_components tests.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function invalid_json_provider() : array {
|
||||
return [
|
||||
'Wrong json' => [
|
||||
'This is not { a json object /'
|
||||
],
|
||||
'Empty string json' => [
|
||||
''
|
||||
],
|
||||
'Empty array json' => [
|
||||
'[]'
|
||||
],
|
||||
'Invalid single statement json' => [
|
||||
'{"actor":{"objectType":"Agent","mbox":"[email protected]"},"verb":{"id":"InvalidVerb"}'
|
||||
.',"object":{"objectType":"Activity","id":"somethingwrong"}}'
|
||||
],
|
||||
'Invalid multiple statement json' => [
|
||||
'[{"actor":{"objectType":"Agent","mbox":"[email protected]"},"verb":{"id":"InvalidVerb"}'
|
||||
.',"object":{"objectType":"Activity","id":"somethingwrong"}}]'
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Testing agent (user) statements.
|
||||
*
|
||||
* This function test several scenarios using different combinations
|
||||
* of statement rejection motives. Some motives produces a full batch
|
||||
* rejection (exception) and other can leed to indivual rejection on
|
||||
* each statement. For example,try to post a statement without $USER
|
||||
* in it produces a full batch rejection, while using an invalid
|
||||
* verb on one statement just reject that specific statement
|
||||
* That is the expected behaviour.
|
||||
*
|
||||
* @dataProvider statement_provider
|
||||
* @param bool $multiple if send multiple statements (adds one valid statement)
|
||||
* @param bool $validactor if the actor used is valid
|
||||
* @param bool $validverb if the verb used is valid
|
||||
* @param array $expected expected results
|
||||
*/
|
||||
public function test_statements_agent(bool $multiple, bool $validactor, bool $validverb, array $expected) {
|
||||
global $USER;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$this->setAdminUser();
|
||||
|
||||
$other = $this->getDataGenerator()->create_user();
|
||||
|
||||
$info = [];
|
||||
|
||||
// Setup actor.
|
||||
if ($validactor) {
|
||||
$info['actor'] = item_agent::create_from_user($USER);
|
||||
} else {
|
||||
$info['actor'] = item_agent::create_from_user($other);
|
||||
}
|
||||
|
||||
// Setup verb.
|
||||
if (!$validverb) {
|
||||
$info['verb'] = item_verb::create_from_id('invalid');
|
||||
}
|
||||
|
||||
$data = $this->get_valid_statement($info);
|
||||
|
||||
if ($multiple) {
|
||||
$data = [
|
||||
$this->get_valid_statement(),
|
||||
$data,
|
||||
];
|
||||
}
|
||||
|
||||
// Perform test.
|
||||
$this->post_statements_data ('fake_component', $data, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Testing group statements.
|
||||
*
|
||||
* This function test several scenarios using different combinations
|
||||
* of statement rejection motives. Some motives produces a full batch
|
||||
* rejection (exception) and other can leed to indivual rejection on
|
||||
* each statement. For example,try to post a statement without $USER
|
||||
* in it produces a full batch rejection, while using an invalid
|
||||
* verb on one statement just reject that specific statement
|
||||
* That is the expected behaviour.
|
||||
*
|
||||
* @dataProvider statement_provider
|
||||
* @param bool $multiple if send multiple statements (adds one valid statement)
|
||||
* @param bool $validactor if the actor used is valid
|
||||
* @param bool $validverb if the verb used is valid
|
||||
* @param array $expected expected results
|
||||
*/
|
||||
public function test_statements_group(bool $multiple, bool $validactor, bool $validverb, array $expected) {
|
||||
global $USER, $CFG;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$this->setAdminUser();
|
||||
|
||||
$other = $this->getDataGenerator()->create_user();
|
||||
|
||||
$info = [];
|
||||
|
||||
// Enable group mode in the handle.
|
||||
$CFG->xapitestforcegroupactors = true;
|
||||
|
||||
// Create one course and 1 group.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$this->getDataGenerator()->enrol_user($USER->id, $course->id);
|
||||
$this->getDataGenerator()->enrol_user($other->id, $course->id);
|
||||
|
||||
$group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
|
||||
$this->getDataGenerator()->create_group_member(array('groupid' => $group->id, 'userid' => $other->id));
|
||||
|
||||
if ($validactor) {
|
||||
// Add $USER into a group to make group valid for processing.
|
||||
$this->getDataGenerator()->create_group_member(array('groupid' => $group->id, 'userid' => $USER->id));
|
||||
}
|
||||
$info['actor'] = item_group::create_from_group($group);
|
||||
|
||||
// Setup verb.
|
||||
if (!$validverb) {
|
||||
$info['verb'] = item_verb::create_from_id('invalid');
|
||||
}
|
||||
|
||||
$data = $this->get_valid_statement($info);
|
||||
|
||||
if ($multiple) {
|
||||
$data = [
|
||||
$this->get_valid_statement(),
|
||||
$data,
|
||||
];
|
||||
}
|
||||
|
||||
// Perform test.
|
||||
$this->post_statements_data ('fake_component', $data, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for the test_components tests.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function statement_provider() : array {
|
||||
return [
|
||||
// Single statement with group statements enabled.
|
||||
'Single, Valid actor, valid verb' => [
|
||||
false, true, true, [true]
|
||||
],
|
||||
'Single, Invalid actor, valid verb' => [
|
||||
false, false, true, []
|
||||
],
|
||||
'Single, Valid actor, invalid verb' => [
|
||||
false, true, false, []
|
||||
],
|
||||
'Single, Inalid actor, invalid verb' => [
|
||||
false, false, false, []
|
||||
],
|
||||
// Multi statement with group statements enabled.
|
||||
'Multiple, Valid actor, valid verb' => [
|
||||
true, true, true, [true, true]
|
||||
],
|
||||
'Multiple, Invalid actor, valid verb' => [
|
||||
true, false, true, []
|
||||
],
|
||||
'Multiple, Valid actor, invalid verb' => [
|
||||
true, true, false, [true, false]
|
||||
],
|
||||
'Multiple, Inalid actor, invalid verb' => [
|
||||
true, false, false, []
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test posting group statements to a handler without group actor support.
|
||||
*
|
||||
* Try to use group statement in components that not support this feature
|
||||
* causes a full statements batch rejection.
|
||||
*
|
||||
* @dataProvider group_statement_provider
|
||||
* @param bool $usegroup1 if the 1st statement must be groupal
|
||||
* @param bool $usegroup2 if the 2nd statement must be groupal
|
||||
* @param array $expected expected results
|
||||
*/
|
||||
public function test_group_disabled(bool $usegroup1, bool $usegroup2, array $expected) {
|
||||
global $USER;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$this->setAdminUser();
|
||||
|
||||
// Create one course and 1 group.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$this->getDataGenerator()->enrol_user($USER->id, $course->id);
|
||||
$group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
|
||||
$this->getDataGenerator()->create_group_member(array('groupid' => $group->id, 'userid' => $USER->id));
|
||||
|
||||
$info = ['actor' => item_group::create_from_group($group)];
|
||||
|
||||
$groupstatement = $this->get_valid_statement($info);
|
||||
$agentstatement = $this->get_valid_statement();
|
||||
|
||||
$data = [];
|
||||
$data[] = ($usegroup1) ? $groupstatement : $agentstatement;
|
||||
$data[] = ($usegroup2) ? $groupstatement : $agentstatement;
|
||||
|
||||
// Perform test.
|
||||
$this->post_statements_data ('fake_component', $data, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for the test_components tests.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function group_statement_provider() : array {
|
||||
return [
|
||||
// Single statement with group statements enabled.
|
||||
'Group statement + group statement without group support' => [
|
||||
true, true, []
|
||||
],
|
||||
'Group statement + agent statement without group support' => [
|
||||
true, false, []
|
||||
],
|
||||
'Agent statement + group statement without group support' => [
|
||||
true, false, []
|
||||
],
|
||||
'Agent statement + agent statement without group support' => [
|
||||
false, false, [true, true]
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test posting a statements batch not accepted by handler.
|
||||
*
|
||||
* If all statements from a batch are rejectes by the plugin the full
|
||||
* batch is considered rejected and an exception is returned.
|
||||
*/
|
||||
public function test_full_batch_rejected() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$this->setAdminUser();
|
||||
|
||||
$info = ['verb' => item_verb::create_from_id('invalid')];
|
||||
|
||||
$statement = $this->get_valid_statement($info);
|
||||
|
||||
$data = [$statement, $statement];
|
||||
|
||||
// Perform test.
|
||||
$this->post_statements_data ('fake_component', $data, []);
|
||||
}
|
||||
}
|
||||
Vendored
+115
@@ -0,0 +1,115 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* The core_xapi test class for xAPI statements.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @since Moodle 3.9
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace fake_component\xapi;
|
||||
|
||||
use core_xapi\local\statement;
|
||||
use core_xapi\handler as handler_base;
|
||||
use core_xapi\event\xapi_test_statement_post;
|
||||
use context_system;
|
||||
use core\event\base;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Class xapi_handler testing dummie class.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @since Moodle 3.9
|
||||
* @copyright 2020 Ferran Recio
|
||||
*/
|
||||
class handler extends handler_base {
|
||||
|
||||
/**
|
||||
* Convert statements to event.
|
||||
*
|
||||
* Convert a statement object into a Moodle xAPI Event. If a statement is accepted
|
||||
* by validate_statement the component must provide a event to handle that statement,
|
||||
* otherwise the statement will be rejected.
|
||||
*
|
||||
* @param statement $statement the statement object
|
||||
* @return \core\event\base|null a Moodle event to trigger
|
||||
*/
|
||||
public function statement_to_event(statement $statement): ?base {
|
||||
global $USER;
|
||||
|
||||
// Validate verb.
|
||||
$validvalues = [
|
||||
'cook',
|
||||
'http://adlnet.gov/expapi/verbs/answered'
|
||||
];
|
||||
$verbid = $statement->get_verb_id();
|
||||
if (!in_array($verbid, $validvalues)) {
|
||||
return null;
|
||||
}
|
||||
// Validate object.
|
||||
$validvalues = [
|
||||
'paella',
|
||||
'http://adlnet.gov/expapi/activities/example'
|
||||
];
|
||||
$activityid = $statement->get_activity_id();
|
||||
if (!in_array($activityid, $validvalues)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if ($this->supports_group_actors()) {
|
||||
$users = $statement->get_all_users();
|
||||
// In most cases we can use $USER->id as the event userid but because
|
||||
// this is just a test class it checks first for $USER and, if not
|
||||
// present just pick the first one.
|
||||
$user = $users[$USER->id] ?? array_shift($users);
|
||||
} else {
|
||||
$user = $statement->get_user();
|
||||
}
|
||||
|
||||
// Convert into a Moodle event.
|
||||
$minstatement = $statement->minify();
|
||||
$params = array(
|
||||
'other' => $minstatement,
|
||||
'context' => context_system::instance(),
|
||||
'userid' => $user->id,
|
||||
);
|
||||
return xapi_test_statement_post::create($params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Return true if group actor is enabled.
|
||||
*
|
||||
* NOTE: the use of a global is only for testing. We need to change
|
||||
* the behaviour from the PHPUnitTest to test all possible scenarios.
|
||||
*
|
||||
* Note: this method must be overridden by the plugins which want to
|
||||
* use groups in statements.
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function supports_group_actors(): bool {
|
||||
global $CFG;
|
||||
if (isset($CFG->xapitestforcegroupactors)) {
|
||||
return $CFG->xapitestforcegroupactors;
|
||||
}
|
||||
return parent::supports_group_actors();
|
||||
}
|
||||
}
|
||||
@@ -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/>.
|
||||
|
||||
/**
|
||||
* Mock events for xAPI testing.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi\event;
|
||||
|
||||
use context_system;
|
||||
use core_xapi\local\statement;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* xAPI statement webservice testing event.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @since Moodle 3.9
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class xapi_test_statement_post extends \core\event\base {
|
||||
|
||||
/**
|
||||
* Initialise the event data.
|
||||
*/
|
||||
protected function init() {
|
||||
$this->data['crud'] = 'c';
|
||||
$this->data['edulevel'] = self::LEVEL_PARTICIPATING;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns localised general event name.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function get_name() {
|
||||
return "xAPI test statement";
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns non-localised description of what happened.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public function get_description() {
|
||||
return "User '$this->userid' send a statement to component '$this->component'";
|
||||
}
|
||||
|
||||
/**
|
||||
* Compare if a given statement is similar to the one on the record.
|
||||
*
|
||||
* The information stored in the logstore is not exactly a xAPI standard.
|
||||
* Similar checks for actor, verb, object (+ definition) and result for now.
|
||||
*
|
||||
* @param statement $statement An xAPI compatible statement.
|
||||
* @return bool True if the $statement represents this event.
|
||||
*/
|
||||
public function compare_statement(statement $statement): bool {
|
||||
// Check minified version.
|
||||
$calculatedfields = ['actor', 'id', 'timestamp', 'stored', 'version'];
|
||||
foreach ($calculatedfields as $field) {
|
||||
if (isset($this->data['other'][$field])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
// Check verb structure.
|
||||
$data = $statement->get_verb()->get_data();
|
||||
if ($this->data['other']['verb']['id'] != $data->id) {
|
||||
return false;
|
||||
}
|
||||
// Check user.
|
||||
$users = $statement->get_all_users();
|
||||
if (empty($users) || !isset($users[$this->data['userid']])) {
|
||||
return false;
|
||||
}
|
||||
// Check object.
|
||||
$data = $statement->get_object()->get_data();
|
||||
if ($this->data['other']['object']['id'] != $data->id) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,129 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* This file contains unit test related to xAPI library.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi;
|
||||
|
||||
use core_xapi\xapi_exception;
|
||||
use core_xapi\local\statement;
|
||||
use core_xapi\local\statement\item_agent;
|
||||
use core_xapi\local\statement\item_verb;
|
||||
use core_xapi\local\statement\item_activity;
|
||||
use advanced_testcase;
|
||||
use core\event\base;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Contains test cases for testing xAPI statement handler base methods.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @since Moodle 3.9
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class handler_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Setup to ensure that fixtures are loaded.
|
||||
*/
|
||||
public static function setupBeforeClass(): void {
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot.'/lib/xapi/tests/helper.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test handler creation.
|
||||
*/
|
||||
public function test_handler_create() {
|
||||
// Get an existent handler.
|
||||
$handler = handler::create('fake_component');
|
||||
$this->assertEquals(get_class($handler), 'fake_component\\xapi\\handler');
|
||||
|
||||
// Get a non existent handler.
|
||||
$this->expectException(xapi_exception::class);
|
||||
$value = handler::create('potato_omelette');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test support group.
|
||||
*/
|
||||
public function test_support_group_actor() {
|
||||
global $CFG;
|
||||
// Get an existent handler.
|
||||
$this->resetAfterTest();
|
||||
$handler = handler::create('fake_component');
|
||||
$this->assertEquals(get_class($handler), 'fake_component\\xapi\\handler');
|
||||
$CFG->xapitestforcegroupactors = false;
|
||||
$this->assertEquals(false, $handler->supports_group_actors());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for process_statements method.
|
||||
*/
|
||||
public function test_process_statements() {
|
||||
|
||||
$this->resetAfterTest();
|
||||
$this->preventResetByRollback(); // Logging waits till the transaction gets committed.
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
|
||||
$testhelper = new test_helper();
|
||||
$testhelper->init_log();
|
||||
|
||||
// Generate a 2 statements array (one accepted one not).
|
||||
$statements = [];
|
||||
|
||||
$statement = new statement();
|
||||
$statement->set_actor(item_agent::create_from_user($user));
|
||||
$statement->set_verb(item_verb::create_from_id('cook'));
|
||||
$statement->set_object(item_activity::create_from_id('paella'));
|
||||
$statements[] = $statement;
|
||||
|
||||
$statement2 = new statement();
|
||||
$statement2->set_actor(item_agent::create_from_user($user));
|
||||
$statement2->set_verb(item_verb::create_from_id('invalid'));
|
||||
$statement2->set_object(item_activity::create_from_id('paella'));
|
||||
$statements[] = $statement2;
|
||||
|
||||
$handler = handler::create('fake_component');
|
||||
$result = $handler->process_statements($statements);
|
||||
|
||||
// Check results.
|
||||
$this->assertCount(2, $result);
|
||||
$this->assertEquals(true, $result[0]);
|
||||
$this->assertEquals(false, $result[1]);
|
||||
|
||||
// Check log entries.
|
||||
$log = $testhelper->get_last_log_entry();
|
||||
$this->assertNotEmpty($log);
|
||||
|
||||
// Validate statement information on log.
|
||||
$value = $log->get_name();
|
||||
$this->assertEquals($value, 'xAPI test statement');
|
||||
$value = $log->get_description();
|
||||
// Due to logstore limitation, event must use a real component (core_xapi).
|
||||
$this->assertEquals($value, 'User \''.$user->id.'\' send a statement to component \'core_xapi\'');
|
||||
$this->assertTrue($log->compare_statement($statement));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,79 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* Unit tests helper for xAPI library.
|
||||
*
|
||||
* This file contains unit test helpers related to xAPI library.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace core_xapi;
|
||||
|
||||
use stdClass;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
require_once(__DIR__ . '/fixtures/handler.php');
|
||||
require_once(__DIR__ . '/fixtures/xapi_test_statement_post.php');
|
||||
|
||||
/**
|
||||
* Contains helper functions for xAPI PHPUnit Tests.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @since Moodle 3.9
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class test_helper {
|
||||
|
||||
/** @var \core\log\reader contains a valid logstore reader. */
|
||||
private $store;
|
||||
|
||||
/**
|
||||
* Constructor for a xAPI test helper.
|
||||
*
|
||||
*/
|
||||
public function init_log() {
|
||||
// Enable logs.
|
||||
set_config('jsonformat', 1, 'logstore_standard');
|
||||
set_config('enabled_stores', 'logstore_standard', 'tool_log');
|
||||
set_config('buffersize', 0, 'logstore_standard');
|
||||
set_config('logguests', 1, 'logstore_standard');
|
||||
$manager = get_log_manager(true);
|
||||
$stores = $manager->get_readers();
|
||||
$this->store = $stores['logstore_standard'];
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the last log entry from standardlog.
|
||||
*
|
||||
* @return \core\event\base|null The last log event or null if none found.
|
||||
*/
|
||||
public function get_last_log_entry(): ?\core\event\base {
|
||||
|
||||
$select = "component = :component";
|
||||
$params = ['component' => 'core_xapi'];
|
||||
$records = $this->store->get_events_select($select, $params, 'id DESC', 0, 1);
|
||||
|
||||
if (empty($records)) {
|
||||
return null;
|
||||
}
|
||||
return array_pop($records);
|
||||
}
|
||||
}
|
||||
@@ -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/>.
|
||||
|
||||
/**
|
||||
* This file contains unit test related to xAPI library.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
namespace core_xapi;
|
||||
|
||||
use advanced_testcase;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Contains test cases for testing xAPI iri class.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @since Moodle 3.9
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class iri_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Setup to ensure that fixtures are loaded.
|
||||
*/
|
||||
public static function setupBeforeClass(): void {
|
||||
global $CFG;
|
||||
require_once($CFG->dirroot.'/lib/xapi/tests/helper.php');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test IRI generation.
|
||||
*
|
||||
* @dataProvider iri_samples_provider
|
||||
* @param string $value Value to generate IRI
|
||||
* @param string $expected Expected result
|
||||
* @param string $type = null If some special type is provided
|
||||
*/
|
||||
public function test_generate(string $value, string $expected, string $type = null) {
|
||||
$iri = iri::generate($value, $type);
|
||||
$this->assertEquals($iri, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test IRI extraction.
|
||||
*
|
||||
* @dataProvider iri_samples_provider
|
||||
* @param string $expected Expected result
|
||||
* @param string $value Value to generate IRI
|
||||
* @param string $type = null If some special type is provided
|
||||
*/
|
||||
public function test_extract(string $expected, string $value, string $type = null) {
|
||||
$extract = iri::extract($value, $type);
|
||||
$this->assertEquals($extract, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for the test_generate and test_extract tests.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function iri_samples_provider() : array {
|
||||
global $CFG;
|
||||
|
||||
return [
|
||||
'Fake IRI without type' => [
|
||||
'paella',
|
||||
"{$CFG->wwwroot}/xapi/element/paella",
|
||||
null,
|
||||
],
|
||||
'Real IRI without type' => [
|
||||
'http://adlnet.gov/expapi/activities/example',
|
||||
'http://adlnet.gov/expapi/activities/example',
|
||||
null,
|
||||
],
|
||||
'Fake IRI with type' => [
|
||||
'paella',
|
||||
"{$CFG->wwwroot}/xapi/dish/paella",
|
||||
'dish',
|
||||
],
|
||||
'Real IRI with type' => [
|
||||
'http://adlnet.gov/expapi/activities/example',
|
||||
'http://adlnet.gov/expapi/activities/example',
|
||||
'dish',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test IRI generation.
|
||||
*
|
||||
* @dataProvider iri_check_provider
|
||||
* @param string $value Value to generate IRI
|
||||
* @param bool $expected Expected result
|
||||
*/
|
||||
public function test_check(string $value, bool $expected) {
|
||||
$check = iri::check($value);
|
||||
$this->assertEquals($check, $expected);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for the test_check.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function iri_check_provider() : array {
|
||||
return [
|
||||
'Real IRI http' => [
|
||||
'http://adlnet.gov/expapi/activities/example',
|
||||
true,
|
||||
],
|
||||
'Real IRI https' => [
|
||||
'https://adlnet.gov/expapi/activities/example',
|
||||
true,
|
||||
],
|
||||
'Invalid IRI' => [
|
||||
'paella',
|
||||
false,
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,193 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* This file contains unit test related to xAPI library.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi\local\statement;
|
||||
|
||||
use advanced_testcase;
|
||||
use core_xapi\xapi_exception;
|
||||
use core_xapi\iri;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Contains test cases for testing statement activity class.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @since Moodle 3.9
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class item_activity_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test item creation.
|
||||
*/
|
||||
public function test_creation(): void {
|
||||
|
||||
// Activity without definition.
|
||||
$data = (object) [
|
||||
'objectType' => 'Activity',
|
||||
'id' => iri::generate('paella', 'activity'),
|
||||
];
|
||||
$item = item_activity::create_from_data($data);
|
||||
|
||||
$this->assertEquals(json_encode($item), json_encode($data));
|
||||
$this->assertEquals($item->get_id(), 'paella');
|
||||
$this->assertNull($item->get_definition());
|
||||
|
||||
// Add optional objectType.
|
||||
$data->objectType = 'Activity';
|
||||
$item = item_activity::create_from_data($data);
|
||||
$this->assertEquals(json_encode($item), json_encode($data));
|
||||
|
||||
// Add definition.
|
||||
$data->definition = (object) [
|
||||
'interactionType' => 'choice',
|
||||
];
|
||||
$item = item_activity::create_from_data($data);
|
||||
|
||||
$this->assertEquals(json_encode($item), json_encode($data));
|
||||
$this->assertNotNull($item->get_definition());
|
||||
}
|
||||
|
||||
/**
|
||||
* Test item creation from string.
|
||||
*
|
||||
* @dataProvider create_from_id_provider
|
||||
* @param string $id Object string ID (IRI or not)
|
||||
* @param bool $usedefinition if a valir definition must be attached or not
|
||||
*/
|
||||
public function test_create_from_id(string $id, bool $usedefinition): void {
|
||||
|
||||
$definition = null;
|
||||
if ($usedefinition) {
|
||||
$data = (object) [
|
||||
'type' => iri::generate('example', 'id'),
|
||||
'interactionType' => 'choice'
|
||||
];
|
||||
$definition = item_definition::create_from_data($data);
|
||||
}
|
||||
|
||||
$item = item_activity::create_from_id($id, $definition);
|
||||
|
||||
$this->assertEquals($id, $item->get_id());
|
||||
$itemdefinition = $item->get_definition();
|
||||
if ($usedefinition) {
|
||||
$this->assertEquals('choice', $itemdefinition->get_interactiontype());
|
||||
} else {
|
||||
$this->assertNull($itemdefinition);
|
||||
}
|
||||
|
||||
// Check generated data.
|
||||
$data = $item->get_data();
|
||||
$this->assertEquals('Activity', $data->objectType);
|
||||
$this->assertEquals(iri::generate($id, 'activity'), $data->id);
|
||||
if ($usedefinition) {
|
||||
$this->assertEquals('choice', $data->definition->interactionType);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for the test_create_from_id tests.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function create_from_id_provider() : array {
|
||||
return [
|
||||
'Fake IRI with no definition' => [
|
||||
'paella', false,
|
||||
],
|
||||
'Fake IRI with definition' => [
|
||||
'paella', true,
|
||||
],
|
||||
'Real IRI with no definition' => [
|
||||
'http://adlnet.gov/expapi/activities/example', false,
|
||||
],
|
||||
'Real IRI with definition' => [
|
||||
'http://adlnet.gov/expapi/activities/example', true,
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for invalid structures.
|
||||
*
|
||||
* @dataProvider invalid_data_provider
|
||||
* @param string $type objectType attribute
|
||||
* @param string $id activity ID
|
||||
*/
|
||||
public function test_invalid_data(string $type, string $id): void {
|
||||
|
||||
$data = (object) [
|
||||
'objectType' => $type,
|
||||
];
|
||||
if (!empty($id)) {
|
||||
$data->id = $id;
|
||||
}
|
||||
|
||||
$this->expectException(xapi_exception::class);
|
||||
$item = item_activity::create_from_data($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for the test_invalid_data tests.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function invalid_data_provider() : array {
|
||||
return [
|
||||
'Invalid Avtivity objectType' => [
|
||||
'Invalid Type!', iri::generate('paella', 'activity'),
|
||||
],
|
||||
'Invalid id value' => [
|
||||
'Activity', 'Invalid_iri_value',
|
||||
],
|
||||
'Non-existent id value' => [
|
||||
'Activity', '',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for missing object type.
|
||||
*/
|
||||
public function test_missing_object_type(): void {
|
||||
$data = (object) ['id' => 42];
|
||||
$this->expectException(xapi_exception::class);
|
||||
$item = item_activity::create_from_data($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for invalid activity objectType.
|
||||
*/
|
||||
public function test_inexistent_agent(): void {
|
||||
global $CFG;
|
||||
$data = (object) [
|
||||
'objectType' => 'Invalid',
|
||||
'id' => -1,
|
||||
];
|
||||
$this->expectException(xapi_exception::class);
|
||||
$item = item_activity::create_from_data($data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,115 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* This file contains unit test related to xAPI library.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi\local\statement;
|
||||
|
||||
use advanced_testcase;
|
||||
use core_xapi\xapi_exception;
|
||||
use core_xapi\iri;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Contains test cases for testing statement actor class.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @since Moodle 3.9
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class item_actor_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test item creation with agent.
|
||||
*/
|
||||
public function test_creation_agent(): void {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
|
||||
$item = item_agent::create_from_user($user);
|
||||
$data = $item->get_data();
|
||||
|
||||
$item = item_actor::create_from_data($data);
|
||||
|
||||
$this->assertEquals(json_encode($item), json_encode($data));
|
||||
$this->assertEquals('core_xapi\local\statement\item_agent', get_class($item));
|
||||
|
||||
// Create without specify type.
|
||||
unset($data->objectType);
|
||||
|
||||
$item = item_actor::create_from_data($data);
|
||||
|
||||
$this->assertEquals(json_encode($item), json_encode($data));
|
||||
$this->assertEquals('core_xapi\local\statement\item_agent', get_class($item));
|
||||
|
||||
// Check user.
|
||||
$itemuser = $item->get_user();
|
||||
$this->assertEquals($itemuser->id, $user->id);
|
||||
$itemusers = $item->get_all_users();
|
||||
$this->assertCount(1, $itemusers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test item creation with group.
|
||||
*/
|
||||
public function test_creation_group(): void {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id);
|
||||
$group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
|
||||
$this->getDataGenerator()->create_group_member(array('groupid' => $group->id, 'userid' => $user->id));
|
||||
|
||||
$item = item_group::create_from_group($group);
|
||||
$data = $item->get_data();
|
||||
|
||||
$item = item_actor::create_from_data($data);
|
||||
|
||||
$this->assertEquals(json_encode($item), json_encode($data));
|
||||
$this->assertEquals('core_xapi\local\statement\item_group', get_class($item));
|
||||
|
||||
// Check group.
|
||||
$itemgroup = $item->get_group();
|
||||
$this->assertEquals($itemgroup->id, $group->id);
|
||||
$itemusers = $item->get_all_users();
|
||||
$this->assertCount(1, $itemusers);
|
||||
|
||||
// Code must prevent from using group as a single user.
|
||||
$this->expectException(xapi_exception::class);
|
||||
$itemusers = $item->get_user();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for invalid structures.
|
||||
*/
|
||||
public function test_invalid_data(): void {
|
||||
$this->expectException(xapi_exception::class);
|
||||
$data = (object) [
|
||||
'objectType' => 'Fake',
|
||||
];
|
||||
$item = item_actor::create_from_data($data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,253 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* This file contains unit test related to xAPI library.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi\local\statement;
|
||||
|
||||
use advanced_testcase;
|
||||
use core_xapi\xapi_exception;
|
||||
use core_xapi\iri;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Contains test cases for testing statement agent class.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @since Moodle 3.9
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class item_agent_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test item creation.
|
||||
*/
|
||||
public function test_create(): void {
|
||||
global $CFG;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
|
||||
// Ceate using account.
|
||||
$data = (object) [
|
||||
'objectType' => 'Agent',
|
||||
'account' => (object) [
|
||||
'homePage' => $CFG->wwwroot,
|
||||
'name' => $user->id,
|
||||
],
|
||||
];
|
||||
$item = item_agent::create_from_data($data);
|
||||
|
||||
$this->assertEquals(json_encode($item), json_encode($data));
|
||||
$itemuser = $item->get_user();
|
||||
$this->assertEquals($itemuser->id, $user->id);
|
||||
$itemusers = $item->get_all_users();
|
||||
$this->assertCount(1, $itemusers);
|
||||
|
||||
// Ceate using mbox.
|
||||
$data = (object) [
|
||||
'objectType' => 'Agent',
|
||||
'mbox' => $user->email,
|
||||
];
|
||||
$item = item_agent::create_from_data($data);
|
||||
|
||||
$this->assertEquals(json_encode($item), json_encode($data));
|
||||
$itemuser = $item->get_user();
|
||||
$this->assertEquals($itemuser->id, $user->id);
|
||||
$itemusers = $item->get_all_users();
|
||||
$this->assertCount(1, $itemusers);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test item creation from Record.
|
||||
*/
|
||||
public function test_create_from_user(): void {
|
||||
global $CFG;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
|
||||
$item = item_agent::create_from_user($user);
|
||||
|
||||
$itemuser = $item->get_user();
|
||||
$this->assertEquals($itemuser->id, $user->id);
|
||||
$itemusers = $item->get_all_users();
|
||||
$this->assertCount(1, $itemusers);
|
||||
|
||||
// Check generated data.
|
||||
$data = $item->get_data();
|
||||
$this->assertEquals('Agent', $data->objectType);
|
||||
$this->assertEquals($CFG->wwwroot, $data->account->homePage);
|
||||
$this->assertEquals($user->id, $data->account->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for invalid structures.
|
||||
*
|
||||
* @dataProvider invalid_data_provider
|
||||
* @param string $objecttype object type attribute
|
||||
* @param bool $validhome if valid homepage is user
|
||||
* @param bool $validid if valid group id is used
|
||||
*/
|
||||
public function test_invalid_data(string $objecttype, bool $validhome, bool $validid): void {
|
||||
global $CFG;
|
||||
|
||||
// Create one course with a group if necessary.
|
||||
$id = 'Wrong ID';
|
||||
if ($validid) {
|
||||
$this->resetAfterTest();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$id = $user->id;
|
||||
}
|
||||
|
||||
$homepage = 'Invalid homepage!';
|
||||
if ($validhome) {
|
||||
$homepage = $CFG->wwwroot;
|
||||
}
|
||||
|
||||
$data = (object) [
|
||||
'objectType' => $objecttype,
|
||||
'account' => (object) [
|
||||
'homePage' => $homepage,
|
||||
'name' => $id,
|
||||
],
|
||||
];
|
||||
|
||||
$this->expectException(xapi_exception::class);
|
||||
$item = item_agent::create_from_data($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for the test_invalid_data tests.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function invalid_data_provider() : array {
|
||||
return [
|
||||
'Wrong objecttype' => [
|
||||
'Invalid', true, true
|
||||
],
|
||||
'Wrong homepage' => [
|
||||
'Agent', false, true
|
||||
],
|
||||
'Wrong id' => [
|
||||
'Agent', true, false
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test non supported account identifier xAPI formats.
|
||||
*
|
||||
* @dataProvider unspupported_create_provider
|
||||
* @param bool $usembox
|
||||
* @param bool $useaccount
|
||||
* @param bool $usesha1
|
||||
* @param bool $useopenid
|
||||
*/
|
||||
public function test_unspupported_create(bool $usembox, bool $useaccount, bool $usesha1, bool $useopenid): void {
|
||||
global $CFG;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
|
||||
// Ceate using both account and mbox.
|
||||
$data = (object) [
|
||||
'objectType' => 'Agent'
|
||||
];
|
||||
|
||||
if ($usembox) {
|
||||
$data->mbox = $user->email;
|
||||
}
|
||||
if ($useaccount) {
|
||||
$data->account = (object) [
|
||||
'homePage' => $CFG->wwwroot,
|
||||
'name' => $user->id,
|
||||
];
|
||||
}
|
||||
if ($usesha1) {
|
||||
$data->mbox_sha1sum = sha1($user->email);
|
||||
}
|
||||
if ($useopenid) {
|
||||
// Note: this is not a real openid, it's just a value to test.
|
||||
$data->openid = 'https://www.moodle.openid.com/accounts/o8/id';
|
||||
}
|
||||
|
||||
$this->expectException(xapi_exception::class);
|
||||
$item = item_agent::create_from_data($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for the unsupported identifiers tests.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function unspupported_create_provider() : array {
|
||||
return [
|
||||
'Both mbox and account' => [
|
||||
true, true, false, false
|
||||
],
|
||||
'Email SHA1' => [
|
||||
false, false, false, false
|
||||
],
|
||||
'Open ID' => [
|
||||
false, false, false, false
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for missing object type.
|
||||
*/
|
||||
public function test_missing_object_type(): void {
|
||||
$data = (object) ['id' => -1];
|
||||
$this->expectException(xapi_exception::class);
|
||||
$item = item_agent::create_from_data($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for invalid user id.
|
||||
*/
|
||||
public function test_inexistent_agent(): void {
|
||||
global $CFG;
|
||||
$data = (object) [
|
||||
'objectType' => 'Agent',
|
||||
'account' => (object) [
|
||||
'homePage' => $CFG->wwwroot,
|
||||
'name' => 0,
|
||||
],
|
||||
];
|
||||
$this->expectException(xapi_exception::class);
|
||||
$item = item_agent::create_from_data($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for invalid agent record.
|
||||
*/
|
||||
public function test_inexistent_agent_id(): void {
|
||||
$user = (object) ['name' => 'Me'];
|
||||
$this->expectException(xapi_exception::class);
|
||||
$item = item_agent::create_from_user($user);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* This file contains unit test related to xAPI library.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi\local\statement;
|
||||
|
||||
use advanced_testcase;
|
||||
use core_xapi\xapi_exception;
|
||||
use core_xapi\iri;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Contains test cases for testing statement definition class.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @since Moodle 3.9
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class item_definition_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test item_definition creation.
|
||||
*
|
||||
* @dataProvider creation_provider
|
||||
* @param string $interactiontype
|
||||
*/
|
||||
public function test_creation(string $interactiontype): void {
|
||||
|
||||
// Activity without interactionType.
|
||||
$data = (object) [
|
||||
'type' => iri::generate('example', 'id'),
|
||||
];
|
||||
|
||||
// Add interactionType.
|
||||
if (!empty($interactiontype)) {
|
||||
$data->interactionType = $interactiontype;
|
||||
}
|
||||
$item = item_definition::create_from_data($data);
|
||||
|
||||
$this->assertEquals(json_encode($item), json_encode($data));
|
||||
if (empty($interactiontype)) {
|
||||
$this->assertNull($item->get_interactiontype());
|
||||
} else {
|
||||
$this->assertEquals($interactiontype, $item->get_interactiontype());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for the test_creation tests.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function creation_provider() : array {
|
||||
return [
|
||||
'No interactionType' => [''],
|
||||
'Choice' => ['choice'],
|
||||
'fill-in' => ['fill-in'],
|
||||
'long-fill-in' => ['long-fill-in'],
|
||||
'true-false' => ['true-false'],
|
||||
'matching' => ['matching'],
|
||||
'performance' => ['performance'],
|
||||
'sequencing' => ['sequencing'],
|
||||
'likert' => ['likert'],
|
||||
'numeric' => ['numeric'],
|
||||
'other' => ['other'],
|
||||
'compound' => ['compound'],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for invalid structures.
|
||||
*/
|
||||
public function test_invalid_data(): void {
|
||||
// Activity without interactionType.
|
||||
$data = (object) [
|
||||
'interactionType' => 'Invalid value!',
|
||||
];
|
||||
|
||||
$this->expectException(xapi_exception::class);
|
||||
$item = item_definition::create_from_data($data);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* This file contains unit test related to xAPI library.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi\local\statement;
|
||||
|
||||
use advanced_testcase;
|
||||
use core_xapi\xapi_exception;
|
||||
use core_xapi\iri;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Contains test cases for testing statement group class.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @since Moodle 3.9
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class item_group_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test item creation.
|
||||
*/
|
||||
public function test_create(): void {
|
||||
global $CFG;
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Create one course with a group.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id);
|
||||
$user2 = $this->getDataGenerator()->create_user();
|
||||
$this->getDataGenerator()->enrol_user($user2->id, $course->id);
|
||||
|
||||
$group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
|
||||
$this->getDataGenerator()->create_group_member(array('groupid' => $group->id, 'userid' => $user->id));
|
||||
$this->getDataGenerator()->create_group_member(array('groupid' => $group->id, 'userid' => $user2->id));
|
||||
|
||||
$data = (object) [
|
||||
'objectType' => 'Group',
|
||||
'account' => (object) [
|
||||
'homePage' => $CFG->wwwroot,
|
||||
'name' => $group->id,
|
||||
],
|
||||
];
|
||||
$item = item_group::create_from_data($data);
|
||||
|
||||
$this->assertEquals(json_encode($item), json_encode($data));
|
||||
$itemgroup = $item->get_group();
|
||||
$this->assertEquals($itemgroup->id, $group->id);
|
||||
$itemusers = $item->get_all_users();
|
||||
$this->assertCount(2, $itemusers);
|
||||
|
||||
// Get user in group item must throw an exception.
|
||||
$this->expectException(xapi_exception::class);
|
||||
$itemusers = $item->get_user();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test item creation from Record.
|
||||
*/
|
||||
public function test_create_from_group(): void {
|
||||
global $CFG;
|
||||
|
||||
$this->resetAfterTest();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
|
||||
|
||||
$item = item_group::create_from_group($group);
|
||||
|
||||
$itemgroup = $item->get_group();
|
||||
$this->assertEquals($itemgroup->id, $group->id);
|
||||
|
||||
// Check generated data.
|
||||
$data = $item->get_data();
|
||||
$this->assertEquals('Group', $data->objectType);
|
||||
$this->assertEquals($CFG->wwwroot, $data->account->homePage);
|
||||
$this->assertEquals($group->id, $data->account->name);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for invalid structures.
|
||||
*
|
||||
* @dataProvider invalid_data_provider
|
||||
* @param string $objecttype object type attribute
|
||||
* @param bool $validhome if valid homepage is user
|
||||
* @param bool $validid if valid group id is used
|
||||
*/
|
||||
public function test_invalid_data(string $objecttype, bool $validhome, bool $validid): void {
|
||||
global $CFG;
|
||||
|
||||
// Create one course with a group if necessary.
|
||||
$id = 'Wrong ID';
|
||||
if ($validid) {
|
||||
$this->resetAfterTest();
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
|
||||
$id = $group->id;
|
||||
}
|
||||
|
||||
$homepage = 'Invalid homepage!';
|
||||
if ($validhome) {
|
||||
$homepage = $CFG->wwwroot;
|
||||
}
|
||||
|
||||
$data = (object) [
|
||||
'objectType' => $objecttype,
|
||||
'account' => (object) [
|
||||
'homePage' => $homepage,
|
||||
'name' => $id,
|
||||
],
|
||||
];
|
||||
|
||||
$this->expectException(xapi_exception::class);
|
||||
$item = item_group::create_from_data($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for the test_invalid_data tests.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function invalid_data_provider() : array {
|
||||
return [
|
||||
'Wrong objecttype' => [
|
||||
'Invalid', true, true
|
||||
],
|
||||
'Wrong homepage' => [
|
||||
'Group', false, true
|
||||
],
|
||||
'Wrong id' => [
|
||||
'Group', true, false
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for missing object type.
|
||||
*/
|
||||
public function test_missing_object_type(): void {
|
||||
$data = (object) ['id' => -1];
|
||||
$this->expectException(xapi_exception::class);
|
||||
$item = item_group::create_from_data($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for invalid anonymous group.
|
||||
*/
|
||||
public function test_invalid_anonymous_group(): void {
|
||||
$data = (object) [
|
||||
'objectType' => 'Group'
|
||||
];
|
||||
$this->expectException(xapi_exception::class);
|
||||
$item = item_group::create_from_data($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for invalid anonymous group.
|
||||
*/
|
||||
public function test_inexistent_group(): void {
|
||||
global $CFG;
|
||||
$data = (object) [
|
||||
'objectType' => 'Group',
|
||||
'account' => (object) [
|
||||
'homePage' => $CFG->wwwroot,
|
||||
'name' => 0,
|
||||
],
|
||||
];
|
||||
$this->expectException(xapi_exception::class);
|
||||
$item = item_group::create_from_data($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for invalid group record.
|
||||
*/
|
||||
public function test_inexistent_group_id(): void {
|
||||
$group = (object) ['name' => 'My Group'];
|
||||
$this->expectException(xapi_exception::class);
|
||||
$item = item_group::create_from_group($group);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* This file contains unit test related to xAPI library.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi\local\statement;
|
||||
|
||||
use advanced_testcase;
|
||||
use core_xapi\xapi_exception;
|
||||
use core_xapi\iri;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Contains test cases for testing statement object class.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @since Moodle 3.9
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class item_object_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test item creation with agent.
|
||||
*/
|
||||
public function test_creation_agent(): void {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$item = item_agent::create_from_user($user);
|
||||
$data = $item->get_data();
|
||||
|
||||
$item = item_object::create_from_data($data);
|
||||
|
||||
$this->assertEquals(json_encode($item), json_encode($data));
|
||||
$this->assertEquals('core_xapi\local\statement\item_agent', get_class($item));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test item creation with group.
|
||||
*/
|
||||
public function test_creation_group(): void {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
|
||||
$item = item_group::create_from_group($group);
|
||||
$data = $item->get_data();
|
||||
|
||||
$item = item_object::create_from_data($data);
|
||||
|
||||
$this->assertEquals(json_encode($item), json_encode($data));
|
||||
$this->assertEquals('core_xapi\local\statement\item_group', get_class($item));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test item creation with activity.
|
||||
*/
|
||||
public function test_creation_activity(): void {
|
||||
|
||||
$item = item_activity::create_from_id('paella');
|
||||
$data = $item->get_data();
|
||||
$item = item_object::create_from_data($data);
|
||||
|
||||
$this->assertEquals(json_encode($item), json_encode($data));
|
||||
$this->assertEquals('core_xapi\local\statement\item_activity', get_class($item));
|
||||
}
|
||||
|
||||
/**
|
||||
* Test unsupported item creation.
|
||||
*/
|
||||
public function test_unsupported_activity(): void {
|
||||
$this->expectException(xapi_exception::class);
|
||||
$data = (object) [
|
||||
'objectType' => 'FakeType',
|
||||
'id' => -1,
|
||||
];
|
||||
$item = item_object::create_from_data($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for invalid structures.
|
||||
*
|
||||
* @dataProvider invalid_data_provider
|
||||
* @param string $id
|
||||
*/
|
||||
public function test_invalid_data(string $id): void {
|
||||
$this->expectException(xapi_exception::class);
|
||||
$data = (object) [
|
||||
'id' => $id,
|
||||
];
|
||||
$item = item_object::create_from_data($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for the test_invalid_data tests.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function invalid_data_provider() : array {
|
||||
return [
|
||||
'Empty or null id' => [
|
||||
'',
|
||||
],
|
||||
'Invalid IRI value' => [
|
||||
'invalid_iri_value',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -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/>.
|
||||
|
||||
/**
|
||||
* This file contains unit test related to xAPI library.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi\local\statement;
|
||||
|
||||
use advanced_testcase;
|
||||
use core_xapi\xapi_exception;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Contains test cases for testing statement base class.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @since Moodle 3.9
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class item_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test item creation.
|
||||
*/
|
||||
public function test_create() {
|
||||
|
||||
// This is a generic item so check that it can create and item and json encode later.
|
||||
$data = (object) [
|
||||
'this' => 'is',
|
||||
'just' => 1,
|
||||
'example' => ['of', 'structure'],
|
||||
];
|
||||
$item = item::create_from_data($data);
|
||||
|
||||
$this->assertEquals(json_encode($item), json_encode($data));
|
||||
|
||||
}
|
||||
}
|
||||
@@ -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/>.
|
||||
|
||||
/**
|
||||
* This file contains unit test related to xAPI library.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi\local\statement;
|
||||
|
||||
use advanced_testcase;
|
||||
use core_xapi\xapi_exception;
|
||||
use core_xapi\iri;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Contains test cases for testing statement verb class.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @since Moodle 3.9
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class item_verb_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test item creation.
|
||||
*/
|
||||
public function test_creation(): void {
|
||||
|
||||
$data = (object) [
|
||||
'id' => iri::generate('cook', 'verb'),
|
||||
];
|
||||
$item = item_verb::create_from_data($data);
|
||||
|
||||
$this->assertEquals(json_encode($item), json_encode($data));
|
||||
$this->assertEquals($item->get_id(), 'cook');
|
||||
}
|
||||
|
||||
/**
|
||||
* Test item creation from string.
|
||||
*
|
||||
* @dataProvider create_from_id_provider
|
||||
* @param string $id Object string ID (IRI or not)
|
||||
*/
|
||||
public function test_create_from_id(string $id): void {
|
||||
$item = item_verb::create_from_id($id);
|
||||
|
||||
$this->assertEquals($id, $item->get_id());
|
||||
|
||||
// Check generated data.
|
||||
$data = $item->get_data();
|
||||
$this->assertEquals(iri::generate($id, 'verb'), $data->id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for the test_create_from_id tests.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function create_from_id_provider() : array {
|
||||
return [
|
||||
'Fake IRI' => [
|
||||
'cook',
|
||||
],
|
||||
'Real IRI' => [
|
||||
'http://adlnet.gov/expapi/verb/example',
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for invalid structures.
|
||||
*
|
||||
* @dataProvider invalid_data_provider
|
||||
* @param string $id
|
||||
*/
|
||||
public function test_invalid_data(string $id): void {
|
||||
$this->expectException(xapi_exception::class);
|
||||
$data = (object) [
|
||||
'id' => $id,
|
||||
];
|
||||
$item = item_verb::create_from_data($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for the test_invalid_data tests.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function invalid_data_provider() : array {
|
||||
return [
|
||||
'Empty or null id' => [
|
||||
'',
|
||||
],
|
||||
'Invalid IRI value' => [
|
||||
'invalid_iri_value',
|
||||
],
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,451 @@
|
||||
<?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/>.
|
||||
|
||||
/**
|
||||
* This file contains unit test related to xAPI library.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
|
||||
namespace core_xapi\local;
|
||||
|
||||
use core_xapi\local\statement\item;
|
||||
use core_xapi\local\statement\item_actor;
|
||||
use core_xapi\local\statement\item_object;
|
||||
use core_xapi\local\statement\item_activity;
|
||||
use core_xapi\local\statement\item_verb;
|
||||
use core_xapi\local\statement\item_agent;
|
||||
use core_xapi\local\statement\item_group;
|
||||
use core_xapi\xapi_exception;
|
||||
use advanced_testcase;
|
||||
use stdClass;
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
/**
|
||||
* Contains test cases for testing statement class.
|
||||
*
|
||||
* @package core_xapi
|
||||
* @since Moodle 3.9
|
||||
* @copyright 2020 Ferran Recio
|
||||
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
|
||||
*/
|
||||
class statement_testcase extends advanced_testcase {
|
||||
|
||||
/**
|
||||
* Test statement creation.
|
||||
*
|
||||
* @dataProvider create_provider
|
||||
* @param bool $useagent if use agent as actor (or group if false)
|
||||
* @param array $extras
|
||||
*/
|
||||
public function test_create(bool $useagent, array $extras) {
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Create one course with a group.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id);
|
||||
$group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
|
||||
$this->getDataGenerator()->create_group_member(array('groupid' => $group->id, 'userid' => $user->id));
|
||||
|
||||
// Our statement.
|
||||
$statement = new statement();
|
||||
|
||||
// Populate statement.
|
||||
if ($useagent) {
|
||||
$statement->set_actor(item_agent::create_from_user($user));
|
||||
} else {
|
||||
$statement->set_actor(item_group::create_from_group($group));
|
||||
}
|
||||
$statement->set_verb(item_verb::create_from_id('cook'));
|
||||
$statement->set_object(item_activity::create_from_id('paella'));
|
||||
// For now, the rest of the optional properties have no validation
|
||||
// so we create a standard stdClass for all of them.
|
||||
$data = (object)[
|
||||
'some' => 'data',
|
||||
];
|
||||
foreach ($extras as $extra) {
|
||||
$method = 'set_'.$extra;
|
||||
$statement->$method(item::create_from_data($data));
|
||||
}
|
||||
|
||||
// Check resulting statement.
|
||||
if ($useagent) {
|
||||
$stuser = $statement->get_user();
|
||||
$this->assertEquals($user->id, $stuser->id);
|
||||
$stusers = $statement->get_all_users();
|
||||
$this->assertCount(1, $stusers);
|
||||
} else {
|
||||
$stgroup = $statement->get_group();
|
||||
$this->assertEquals($group->id, $stgroup->id);
|
||||
$stusers = $statement->get_all_users();
|
||||
$this->assertCount(1, $stusers);
|
||||
$stuser = array_shift($stusers);
|
||||
$this->assertEquals($user->id, $stuser->id);
|
||||
}
|
||||
$this->assertEquals('cook', $statement->get_verb_id());
|
||||
$this->assertEquals('paella', $statement->get_activity_id());
|
||||
|
||||
// Check resulting json (only first node structure, internal structure
|
||||
// depends on every item json_encode test).
|
||||
$data = json_decode(json_encode($statement));
|
||||
$this->assertNotEmpty($data->actor);
|
||||
$this->assertNotEmpty($data->verb);
|
||||
$this->assertNotEmpty($data->object);
|
||||
$allextras = ['context', 'result', 'timestamp', 'stored', 'authority', 'version', 'attachments'];
|
||||
foreach ($allextras as $extra) {
|
||||
if (in_array($extra, $extras)) {
|
||||
$this->assertObjectHasAttribute($extra, $data);
|
||||
$this->assertNotEmpty($data->object);
|
||||
} else {
|
||||
$this->assertObjectNotHasAttribute($extra, $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for the test_create and test_create_from_data tests.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function create_provider() : array {
|
||||
return [
|
||||
'Agent statement with no extras' => [
|
||||
true, []
|
||||
],
|
||||
'Agent statement with context' => [
|
||||
true, ['context']
|
||||
],
|
||||
'Agent statement with result' => [
|
||||
true, ['result']
|
||||
],
|
||||
'Agent statement with timestamp' => [
|
||||
true, ['timestamp']
|
||||
],
|
||||
'Agent statement with stored' => [
|
||||
true, ['stored']
|
||||
],
|
||||
'Agent statement with authority' => [
|
||||
true, ['authority']
|
||||
],
|
||||
'Agent statement with version' => [
|
||||
true, ['version']
|
||||
],
|
||||
'Agent statement with attachments' => [
|
||||
true, ['attachments']
|
||||
],
|
||||
'Group statement with no extras' => [
|
||||
false, []
|
||||
],
|
||||
'Group statement with context' => [
|
||||
false, ['context']
|
||||
],
|
||||
'Group statement with result' => [
|
||||
false, ['result']
|
||||
],
|
||||
'Group statement with timestamp' => [
|
||||
false, ['timestamp']
|
||||
],
|
||||
'Group statement with stored' => [
|
||||
false, ['stored']
|
||||
],
|
||||
'Group statement with authority' => [
|
||||
false, ['authority']
|
||||
],
|
||||
'Group statement with version' => [
|
||||
false, ['version']
|
||||
],
|
||||
'Group statement with attachments' => [
|
||||
false, ['attachments']
|
||||
],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test statement creation from xAPI statement data.
|
||||
*
|
||||
* @dataProvider create_provider
|
||||
* @param bool $useagent if use agent as actor (or group if false)
|
||||
* @param array $extras
|
||||
*/
|
||||
public function test_create_from_data(bool $useagent, array $extras) {
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Create one course with a group.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id);
|
||||
$group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
|
||||
$this->getDataGenerator()->create_group_member(array('groupid' => $group->id, 'userid' => $user->id));
|
||||
|
||||
// Populate data.
|
||||
if ($useagent) {
|
||||
$actor = item_agent::create_from_user($user);
|
||||
} else {
|
||||
$actor = item_group::create_from_group($group);
|
||||
}
|
||||
$verb = item_verb::create_from_id('cook');
|
||||
$object = item_activity::create_from_id('paella');
|
||||
|
||||
$data = (object) [
|
||||
'actor' => $actor->get_data(),
|
||||
'verb' => $verb->get_data(),
|
||||
'object' => $object->get_data(),
|
||||
];
|
||||
// For now, the rest of the optional properties have no validation
|
||||
// so we create a standard stdClass for all of them.
|
||||
$info = (object)[
|
||||
'some' => 'data',
|
||||
];
|
||||
foreach ($extras as $extra) {
|
||||
$data->$extra = $info;
|
||||
}
|
||||
|
||||
$statement = statement::create_from_data($data);
|
||||
|
||||
// Check resulting statement.
|
||||
if ($useagent) {
|
||||
$stuser = $statement->get_user();
|
||||
$this->assertEquals($user->id, $stuser->id);
|
||||
$stusers = $statement->get_all_users();
|
||||
$this->assertCount(1, $stusers);
|
||||
} else {
|
||||
$stgroup = $statement->get_group();
|
||||
$this->assertEquals($group->id, $stgroup->id);
|
||||
$stusers = $statement->get_all_users();
|
||||
$this->assertCount(1, $stusers);
|
||||
$stuser = array_shift($stusers);
|
||||
$this->assertEquals($user->id, $stuser->id);
|
||||
}
|
||||
$this->assertEquals('cook', $statement->get_verb_id());
|
||||
$this->assertEquals('paella', $statement->get_activity_id());
|
||||
|
||||
// Check resulting json (only first node structure, internal structure
|
||||
// depends on every item json_encode test).
|
||||
$data = json_decode(json_encode($statement));
|
||||
$this->assertNotEmpty($data->actor);
|
||||
$this->assertNotEmpty($data->verb);
|
||||
$this->assertNotEmpty($data->object);
|
||||
$allextras = ['context', 'result', 'timestamp', 'stored', 'authority', 'version', 'attachments'];
|
||||
foreach ($allextras as $extra) {
|
||||
if (in_array($extra, $extras)) {
|
||||
$this->assertObjectHasAttribute($extra, $data);
|
||||
$this->assertNotEmpty($data->object);
|
||||
} else {
|
||||
$this->assertObjectNotHasAttribute($extra, $data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Test all getters into a not set statement.
|
||||
*
|
||||
* @dataProvider invalid_gets_provider
|
||||
* @param string $method the method to test
|
||||
* @param bool $exception if an exception is expected
|
||||
*/
|
||||
public function test_invalid_gets(string $method, bool $exception) {
|
||||
$statement = new statement();
|
||||
if ($exception) {
|
||||
$this->expectException(xapi_exception::class);
|
||||
}
|
||||
$result = $statement->$method();
|
||||
$this->assertNull($result);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for the text_invalid_gets.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function invalid_gets_provider() : array {
|
||||
return [
|
||||
'Method get_user on empty statement' => ['get_user', true],
|
||||
'Method get_all_users on empty statement' => ['get_all_users', true],
|
||||
'Method get_group on empty statement' => ['get_group', true],
|
||||
'Method get_verb_id on empty statement' => ['get_verb_id', true],
|
||||
'Method get_activity_id on empty statement' => ['get_activity_id', true],
|
||||
'Method get_actor on empty statement' => ['get_actor', false],
|
||||
'Method get_verb on empty statement' => ['get_verb', false],
|
||||
'Method get_object on empty statement' => ['get_object', false],
|
||||
'Method get_context on empty statement' => ['get_context', false],
|
||||
'Method get_result on empty statement' => ['get_result', false],
|
||||
'Method get_timestamp on empty statement' => ['get_timestamp', false],
|
||||
'Method get_stored on empty statement' => ['get_stored', false],
|
||||
'Method get_authority on empty statement' => ['get_authority', false],
|
||||
'Method get_version on empty statement' => ['get_version', false],
|
||||
'Method get_attachments on empty statement' => ['get_attachments', false],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to get a user from a group statement.
|
||||
*/
|
||||
public function test_invalid_get_user() {
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
// Create one course with a group.
|
||||
$course = $this->getDataGenerator()->create_course();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$this->getDataGenerator()->enrol_user($user->id, $course->id);
|
||||
$group = $this->getDataGenerator()->create_group(array('courseid' => $course->id));
|
||||
$this->getDataGenerator()->create_group_member(array('groupid' => $group->id, 'userid' => $user->id));
|
||||
|
||||
// Our statement.
|
||||
$statement = new statement();
|
||||
|
||||
// Populate statement.
|
||||
$statement->set_actor(item_group::create_from_group($group));
|
||||
$statement->set_verb(item_verb::create_from_id('cook'));
|
||||
$statement->set_object(item_activity::create_from_id('paella'));
|
||||
|
||||
$this->expectException(xapi_exception::class);
|
||||
$statement->get_user();
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to get a group from an agent statement.
|
||||
*/
|
||||
public function test_invalid_get_group() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
|
||||
// Our statement.
|
||||
$statement = new statement();
|
||||
|
||||
// Populate statement.
|
||||
$statement->set_actor(item_agent::create_from_user($user));
|
||||
$statement->set_verb(item_verb::create_from_id('cook'));
|
||||
$statement->set_object(item_activity::create_from_id('paella'));
|
||||
|
||||
$this->expectException(xapi_exception::class);
|
||||
$statement->get_group();
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to get activity Id from a statement with agent object.
|
||||
*/
|
||||
public function test_invalid_get_activity_id() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
|
||||
// Our statement.
|
||||
$statement = new statement();
|
||||
|
||||
// Populate statement with and agent object.
|
||||
$statement->set_actor(item_agent::create_from_user($user));
|
||||
$statement->set_verb(item_verb::create_from_id('cook'));
|
||||
$statement->set_object(item_agent::create_from_user($user));
|
||||
|
||||
$this->expectException(xapi_exception::class);
|
||||
$statement->get_activity_id();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test for invalid structures.
|
||||
*
|
||||
* @dataProvider invalid_data_provider
|
||||
* @param bool $useuser if use user into statement
|
||||
* @param bool $userverb if use verb into statement
|
||||
* @param bool $useobject if use object into statement
|
||||
*/
|
||||
public function test_invalid_data(bool $useuser, bool $userverb, bool $useobject): void {
|
||||
|
||||
$data = new stdClass();
|
||||
|
||||
if ($useuser) {
|
||||
$this->resetAfterTest();
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
$data->actor = item_agent::create_from_user($user);
|
||||
}
|
||||
if ($userverb) {
|
||||
$data->verb = item_verb::create_from_id('cook');
|
||||
}
|
||||
if ($useobject) {
|
||||
$data->object = item_activity::create_from_id('paella');
|
||||
}
|
||||
|
||||
$this->expectException(xapi_exception::class);
|
||||
$statement = statement::create_from_data($data);
|
||||
}
|
||||
|
||||
/**
|
||||
* Data provider for the test_invalid_data tests.
|
||||
*
|
||||
* @return array
|
||||
*/
|
||||
public function invalid_data_provider() : array {
|
||||
return [
|
||||
'No actor, no verb, no object' => [false, false, false],
|
||||
'No actor, verb, no object' => [false, true, false],
|
||||
'No actor, no verb, object' => [false, false, true],
|
||||
'No actor, verb, object' => [false, true, true],
|
||||
'Actor, no verb, no object' => [true, false, false],
|
||||
'Actor, verb, no object' => [true, true, false],
|
||||
'Actor, no verb, object' => [true, false, true],
|
||||
];
|
||||
}
|
||||
|
||||
/**
|
||||
* Test minify statement.
|
||||
*/
|
||||
public function test_minify() {
|
||||
|
||||
$this->resetAfterTest();
|
||||
|
||||
$user = $this->getDataGenerator()->create_user();
|
||||
|
||||
// Our statement.
|
||||
$statement = new statement();
|
||||
|
||||
// Populate statement.
|
||||
$statement->set_actor(item_agent::create_from_user($user));
|
||||
$statement->set_verb(item_verb::create_from_id('cook'));
|
||||
$statement->set_object(item_activity::create_from_id('paella'));
|
||||
// For now, the rest of the optional properties have no validation
|
||||
// so we create a standard stdClass for all of them.
|
||||
$data = (object)[
|
||||
'some' => 'data',
|
||||
];
|
||||
$extras = ['context', 'result', 'timestamp', 'stored', 'authority', 'version', 'attachments'];
|
||||
foreach ($extras as $extra) {
|
||||
$method = 'set_'.$extra;
|
||||
$statement->$method(item::create_from_data($data));
|
||||
}
|
||||
|
||||
$min = $statement->minify();
|
||||
|
||||
// Check calculated fields.
|
||||
$this->assertCount(6, $min);
|
||||
$this->assertArrayNotHasKey('actor', $min);
|
||||
$this->assertArrayHasKey('verb', $min);
|
||||
$this->assertArrayHasKey('object', $min);
|
||||
$this->assertArrayHasKey('context', $min);
|
||||
$this->assertArrayHasKey('result', $min);
|
||||
$this->assertArrayNotHasKey('timestamp', $min);
|
||||
$this->assertArrayNotHasKey('stored', $min);
|
||||
$this->assertArrayHasKey('authority', $min);
|
||||
$this->assertArrayNotHasKey('version', $min);
|
||||
$this->assertArrayHasKey('attachments', $min);
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -29,7 +29,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2020032700.00; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
$version = 2020032700.01; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
$release = '3.9dev (Build: 20200327)'; // Human-friendly version name
|
||||
|
||||
Reference in New Issue
Block a user