first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
+10
View File
@@ -0,0 +1,10 @@
define("tool_analytics/log_info",["exports","core/modal","core/str"],(function(_exports,_modal,_str){var obj;
/**
* Shows a dialogue with info about this logs.
*
* @module tool_analytics/log_info
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.loadInfo=void 0,_modal=(obj=_modal)&&obj.__esModule?obj:{default:obj};_exports.loadInfo=(id,info)=>{document.addEventListener("click",(e=>{if(!e.target.closest('[data-model-log-id="'.concat(id,'"]')))return;e.preventDefault();const bodyInfo=document.createElement("ul");info.forEach((item=>{const li=document.createElement("li");li.innerHTML=item,bodyInfo.append(li)})),_modal.default.create({title:(0,_str.get_string)("loginfo","tool_analytics"),body:bodyInfo.outerHTML,large:!0,show:!0,removeOnClose:!0})}))}}));
//# sourceMappingURL=log_info.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"log_info.min.js","sources":["../src/log_info.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Shows a dialogue with info about this logs.\n *\n * @module tool_analytics/log_info\n * @copyright 2017 David Monllao {@link http://www.davidmonllao.com}\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport Modal from 'core/modal';\nimport {get_string as getString} from 'core/str';\n\n/**\n * Prepares a modal info for a log's results.\n *\n * @param {Number} id\n * @param {string[]} info\n */\nexport const loadInfo = (id, info) => {\n document.addEventListener('click', (e) => {\n const link = e.target.closest(`[data-model-log-id=\"${id}\"]`);\n if (!link) {\n return;\n }\n e.preventDefault();\n const bodyInfo = document.createElement('ul');\n info.forEach((item) => {\n const li = document.createElement('li');\n li.innerHTML = item;\n bodyInfo.append(li);\n });\n\n Modal.create({\n title: getString('loginfo', 'tool_analytics'),\n body: bodyInfo.outerHTML,\n large: true,\n show: true,\n removeOnClose: true,\n });\n });\n};\n"],"names":["id","info","document","addEventListener","e","target","closest","preventDefault","bodyInfo","createElement","forEach","item","li","innerHTML","append","create","title","body","outerHTML","large","show","removeOnClose"],"mappings":";;;;;;;8JA+BwB,CAACA,GAAIC,QACzBC,SAASC,iBAAiB,SAAUC,QACnBA,EAAEC,OAAOC,sCAA+BN,iBAIrDI,EAAEG,uBACIC,SAAWN,SAASO,cAAc,MACxCR,KAAKS,SAASC,aACJC,GAAKV,SAASO,cAAc,MAClCG,GAAGC,UAAYF,KACfH,SAASM,OAAOF,sBAGdG,OAAO,CACTC,OAAO,mBAAU,UAAW,kBAC5BC,KAAMT,SAASU,UACfC,OAAO,EACPC,MAAM,EACNC,eAAe"}
+10
View File
@@ -0,0 +1,10 @@
/**
* AMD module for model actions confirmation.
*
* @module tool_analytics/model
* @copyright 2017 David Monllao
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define("tool_analytics/model",["jquery","core/str","core/log","core/notification","core/modal_save_cancel","core/modal_cancel","core/modal_events","core/templates"],(function($,Str,log,Notification,ModalSaveCancel,ModalCancel,ModalEvents,Templates){var actionsList={clear:{title:{key:"clearpredictions",component:"tool_analytics"},body:{key:"clearmodelpredictions",component:"tool_analytics"}},delete:{title:{key:"delete",component:"tool_analytics"},body:{key:"deletemodelconfirmation",component:"tool_analytics"}}};return{confirmAction:function(actionId,actionType){$('[data-action-id="'+actionId+'"]').on("click",(function(ev){ev.preventDefault();var a=$(ev.currentTarget);if(void 0!==actionsList[actionType]){var wrap,reqStrings=[actionsList[actionType].title,actionsList[actionType].body];reqStrings[1].param=(wrap=$(a).closest("[data-model-name]")).length?wrap.attr("data-model-name"):(log.error("Unexpected DOM error - unable to obtain the model name"),"");var stringsPromise=Str.get_strings(reqStrings),modalPromise=ModalSaveCancel.create({});$.when(stringsPromise,modalPromise).then((function(strings,modal){return modal.setTitle(strings[0]),modal.setBody(strings[1]),modal.setSaveButtonText(strings[0]),modal.getRoot().on(ModalEvents.save,(function(){window.location.href=a.attr("href")})),modal.show(),modal})).fail(Notification.exception)}else log.error('Action "'+actionType+'" is not allowed.')}))},selectEvaluationOptions:function(actionId,trainedOnlyExternally){$('[data-action-id="'+actionId+'"]').on("click",(function(ev){ev.preventDefault();var a=$(ev.currentTarget),timeSplittingMethods=$(this).attr("data-timesplitting-methods");ModalSaveCancel.create({title:Str.get_string("evaluatemodel","tool_analytics"),body:Templates.render("tool_analytics/evaluation_options",{trainedexternally:trainedOnlyExternally,timesplittingmethods:JSON.parse(timeSplittingMethods)}),removeOnClose:!0,buttons:{save:Str.get_string("evaluate","tool_analytics")},show:!0}).then((modal=>(modal.getRoot().on(ModalEvents.save,(function(){"trainedmodel"==$("input[name='evaluationmode']:checked").val()&&a.attr("href",a.attr("href")+"&mode=trainedmodel");var timeSplittingMethod=$("#id-evaluation-timesplitting").val();a.attr("href",a.attr("href")+"&timesplitting="+timeSplittingMethod),window.location.href=a.attr("href")})),modal))).catch(Notification.exception)}))},selectExportOptions:function(actionId,isTrained){$('[data-action-id="'+actionId+'"]').on("click",(function(ev){ev.preventDefault();var a=$(ev.currentTarget);if(!isTrained)return a.attr("href",a.attr("href")+"&action=exportmodel&includeweights=0"),void(window.location.href=a.attr("href"));var stringsPromise=Str.get_strings([{key:"export",component:"tool_analytics"}]),modalPromise=ModalSaveCancel.create({body:Templates.render("tool_analytics/export_options",{}),removeOnClose:!0});$.when(stringsPromise,modalPromise).then((function(strings,modal){return modal.setTitle(strings[0]),modal.setSaveButtonText(strings[0]),modal.getRoot().on(ModalEvents.save,(function(){"exportdata"==$("input[name='exportoption']:checked").val()?a.attr("href",a.attr("href")+"&action=exportdata"):(a.attr("href",a.attr("href")+"&action=exportmodel"),$("#id-includeweights").is(":checked")?a.attr("href",a.attr("href")+"&includeweights=1"):a.attr("href",a.attr("href")+"&includeweights=0")),window.location.href=a.attr("href")})),modal.show(),modal})).fail(Notification.exception)}))}}}));
//# sourceMappingURL=model.min.js.map
File diff suppressed because one or more lines are too long
@@ -0,0 +1,10 @@
/**
* Potential contexts selector module.
*
* @module tool_analytics/potential-contexts
* @copyright 2019 David Monllao
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define("tool_analytics/potential-contexts",["jquery","core/ajax"],(function($,Ajax){return{processResults:function(selector,results){var contexts=[];return $.isArray(results)?($.each(results,(function(index,context){contexts.push({value:context.id,label:context.name})})),contexts):results},transport:function(selector,query,success,failure){let modelid=$(selector).attr("modelid")||null;Ajax.call([{methodname:"tool_analytics_potential_contexts",args:{query:query,modelid:modelid}}])[0].then(success).fail(failure)}}}));
//# sourceMappingURL=potential-contexts.min.js.map
@@ -0,0 +1 @@
{"version":3,"file":"potential-contexts.min.js","sources":["../src/potential-contexts.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see <http://www.gnu.org/licenses/>.\n\n/**\n * Potential contexts selector module.\n *\n * @module tool_analytics/potential-contexts\n * @copyright 2019 David Monllao\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\ndefine(['jquery', 'core/ajax'], function($, Ajax) {\n\n return /** @alias module:tool_analytics/potential-contexts */ {\n\n processResults: function(selector, results) {\n var contexts = [];\n if ($.isArray(results)) {\n $.each(results, function(index, context) {\n contexts.push({\n value: context.id,\n label: context.name\n });\n });\n return contexts;\n\n } else {\n return results;\n }\n },\n\n transport: function(selector, query, success, failure) {\n var promise;\n\n let modelid = $(selector).attr('modelid') || null;\n promise = Ajax.call([{\n methodname: 'tool_analytics_potential_contexts',\n args: {\n query: query,\n modelid: modelid\n }\n }]);\n\n promise[0].then(success).fail(failure);\n }\n\n };\n\n});\n"],"names":["define","$","Ajax","processResults","selector","results","contexts","isArray","each","index","context","push","value","id","label","name","transport","query","success","failure","modelid","attr","call","methodname","args","then","fail"],"mappings":";;;;;;;AAuBAA,2CAAO,CAAC,SAAU,cAAc,SAASC,EAAGC,YAEsB,CAE1DC,eAAgB,SAASC,SAAUC,aAC3BC,SAAW,UACXL,EAAEM,QAAQF,UACVJ,EAAEO,KAAKH,SAAS,SAASI,MAAOC,SAC5BJ,SAASK,KAAK,CACVC,MAAOF,QAAQG,GACfC,MAAOJ,QAAQK,UAGhBT,UAGAD,SAIfW,UAAW,SAASZ,SAAUa,MAAOC,QAASC,aAGtCC,QAAUnB,EAAEG,UAAUiB,KAAK,YAAc,KACnCnB,KAAKoB,KAAK,CAAC,CACjBC,WAAY,oCACZC,KAAM,CACFP,MAAOA,MACPG,QAASA,YAIT,GAAGK,KAAKP,SAASQ,KAAKP"}
+54
View File
@@ -0,0 +1,54 @@
// 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/>.
/**
* Shows a dialogue with info about this logs.
*
* @module tool_analytics/log_info
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
import Modal from 'core/modal';
import {get_string as getString} from 'core/str';
/**
* Prepares a modal info for a log's results.
*
* @param {Number} id
* @param {string[]} info
*/
export const loadInfo = (id, info) => {
document.addEventListener('click', (e) => {
const link = e.target.closest(`[data-model-log-id="${id}"]`);
if (!link) {
return;
}
e.preventDefault();
const bodyInfo = document.createElement('ul');
info.forEach((item) => {
const li = document.createElement('li');
li.innerHTML = item;
bodyInfo.append(li);
});
Modal.create({
title: getString('loginfo', 'tool_analytics'),
body: bodyInfo.outerHTML,
large: true,
show: true,
removeOnClose: true,
});
});
};
+223
View File
@@ -0,0 +1,223 @@
// 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/>.
/**
* AMD module for model actions confirmation.
*
* @module tool_analytics/model
* @copyright 2017 David Monllao
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define([
'jquery', 'core/str', 'core/log', 'core/notification', 'core/modal_save_cancel',
'core/modal_cancel', 'core/modal_events', 'core/templates'],
function($, Str, log, Notification, ModalSaveCancel, ModalCancel, ModalEvents, Templates) {
/**
* List of actions that require confirmation and confirmation message.
*/
var actionsList = {
clear: {
title: {
key: 'clearpredictions',
component: 'tool_analytics'
}, body: {
key: 'clearmodelpredictions',
component: 'tool_analytics'
}
},
'delete': {
title: {
key: 'delete',
component: 'tool_analytics'
}, body: {
key: 'deletemodelconfirmation',
component: 'tool_analytics'
}
}
};
/**
* Returns the model name.
*
* @param {Object} actionItem The action item DOM node.
* @return {String}
*/
var getModelName = function(actionItem) {
var wrap = $(actionItem).closest('[data-model-name]');
if (wrap.length) {
return wrap.attr('data-model-name');
} else {
log.error('Unexpected DOM error - unable to obtain the model name');
return '';
}
};
/** @alias module:tool_analytics/model */
return {
/**
* Displays a confirm modal window before executing the action.
*
* @param {String} actionId
* @param {String} actionType
*/
confirmAction: function(actionId, actionType) {
$('[data-action-id="' + actionId + '"]').on('click', function(ev) {
ev.preventDefault();
var a = $(ev.currentTarget);
if (typeof actionsList[actionType] === "undefined") {
log.error('Action "' + actionType + '" is not allowed.');
return;
}
var reqStrings = [
actionsList[actionType].title,
actionsList[actionType].body
];
reqStrings[1].param = getModelName(a);
var stringsPromise = Str.get_strings(reqStrings);
var modalPromise = ModalSaveCancel.create({});
$.when(stringsPromise, modalPromise).then(function(strings, modal) {
modal.setTitle(strings[0]);
modal.setBody(strings[1]);
modal.setSaveButtonText(strings[0]);
modal.getRoot().on(ModalEvents.save, function() {
window.location.href = a.attr('href');
});
modal.show();
return modal;
}).fail(Notification.exception);
});
},
/**
* Displays evaluation mode and time-splitting method choices.
*
* @param {String} actionId
* @param {Boolean} trainedOnlyExternally
*/
selectEvaluationOptions: function(actionId, trainedOnlyExternally) {
$('[data-action-id="' + actionId + '"]').on('click', function(ev) {
ev.preventDefault();
var a = $(ev.currentTarget);
var timeSplittingMethods = $(this).attr('data-timesplitting-methods');
ModalSaveCancel.create({
title: Str.get_string('evaluatemodel', 'tool_analytics'),
body: Templates.render('tool_analytics/evaluation_options', {
trainedexternally: trainedOnlyExternally,
timesplittingmethods: JSON.parse(timeSplittingMethods)
}),
removeOnClose: true,
buttons: {
save: Str.get_string('evaluate', 'tool_analytics'),
},
show: true,
})
.then((modal) => {
modal.getRoot().on(ModalEvents.save, function() {
// Evaluation mode.
var evaluationMode = $("input[name='evaluationmode']:checked").val();
if (evaluationMode == 'trainedmodel') {
a.attr('href', a.attr('href') + '&mode=trainedmodel');
}
// Selected time-splitting id.
var timeSplittingMethod = $("#id-evaluation-timesplitting").val();
a.attr('href', a.attr('href') + '&timesplitting=' + timeSplittingMethod);
window.location.href = a.attr('href');
return;
});
return modal;
}).catch(Notification.exception);
});
},
/**
* Displays export options.
*
* We have two main options: export training data and export configuration.
* The 2nd option has an extra option: include the trained algorithm weights.
*
* @param {String} actionId
* @param {Boolean} isTrained
*/
selectExportOptions: function(actionId, isTrained) {
$('[data-action-id="' + actionId + '"]').on('click', function(ev) {
ev.preventDefault();
var a = $(ev.currentTarget);
if (!isTrained) {
// Export the model configuration if the model is not trained. We can't export anything else.
a.attr('href', a.attr('href') + '&action=exportmodel&includeweights=0');
window.location.href = a.attr('href');
return;
}
var stringsPromise = Str.get_strings([
{
key: 'export',
component: 'tool_analytics'
}
]);
var modalPromise = ModalSaveCancel.create({
body: Templates.render('tool_analytics/export_options', {}),
removeOnClose: true,
});
$.when(stringsPromise, modalPromise).then(function(strings, modal) {
modal.setTitle(strings[0]);
modal.setSaveButtonText(strings[0]);
modal.getRoot().on(ModalEvents.save, function() {
var exportOption = $("input[name='exportoption']:checked").val();
if (exportOption == 'exportdata') {
a.attr('href', a.attr('href') + '&action=exportdata');
} else {
a.attr('href', a.attr('href') + '&action=exportmodel');
if ($("#id-includeweights").is(':checked')) {
a.attr('href', a.attr('href') + '&includeweights=1');
} else {
a.attr('href', a.attr('href') + '&includeweights=0');
}
}
window.location.href = a.attr('href');
return;
});
modal.show();
return modal;
}).fail(Notification.exception);
});
}
};
});
@@ -0,0 +1,61 @@
// 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/>.
/**
* Potential contexts selector module.
*
* @module tool_analytics/potential-contexts
* @copyright 2019 David Monllao
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define(['jquery', 'core/ajax'], function($, Ajax) {
return /** @alias module:tool_analytics/potential-contexts */ {
processResults: function(selector, results) {
var contexts = [];
if ($.isArray(results)) {
$.each(results, function(index, context) {
contexts.push({
value: context.id,
label: context.name
});
});
return contexts;
} else {
return results;
}
},
transport: function(selector, query, success, failure) {
var promise;
let modelid = $(selector).attr('modelid') || null;
promise = Ajax.call([{
methodname: 'tool_analytics_potential_contexts',
args: {
query: query,
modelid: modelid
}
}]);
promise[0].then(success).fail(failure);
}
};
});
@@ -0,0 +1,55 @@
<?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/>.
/**
* Helper class that contains helper functions for cli scripts.
*
* @package tool_analytics
* @copyright 2017 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics;
defined('MOODLE_INTERNAL') || die();
/**
* Helper class that contains helper functions for cli scripts.
*
* @package tool_analytics
* @copyright 2017 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class clihelper {
/**
* List all models in the system. To be used from cli scripts.
*
* @return void
*/
public static function list_models() {
cli_heading("List of models");
echo str_pad(get_string('modelid', 'tool_analytics'), 15, ' ') . ' ' . str_pad(get_string('name'), 50, ' ') .
' ' . str_pad(get_string('status'), 15, ' ') . "\n";
$models = \core_analytics\manager::get_all_models();
foreach ($models as $model) {
$modelid = $model->get_id();
$isenabled = $model->is_enabled() ? get_string('enabled', 'tool_analytics') : get_string('disabled', 'tool_analytics');
$name = $model->get_name();
echo str_pad($modelid, 15, ' ') . ' ' . str_pad($name, 50, ' ') . ' ' . str_pad($isenabled, 15, ' ') . "\n";
}
}
}
+109
View File
@@ -0,0 +1,109 @@
<?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 this component.
*
* @package tool_analytics
* @copyright 2019 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics;
use core_external\external_api;
use core_external\external_function_parameters;
use core_external\external_value;
use core_external\external_single_structure;
use core_external\external_multiple_structure;
/**
* This is the external API for this component.
*
* @copyright 2019 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class external extends external_api {
const MAX_CONTEXTS_RETURNED = 100;
/**
* potential_contexts parameters.
*
* @since Moodle 3.8
* @return external_function_parameters
*/
public static function potential_contexts_parameters() {
return new external_function_parameters(
array(
'query' => new external_value(PARAM_NOTAGS, 'The model id', VALUE_DEFAULT),
'modelid' => new external_value(PARAM_INT, 'The model id', VALUE_DEFAULT)
)
);
}
/**
* Return the contexts that match the provided query.
*
* @since Moodle 3.8
* @param string|null $query
* @param int|null $modelid
* @return array an array of contexts
*/
public static function potential_contexts(?string $query = null, ?int $modelid = null) {
$params = self::validate_parameters(self::potential_contexts_parameters(), ['modelid' => $modelid, 'query' => $query]);
\core_analytics\manager::check_can_manage_models();
if ($params['modelid']) {
$model = new \core_analytics\model($params['modelid']);
$contexts = ($model->get_analyser(['notimesplitting' => true]))::potential_context_restrictions($params['query']);
} else {
$contexts = \core_analytics\manager::get_potential_context_restrictions(null, $params['query']);
}
$contextoptions = [];
$i = 0;
foreach ($contexts as $contextid => $contextname) {
if ($i === self::MAX_CONTEXTS_RETURNED) {
// Limited to MAX_CONTEXTS_RETURNED items.
break;
}
$contextoptions[] = ['id' => $contextid, 'name' => $contextname];
$i++;
}
return $contextoptions;
}
/**
* potential_contexts return
*
* @since Moodle 3.8
* @return \core_external\external_description
*/
public static function potential_contexts_returns() {
return new external_multiple_structure(
new external_single_structure([
'id' => new external_value(PARAM_INT, 'ID of the context'),
'name' => new external_value(PARAM_NOTAGS, 'The context name')
])
);
}
}
@@ -0,0 +1,235 @@
<?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/>.
/**
* Model edit form.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\output\form;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->dirroot.'/lib/formslib.php');
/**
* Model edit form.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class edit_model extends \moodleform {
/**
* Form definition
*/
public function definition() {
global $OUTPUT;
$mform = $this->_form;
if ($this->_customdata['trainedmodel'] && $this->_customdata['staticmodel'] === false) {
$message = get_string('edittrainedwarning', 'tool_analytics');
$mform->addElement('html', $OUTPUT->notification($message, \core\output\notification::NOTIFY_WARNING));
}
$mform->addElement('advcheckbox', 'enabled', get_string('enabled', 'tool_analytics'));
// Target.
if (!empty($this->_customdata['targets'])) {
$targets = array('' => '');
foreach ($this->_customdata['targets'] as $classname => $target) {
$optionname = \tool_analytics\output\helper::class_to_option($classname);
$targets[$optionname] = $target->get_name();
}
$mform->addElement('select', 'target', get_string('target', 'tool_analytics'), $targets);
$mform->addHelpButton('target', 'target', 'tool_analytics');
$mform->addRule('target', get_string('required'), 'required', null, 'client');
}
if (!empty($this->_customdata['targetname']) && !empty($this->_customdata['targetclass'])) {
$mform->addElement('static', 'targetname', get_string('target', 'tool_analytics'), $this->_customdata['targetname']);
$mform->addElement('hidden', 'target',
\tool_analytics\output\helper::class_to_option($this->_customdata['targetclass']));
// We won't update the model's target so no worries about its format (we can't use PARAM_ALPHANUMEXT
// because of class_to_option).
$mform->setType('target', PARAM_TEXT);
}
// Indicators.
if (!$this->_customdata['staticmodel']) {
$indicators = array();
foreach ($this->_customdata['indicators'] as $classname => $indicator) {
$optionname = \tool_analytics\output\helper::class_to_option($classname);
$indicators[$optionname] = $indicator->get_name();
}
$options = array(
'multiple' => true
);
$mform->addElement('autocomplete', 'indicators', get_string('indicators', 'tool_analytics'), $indicators, $options);
$mform->setType('indicators', PARAM_ALPHANUMEXT);
$mform->addHelpButton('indicators', 'indicators', 'tool_analytics');
}
// Time-splitting methods.
if (!empty($this->_customdata['invalidcurrenttimesplitting'])) {
$mform->addElement('html', $OUTPUT->notification(
get_string('invalidcurrenttimesplitting', 'tool_analytics'),
\core\output\notification::NOTIFY_WARNING)
);
}
$timesplittings = array('' => '');
foreach ($this->_customdata['timesplittings'] as $classname => $timesplitting) {
$optionname = \tool_analytics\output\helper::class_to_option($classname);
$timesplittings[$optionname] = $timesplitting->get_name();
}
$mform->addElement('select', 'timesplitting', get_string('timesplittingmethod', 'analytics'), $timesplittings);
$mform->addHelpButton('timesplitting', 'timesplittingmethod', 'analytics');
// Contexts restriction.
if (!empty($this->_customdata['supportscontexts'])) {
$options = [
'ajax' => 'tool_analytics/potential-contexts',
'multiple' => true,
'noselectionstring' => get_string('all')
];
if (!empty($this->_customdata['id'])) {
$options['modelid'] = $this->_customdata['id'];
$contexts = $this->load_current_contexts();
} else {
// No need to preload any selected contexts.
$contexts = [];
}
$mform->addElement('autocomplete', 'contexts', get_string('contexts', 'tool_analytics'), $contexts, $options);
$mform->setType('contexts', PARAM_INT);
$mform->addHelpButton('contexts', 'contexts', 'tool_analytics');
}
// Predictions processor.
if (!$this->_customdata['staticmodel']) {
$defaultprocessor = \core_analytics\manager::get_predictions_processor_name(
\core_analytics\manager::get_predictions_processor()
);
$predictionprocessors = ['' => get_string('defaultpredictoroption', 'analytics', $defaultprocessor)];
foreach ($this->_customdata['predictionprocessors'] as $classname => $predictionsprocessor) {
if ($predictionsprocessor->is_ready() !== true) {
continue;
}
$optionname = \tool_analytics\output\helper::class_to_option($classname);
$predictionprocessors[$optionname] = \core_analytics\manager::get_predictions_processor_name($predictionsprocessor);
}
$mform->addElement('select', 'predictionsprocessor', get_string('predictionsprocessor', 'analytics'),
$predictionprocessors);
$mform->addHelpButton('predictionsprocessor', 'predictionsprocessor', 'analytics');
}
if (!empty($this->_customdata['id'])) {
$mform->addElement('hidden', 'id', $this->_customdata['id']);
$mform->setType('id', PARAM_INT);
$mform->addElement('hidden', 'action', 'edit');
$mform->setType('action', PARAM_ALPHANUMEXT);
}
$this->add_action_buttons();
}
/**
* Form validation
*
* @param array $data data from the form.
* @param array $files files uploaded.
*
* @return array of errors.
*/
public function validation($data, $files) {
$errors = parent::validation($data, $files);
$targetclass = \tool_analytics\output\helper::option_to_class($data['target']);
$target = \core_analytics\manager::get_target($targetclass);
if (!empty($data['timesplitting'])) {
$timesplittingclass = \tool_analytics\output\helper::option_to_class($data['timesplitting']);
if (\core_analytics\manager::is_valid($timesplittingclass, '\core_analytics\local\time_splitting\base') === false) {
$errors['timesplitting'] = get_string('errorinvalidtimesplitting', 'analytics');
}
$timesplitting = \core_analytics\manager::get_time_splitting($timesplittingclass);
if (!$target->can_use_timesplitting($timesplitting)) {
$errors['timesplitting'] = get_string('invalidtimesplitting', 'tool_analytics');
}
}
if (!empty($data['contexts'])) {
$analyserclass = $target->get_analyser_class();
if (!$potentialcontexts = $analyserclass::potential_context_restrictions()) {
$errors['contexts'] = get_string('errornocontextrestrictions', 'analytics');
} else {
// Flip the contexts array so we can just diff by key.
$selectedcontexts = array_flip($data['contexts']);
$invalidcontexts = array_diff_key($selectedcontexts, $potentialcontexts);
if (!empty($invalidcontexts)) {
$errors['contexts'] = get_string('errorinvalidcontexts', 'analytics');
}
}
}
if (!$this->_customdata['staticmodel']) {
if (empty($data['indicators'])) {
$errors['indicators'] = get_string('errornoindicators', 'analytics');
} else {
foreach ($data['indicators'] as $indicator) {
$realindicatorname = \tool_analytics\output\helper::option_to_class($indicator);
if (\core_analytics\manager::is_valid($realindicatorname, '\core_analytics\local\indicator\base') === false) {
$errors['indicators'] = get_string('errorinvalidindicator', 'analytics', $realindicatorname);
}
}
}
}
if (!empty($data['enabled']) && empty($data['timesplitting'])) {
$errors['enabled'] = get_string('errorcantenablenotimesplitting', 'tool_analytics');
}
return $errors;
}
/**
* Load the currently selected context options.
*
* @return array
*/
protected function load_current_contexts() {
$contexts = [];
foreach ($this->_customdata['contexts'] as $context) {
$contexts[$context->id] = $context->get_context_name(true, true);
}
return $contexts;
}
}
@@ -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/>.
/**
* Model upload form.
*
* @package tool_analytics
* @copyright 2017 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\output\form;
defined('MOODLE_INTERNAL') || die();
require_once($CFG->libdir.'/formslib.php');
/**
* Model upload form.
*
* @package tool_analytics
* @copyright 2017 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class import_model extends \moodleform {
/**
* Form definition.
*
* @return null
*/
public function definition() {
$mform = $this->_form;
$mform->addElement('header', 'settingsheader', get_string('importmodel', 'tool_analytics'));
$mform->addElement('filepicker', 'modelfile', get_string('file'), null, ['accepted_types' => '.zip']);
$mform->addRule('modelfile', null, 'required');
$mform->addElement('advcheckbox', 'ignoreversionmismatches', get_string('ignoreversionmismatches', 'tool_analytics'),
get_string('ignoreversionmismatchescheckbox', 'tool_analytics'));
$this->add_action_buttons(true, get_string('import'));
}
}
@@ -0,0 +1,158 @@
<?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/>.
/**
* Typical crappy helper class with tiny functions.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\output;
defined('MOODLE_INTERNAL') || die();
/**
* Helper class with general purpose tiny functions.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class helper {
/**
* Converts a class full name to a select option key
*
* @param string $class
* @return string
*/
public static function class_to_option($class) {
// Form field is PARAM_ALPHANUMEXT and we are sending fully qualified class names
// as option names, but replacing the backslash for a string that is really unlikely
// to ever be part of a class name.
return str_replace('\\', '__', $class ?? '');
}
/**
* option_to_class
*
* @param string $option
* @return string
*/
public static function option_to_class($option) {
// Really unlikely but yeah, I'm a bad booyyy.
return str_replace('__', '\\', $option);
}
/**
* Sets an analytics > analytics models > $title breadcrumb.
*
* @param string $title
* @param \moodle_url $url
* @param \context|null $context Defaults to context_system
* @return null
*/
public static function set_navbar(string $title, \moodle_url $url, ?\context $context = null) {
global $PAGE;
if (!$context) {
$context = \context_system::instance();
}
$PAGE->set_context($context);
$PAGE->set_url($url);
$PAGE->set_secondary_active_tab('siteadminnode');
$PAGE->set_primary_active_tab('siteadminnode');
if ($siteadmin = $PAGE->settingsnav->find('root', \navigation_node::TYPE_SITE_ADMIN)) {
$PAGE->navbar->add($siteadmin->get_content(), $siteadmin->action(),
\breadcrumb_navigation_node::TYPE_SITE_ADMIN, null, 'root');
}
if ($analytics = $PAGE->settingsnav->find('analytics', \navigation_node::TYPE_SETTING)) {
$PAGE->navbar->add($analytics->get_content(), $analytics->action());
}
if ($analyticmodels = $PAGE->settingsnav->find('analyticmodels', \navigation_node::TYPE_SETTING)) {
$PAGE->navbar->add($analyticmodels->get_content(), $analyticmodels->action());
}
$PAGE->navbar->add($title);
$PAGE->set_pagelayout('report');
$PAGE->set_title($title);
$PAGE->set_heading($title);
}
/**
* Resets the current page.
*
* Note that this function can only be used by analytics pages that work at the system context.
*
* @return null
*/
public static function reset_page() {
global $PAGE;
$PAGE->reset_theme_and_output();
$PAGE->set_context(\context_system::instance());
}
/**
* Convert a list of contexts to an associative array where the value is the context name.
*
* @param array $contexts
* @param \moodle_url $url
* @param \renderer_base $output
* @param int|null $selected
* @param bool $includeall
* @param bool $shortentext
* @return \stdClass
*/
public static function prediction_context_selector(array $contexts, \moodle_url $url, \renderer_base $output,
?int $selected = null, ?bool $includeall = false, ?bool $shortentext = true): \stdClass {
foreach ($contexts as $contextid => $unused) {
// We prepare this to be used as single_select template options.
$context = \context::instance_by_id($contextid);
// Special name for system level predictions as showing "System is not visually nice".
if ($contextid == SYSCONTEXTID) {
$contextname = get_string('allpredictions', 'tool_analytics');
} else {
if ($shortentext) {
$contextname = shorten_text($context->get_context_name(false, true), 40);
} else {
$contextname = $context->get_context_name(false, true);
}
}
$contexts[$contextid] = $contextname;
}
if ($includeall) {
$contexts[0] = get_string('all');
$nothing = '';
} else {
$nothing = array('' => 'choosedots');
}
\core_collator::asort($contexts);
if (!$selected) {
$selected = '';
}
$singleselect = new \single_select($url, 'contextid', $contexts, $selected, $nothing);
return $singleselect->export_for_template($output);
}
}
@@ -0,0 +1,184 @@
<?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/>.
/**
* Insights report renderable.
*
* @package tool_analytics
* @copyright 2019 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\output;
defined('MOODLE_INTERNAL') || die;
/**
* Insights report renderable.
*
* @package tool_analytics
* @copyright 2019 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class insights_report implements \renderable, \templatable {
/**
* @var \core_analytics\model
*/
private $model = null;
/**
* @var \context
*/
private $context = null;
/**
* Inits the insights report renderable.
*
* @param \core_analytics\model $model
* @param int|null $contextid
* @return null
*/
public function __construct(\core_analytics\model $model, ?int $contextid = null) {
$this->model = $model;
if ($contextid) {
$this->context = \context::instance_by_id($contextid);
}
}
/**
* Export the data.
*
* @param \renderer_base $output
* @return \stdClass
*/
public function export_for_template(\renderer_base $output): \stdClass {
// Prepare the context object.
$data = new \stdClass();
$data->modelname = $this->model->get_name();
$data->charts = [];
$predictionactionrecords = $this->model->get_prediction_actions($this->context);
// Context selector.
$predictioncontexts = $this->model->get_predictions_contexts(false);
if ($predictioncontexts && count($predictioncontexts) > 1) {
$url = new \moodle_url('/admin/tool/analytics/model.php', ['id' => $this->model->get_id(),
'action' => 'insightsreport']);
if ($this->context) {
$selected = $this->context->id;
} else {
// This is the 'all' option.
$selected = 0;
}
$data->contextselect = \tool_analytics\output\helper::prediction_context_selector($predictioncontexts,
$url, $output, $selected, true, false);
}
if ($predictionactionrecords->valid()) {
foreach ($predictionactionrecords as $record) {
// Using this unusual execution flow to init the chart data because $predictionactionrecords
// is a \moodle_recordset.
if (empty($actionlabels)) {
list($actionlabels, $actionvalues, $actiontypes) = $this->init_action_labels($record);
}
// One value for each action.
$actionvalues['separated'][$record->actionname]++;
// Grouped value.
$actiontype = $actiontypes[$record->actionname];
$actionvalues['grouped'][$actiontype]++;
}
$predictionactionrecords->close();
// Actions doughtnut.
$chart = new \core\chart_pie();
$chart->set_doughnut(true);
$chart->set_title(get_string('actionsexecutedbyusers', 'tool_analytics'));
$series = new \core\chart_series(get_string('actions', 'tool_analytics'),
array_values($actionvalues['separated']));
$chart->add_series($series);
$chart->set_labels(array_values($actionlabels['separated']));
$data->separatedchart = $output->render($chart);
// Positive/negative/neutral bar chart.
$chart = new \core\chart_bar();
$chart->set_title(get_string('actionexecutedgroupedusefulness', 'tool_analytics'));
$series = new \core\chart_series(get_string('actions', 'tool_analytics'),
array_values($actionvalues['grouped']));
$chart->add_series($series);
$chart->set_labels(array_values($actionlabels['grouped']));
$data->groupedchart = $output->render($chart);
} else {
$predictionactionrecords->close();
$data->noactions = [
'message' => get_string('noactionsfound', 'tool_analytics'),
'announce' => true,
];
}
return $data;
}
/**
* Initialises the action labels and values in this model.
*
* @param \stdClass $predictionactionrecord
* @return array Two-dimensional array with the labels and values initialised to zero.
*/
private function init_action_labels(\stdClass $predictionactionrecord): array {
$predictioncontext = \context::instance_by_id($predictionactionrecord->contextid);
// Just 1 result, we just want to retrieve the prediction action names.
list ($unused, $predictions) = $this->model->get_predictions($predictioncontext, false, 0, 1);
// We pass 'true' for $isinsightuser so all the prediction actions available for this target are returning.
$predictionactions = $this->model->get_target()->prediction_actions(reset($predictions), true, true);
$actionlabels = [];
$actionvalues = ['separated' => [], 'grouped' => []];
$actiontypes = [];
foreach ($predictionactions as $action) {
$actionlabels['separated'][$action->get_action_name()] = $action->get_text();
$actionvalues['separated'][$action->get_action_name()] = 0;
$actiontypes[$action->get_action_name()] = $action->get_type();
}
$bulkactions = $this->model->get_target()->bulk_actions($predictions);
foreach ($bulkactions as $action) {
$actionlabels['separated'][$action->get_action_name()] = $action->get_text();
$actionvalues['separated'][$action->get_action_name()] = 0;
$actiontypes[$action->get_action_name()] = $action->get_type();
}
$actionlabels['grouped'][\core_analytics\action::TYPE_POSITIVE] = get_string('useful', 'analytics');
$actionlabels['grouped'][\core_analytics\action::TYPE_NEUTRAL] = get_string('neutral', 'analytics');
$actionlabels['grouped'][\core_analytics\action::TYPE_NEGATIVE] = get_string('notuseful', 'analytics');
$actionvalues['grouped'][\core_analytics\action::TYPE_POSITIVE] = 0;
$actionvalues['grouped'][\core_analytics\action::TYPE_NEUTRAL] = 0;
$actionvalues['grouped'][\core_analytics\action::TYPE_NEGATIVE] = 0;
return [$actionlabels, $actionvalues, $actiontypes];
}
}
@@ -0,0 +1,167 @@
<?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/>.
/**
* Invalid analysables renderable.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\output;
defined('MOODLE_INTERNAL') || die;
/**
* Invalid analysables renderable.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class invalid_analysables implements \renderable, \templatable {
/**
* @var \core_analytics\model
*/
protected $model = null;
/**
* @var int
*/
protected $page = 0;
/**
* @var int
*/
protected $perpage = 0;
/**
* Inits the invalid analysables renderable.
*
* @param \core_analytics\model $model
* @param int $page
* @param int $perpage
* @return \stdClass
*/
public function __construct(\core_analytics\model $model, $page, $perpage) {
$this->model = $model;
$this->page = $page;
$this->perpage = $perpage;
}
/**
* Export the data.
*
* @param \renderer_base $output
* @return \stdClass
*/
public function export_for_template(\renderer_base $output) {
global $PAGE;
$offset = $this->page * $this->perpage;
$contexts = $this->model->get_contexts();
$analysables = $this->model->get_analyser(['notimesplitting' => true])->get_analysables_iterator(null, $contexts);
$skipped = 0;
$enoughresults = false;
$morepages = false;
$results = array();
foreach ($analysables as $analysable) {
if (!$analysable) {
continue;
}
$validtraining = $this->model->get_target()->is_valid_analysable($analysable, true);
if ($validtraining === true) {
if ($this->model->is_static()) {
// We still want to show this analysable if it is not valid to get predictions.
$validtraining = get_string('notrainingbasedassumptions', 'analytics');
} else {
// We skip analysables that are valid for training or valid for prediction.
continue;
}
}
$validprediction = $this->model->get_target()->is_valid_analysable($analysable, false);
if ($validprediction === true) {
// We skip analysables that are valid for training or valid for prediction.
continue;
}
if ($offset && $skipped < $offset) {
$skipped++;
continue;
}
// Add a new results if we don't have enough yet.
if (!$enoughresults) {
$results[$analysable->get_id()] = array($analysable, $validtraining, $validprediction);
if ($this->perpage && count($results) === $this->perpage) {
$enoughresults = true;
}
} else {
// Confirmed that we have results we can not fit into this page.
$morepages = true;
break;
}
}
// Prepare the context object.
$data = new \stdClass();
$data->modelname = $this->model->get_name();
if ($this->page > 0) {
$prev = clone $PAGE->url;
$prev->param('page', $this->page - 1);
$button = new \single_button($prev, get_string('previouspage', 'tool_analytics'), 'get');
$data->prev = $button->export_for_template($output);
}
if ($morepages) {
$next = clone $PAGE->url;
$next->param('page', $this->page + 1);
$button = new \single_button($next, get_string('nextpage', 'tool_analytics'), 'get');
$data->next = $button->export_for_template($output);
}
$data->analysables = [];
foreach ($results as list($analysable, $validtraining, $validprediction)) {
$obj = new \stdClass();
$obj->url = \html_writer::link($analysable->get_context()->get_url(), $analysable->get_name(),
array('target' => '_blank'));
if ($validtraining !== true) {
$obj->validtraining = $validtraining;
}
if ($validprediction !== true) {
$obj->validprediction = $validprediction;
}
$data->analysables[] = $obj;
}
if (empty($data->analysables)) {
$data->noanalysables = [
'message' => get_string('noinvalidanalysables', 'tool_analytics'),
'announce' => true,
];
}
return $data;
}
}
@@ -0,0 +1,217 @@
<?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/>.
/**
* Model logs table class.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\output;
defined('MOODLE_INTERNAL') || die;
require_once($CFG->libdir . '/tablelib.php');
/**
* Model logs table class.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class model_logs extends \table_sql {
/**
* @var \core_analytics\model
*/
protected $model = null;
/**
* @var string|false
*/
protected $evaluationmode = false;
/**
* Sets up the table_log parameters.
*
* @param string $uniqueid unique id of form.
* @param \core_analytics\model $model
*/
public function __construct($uniqueid, $model) {
global $PAGE;
parent::__construct($uniqueid);
$this->model = $model;
$this->set_attribute('class', 'modellog generaltable generalbox');
$this->set_attribute('aria-live', 'polite');
$this->define_columns(array('time', 'version', 'evaluationmode', 'indicators', 'timesplitting',
'accuracy', 'info', 'usermodified'));
$this->define_headers(array(
get_string('time'),
get_string('version'),
get_string('evaluationmode', 'tool_analytics'),
get_string('indicators', 'tool_analytics'),
get_string('timesplittingmethod', 'analytics'),
get_string('accuracy', 'tool_analytics'),
get_string('info', 'tool_analytics'),
get_string('fullnameuser'),
));
$evaluationmodehelp = new \help_icon('evaluationmode', 'tool_analytics');
$this->define_help_for_headers([null, null, $evaluationmodehelp, null, null, null, null, null]);
$this->pageable(true);
$this->collapsible(false);
$this->sortable(false);
$this->is_downloadable(false);
$this->evaluationmode = optional_param('evaluationmode', false, PARAM_ALPHANUM);
if ($this->evaluationmode && $this->evaluationmode != 'configuration' && $this->evaluationmode != 'trainedmodel') {
$this->evaluationmode = '';
}
$this->define_baseurl($PAGE->url);
}
/**
* Generate the version column.
*
* @param \stdClass $log log data.
* @return string HTML for the version column
*/
public function col_version($log) {
$recenttimestr = get_string('strftimerecent', 'core_langconfig');
return userdate($log->version, $recenttimestr);
}
/**
* Generate the evaluation mode column.
*
* @param \stdClass $log log data.
* @return string HTML for the evaluationmode column
*/
public function col_evaluationmode($log) {
return get_string('evaluationmodecol' . $log->evaluationmode, 'tool_analytics');
}
/**
* Generate the time column.
*
* @param \stdClass $log log data.
* @return string HTML for the time column
*/
public function col_time($log) {
$recenttimestr = get_string('strftimerecent', 'core_langconfig');
return userdate($log->timecreated, $recenttimestr);
}
/**
* Generate the indicators column.
*
* @param \stdClass $log log data.
* @return string HTML for the indicators column
*/
public function col_indicators($log) {
$indicatorclasses = json_decode($log->indicators);
$indicators = array();
foreach ($indicatorclasses as $indicatorclass) {
$indicator = \core_analytics\manager::get_indicator($indicatorclass);
if ($indicator) {
$indicators[] = $indicator->get_name();
} else {
debugging('Can\'t load ' . $indicatorclass . ' indicator', DEBUG_DEVELOPER);
}
}
return '<ul><li>' . implode('</li><li>', $indicators) . '</li></ul>';
}
/**
* Generate the context column.
*
* @param \stdClass $log log data.
* @return string HTML for the context column
*/
public function col_timesplitting($log) {
$timesplitting = \core_analytics\manager::get_time_splitting($log->timesplitting);
return $timesplitting->get_name();
}
/**
* Generate the accuracy column.
*
* @param \stdClass $log log data.
* @return string HTML for the accuracy column
*/
public function col_accuracy($log) {
return strval(round($log->score * 100, 2)) . '%';
}
/**
* Generate the info column.
*
* @param \stdClass $log log data.
* @return string HTML for the score column
*/
public function col_info($log) {
global $PAGE;
if (empty($log->info) && empty($log->dir)) {
return '';
}
$info = array();
if (!empty($log->info)) {
$info = json_decode($log->info);
}
if (!empty($log->dir)) {
$info[] = get_string('predictorresultsin', 'tool_analytics', $log->dir);
}
$PAGE->requires->js_call_amd('tool_analytics/log_info', 'loadInfo', array($log->id, $info));
return \html_writer::link('#', get_string('view'), array('data-model-log-id' => $log->id));
}
/**
* Generate the usermodified column.
*
* @param \stdClass $log log data.
* @return string HTML for the usermodified column
*/
public function col_usermodified($log) {
$user = \core_user::get_user($log->usermodified);
return fullname($user);
}
/**
* Query the logs table. Store results in the object for use by build_table.
*
* @param int $pagesize size of page for paginated displayed table.
* @param bool $useinitialsbar do you want to use the initials bar.
*/
public function query_db($pagesize, $useinitialsbar = true) {
$total = count($this->model->get_logs());
$this->pagesize($pagesize, $total);
$this->rawdata = $this->model->get_logs($this->get_page_start(), $this->get_page_size());
// Set initial bars.
if ($useinitialsbar) {
$this->initialbars($total > $pagesize);
}
}
}
@@ -0,0 +1,387 @@
<?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/>.
/**
* Prediction models list page.
*
* @package tool_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\output;
defined('MOODLE_INTERNAL') || die();
/**
* Shows tool_analytics models list.
*
* @package tool_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class models_list implements \renderable, \templatable {
/**
* models
*
* @var \core_analytics\model[]
*/
protected $models = array();
/**
* __construct
*
* @param \core_analytics\model[] $models
* @return void
*/
public function __construct($models) {
$this->models = $models;
}
/**
* Exports the data.
*
* @param \renderer_base $output
* @return \stdClass
*/
public function export_for_template(\renderer_base $output) {
global $PAGE;
$data = new \stdClass();
$newmodelmenu = new \action_menu();
$newmodelmenu->set_menu_trigger(get_string('newmodel', 'tool_analytics'), 'btn btn-secondary');
$newmodelmenu->set_menu_left();
$newmodelmenu->add(new \action_menu_link(
new \moodle_url('/admin/tool/analytics/createmodel.php'),
new \pix_icon('i/edit', ''),
get_string('createmodel', 'tool_analytics'),
false
));
$newmodelmenu->add(new \action_menu_link(
new \moodle_url('/admin/tool/analytics/importmodel.php'),
new \pix_icon('i/import', ''),
get_string('importmodel', 'tool_analytics'),
false
));
$newmodelmenu->add(new \action_menu_link(
new \moodle_url('/admin/tool/analytics/restoredefault.php'),
new \pix_icon('i/reload', ''),
get_string('restoredefault', 'tool_analytics'),
false
));
$data->newmodelmenu = $newmodelmenu->export_for_template($output);
$onlycli = get_config('analytics', 'onlycli');
if ($onlycli === false) {
// Default applied if no config found.
$onlycli = 1;
}
// Evaluation options.
$timesplittingsforevaluation = \core_analytics\manager::get_time_splitting_methods_for_evaluation(true);
$misconfiguredmodels = [];
$data->models = array();
foreach ($this->models as $model) {
$modeldata = $model->export($output);
// Check if there is a help icon for the target to show.
$identifier = $modeldata->target->get_identifier();
$component = $modeldata->target->get_component();
if (get_string_manager()->string_exists($identifier . '_help', $component)) {
$helpicon = new \help_icon($identifier, $component);
$modeldata->targethelp = $helpicon->export_for_template($output);
} else {
// We really want to encourage developers to add help to their targets.
debugging("The target '{$modeldata->target}' should include a '{$identifier}_help' string to
describe its purpose.", DEBUG_DEVELOPER);
}
if ($model->invalid_timesplitting_selected()) {
$misconfiguredmodels[$model->get_id()] = $model->get_name();
}
// Check if there is a help icon for the indicators to show.
if (!empty($modeldata->indicators)) {
$indicators = array();
foreach ($modeldata->indicators as $ind) {
// Create the indicator with the details we want for the context.
$indicator = new \stdClass();
$indicator->name = $ind->out();
$identifier = $ind->get_identifier();
$component = $ind->get_component();
if (get_string_manager()->string_exists($identifier . '_help', $component)) {
$helpicon = new \help_icon($identifier, $component);
$indicator->help = $helpicon->export_for_template($output);
} else {
// We really want to encourage developers to add help to their indicators.
debugging("The indicator '{$ind}' should include a '{$identifier}_help' string to
describe its purpose.", DEBUG_DEVELOPER);
}
$indicators[] = $indicator;
}
$modeldata->indicators = $indicators;
}
$modeldata->indicatorsnum = count($modeldata->indicators);
// Check if there is a help icon for the time splitting method.
if (!empty($modeldata->timesplitting)) {
$identifier = $modeldata->timesplitting->get_identifier();
$component = $modeldata->timesplitting->get_component();
if (get_string_manager()->string_exists($identifier . '_help', $component)) {
$helpicon = new \help_icon($identifier, $component);
$modeldata->timesplittinghelp = $helpicon->export_for_template($output);
} else {
// We really want to encourage developers to add help to their time splitting methods.
debugging("The analysis interval '{$modeldata->timesplitting}' should include a '{$identifier}_help'
string to describe its purpose.", DEBUG_DEVELOPER);
}
} else {
$helpicon = new \help_icon('timesplittingnotdefined', 'tool_analytics');
$modeldata->timesplittinghelp = $helpicon->export_for_template($output);
}
// Has this model generated predictions?.
$predictioncontexts = $model->get_predictions_contexts();
$anypredictionobtained = $model->any_prediction_obtained();
// Model predictions list.
if (!$model->is_enabled()) {
$modeldata->noinsights = get_string('disabledmodel', 'analytics');
} else if ($model->uses_insights()) {
if ($predictioncontexts) {
$url = new \moodle_url('/report/insights/insights.php', array('modelid' => $model->get_id()));
$modeldata->insights = \tool_analytics\output\helper::prediction_context_selector($predictioncontexts,
$url, $output);
}
if (empty($modeldata->insights)) {
if ($anypredictionobtained) {
$modeldata->noinsights = get_string('noinsights', 'analytics');
} else {
$modeldata->noinsights = get_string('nopredictionsyet', 'analytics');
}
}
} else {
$modeldata->noinsights = get_string('noinsightsmodel', 'analytics');
}
// Actions.
$actionsmenu = new \action_menu();
$actionsmenu->set_menu_trigger(get_string('actions'));
$actionsmenu->set_owner_selector('model-actions-' . $model->get_id());
$urlparams = ['id' => $model->get_id(), 'sesskey' => sesskey()];
// Get predictions.
if (!$onlycli && $modeldata->enabled && !empty($modeldata->timesplitting)) {
$urlparams['action'] = 'scheduledanalysis';
$url = new \moodle_url('/admin/tool/analytics/model.php', $urlparams);
$icon = new \action_menu_link_secondary($url,
new \pix_icon('i/notifications', get_string('executescheduledanalysis', 'tool_analytics')),
get_string('executescheduledanalysis', 'tool_analytics'));
$actionsmenu->add($icon);
}
// Evaluate machine-learning-based models.
if (!$onlycli && $model->get_indicators() && !$model->is_static()) {
// Extra is_trained call as trained_locally returns false if the model has not been trained yet.
$trainedonlyexternally = !$model->trained_locally() && $model->is_trained();
$actionid = 'evaluate-' . $model->get_id();
// Evaluation options.
$modeltimesplittingmethods = $this->timesplittings_options_for_evaluation($model, $timesplittingsforevaluation);
// Include the current time-splitting method as the default selection method the model already have one.
if ($model->get_model_obj()->timesplitting) {
$currenttimesplitting = ['id' => 'current', 'text' => get_string('currenttimesplitting', 'tool_analytics')];
array_unshift($modeltimesplittingmethods, $currenttimesplitting);
}
$evaluateparams = [$actionid, $trainedonlyexternally];
$PAGE->requires->js_call_amd('tool_analytics/model', 'selectEvaluationOptions', $evaluateparams);
$urlparams['action'] = 'evaluate';
$url = new \moodle_url('/admin/tool/analytics/model.php', $urlparams);
$icon = new \action_menu_link_secondary($url, new \pix_icon('i/calc', get_string('evaluate', 'tool_analytics')),
get_string('evaluate', 'tool_analytics'), ['data-action-id' => $actionid,
'data-timesplitting-methods' => json_encode($modeltimesplittingmethods)]);
$actionsmenu->add($icon);
}
// Machine-learning-based models evaluation log.
if (!$model->is_static() && $model->get_logs()) {
$urlparams['action'] = 'log';
$url = new \moodle_url('/admin/tool/analytics/model.php', $urlparams);
$icon = new \action_menu_link_secondary($url, new \pix_icon('i/report', get_string('viewlog', 'tool_analytics')),
get_string('viewlog', 'tool_analytics'));
$actionsmenu->add($icon);
}
// Edit model.
$urlparams['action'] = 'edit';
$url = new \moodle_url('/admin/tool/analytics/model.php', $urlparams);
$icon = new \action_menu_link_secondary($url, new \pix_icon('t/edit', get_string('edit')), get_string('edit'));
$actionsmenu->add($icon);
// Enable / disable.
if ($model->is_enabled() || !empty($modeldata->timesplitting)) {
// If there is no timesplitting method set, the model can not be enabled.
if ($model->is_enabled()) {
$action = 'disable';
$text = get_string('disable');
$icontype = 't/block';
} else {
$action = 'enable';
$text = get_string('enable');
$icontype = 'i/checked';
}
$urlparams['action'] = $action;
$url = new \moodle_url('/admin/tool/analytics/model.php', $urlparams);
$icon = new \action_menu_link_secondary($url, new \pix_icon($icontype, $text), $text);
$actionsmenu->add($icon);
}
// Export.
if (!$model->is_static()) {
$fullysetup = $model->get_indicators() && !empty($modeldata->timesplitting);
$istrained = $model->is_trained();
if ($fullysetup || $istrained) {
$url = new \moodle_url('/admin/tool/analytics/model.php', $urlparams);
// Clear the previous action param from the URL, we will set it in JS.
$url->remove_params('action');
$actionid = 'export-' . $model->get_id();
$PAGE->requires->js_call_amd('tool_analytics/model', 'selectExportOptions',
[$actionid, $istrained]);
$icon = new \action_menu_link_secondary($url, new \pix_icon('i/export',
get_string('export', 'tool_analytics')), get_string('export', 'tool_analytics'),
['data-action-id' => $actionid]);
$actionsmenu->add($icon);
}
}
// Insights report.
if (!empty($anypredictionobtained) && $model->uses_insights()) {
$urlparams['action'] = 'insightsreport';
$url = new \moodle_url('/admin/tool/analytics/model.php', $urlparams);
$pix = new \pix_icon('i/report', get_string('insightsreport', 'tool_analytics'));
$icon = new \action_menu_link_secondary($url, $pix, get_string('insightsreport', 'tool_analytics'));
$actionsmenu->add($icon);
}
// Invalid analysables.
$analyser = $model->get_analyser(['notimesplitting' => true]);
if (!$analyser instanceof \core_analytics\local\analyser\sitewide) {
$urlparams['action'] = 'invalidanalysables';
$url = new \moodle_url('/admin/tool/analytics/model.php', $urlparams);
$pix = new \pix_icon('i/report', get_string('invalidanalysables', 'tool_analytics'));
$icon = new \action_menu_link_secondary($url, $pix, get_string('invalidanalysables', 'tool_analytics'));
$actionsmenu->add($icon);
}
// Clear model.
if (!empty($anypredictionobtained) || $model->is_trained()) {
$actionid = 'clear-' . $model->get_id();
$PAGE->requires->js_call_amd('tool_analytics/model', 'confirmAction', [$actionid, 'clear']);
$urlparams['action'] = 'clear';
$url = new \moodle_url('/admin/tool/analytics/model.php', $urlparams);
$icon = new \action_menu_link_secondary($url, new \pix_icon('e/cleanup_messy_code',
get_string('clearpredictions', 'tool_analytics')), get_string('clearpredictions', 'tool_analytics'),
['data-action-id' => $actionid]);
$actionsmenu->add($icon);
}
// Delete model.
$actionid = 'delete-' . $model->get_id();
$PAGE->requires->js_call_amd('tool_analytics/model', 'confirmAction', [$actionid, 'delete']);
$urlparams['action'] = 'delete';
$url = new \moodle_url('/admin/tool/analytics/model.php', $urlparams);
$icon = new \action_menu_link_secondary($url, new \pix_icon('t/delete',
get_string('delete', 'tool_analytics')), get_string('delete', 'tool_analytics'),
['data-action-id' => $actionid]);
$actionsmenu->add($icon);
$modeldata->actions = $actionsmenu->export_for_template($output);
$data->models[] = $modeldata;
}
$data->warnings = [];
$data->infos = [];
if (!$onlycli) {
$data->warnings[] = (object)array('message' => get_string('bettercli', 'tool_analytics'), 'closebutton' => true);
} else {
$url = new \moodle_url('/admin/settings.php', array('section' => 'analyticssettings'),
'id_s_analytics_onlycli');
$langstrid = 'clievaluationandpredictionsnoadmin';
if (is_siteadmin()) {
$langstrid = 'clievaluationandpredictions';
}
$data->infos[] = (object)array('message' => get_string($langstrid, 'tool_analytics', $url->out()),
'closebutton' => true);
}
if ($misconfiguredmodels) {
$warningstr = get_string('invalidtimesplittinginmodels', 'tool_analytics', implode(', ', $misconfiguredmodels));
$data->warnings[] = (object)array('message' => $warningstr, 'closebutton' => true);
}
return $data;
}
/**
* Returns the list of time splitting methods that are available for evaluation.
*
* @param \core_analytics\model $model
* @param array $timesplittingsforevaluation
* @return array
*/
private function timesplittings_options_for_evaluation(\core_analytics\model $model,
array $timesplittingsforevaluation): array {
$modeltimesplittingmethods = [
['id' => 'all', 'text' => get_string('alltimesplittingmethods', 'tool_analytics')],
];
$potentialtimesplittingmethods = $model->get_potential_timesplittings();
foreach ($timesplittingsforevaluation as $timesplitting) {
if (empty($potentialtimesplittingmethods[$timesplitting->get_id()])) {
// This time-splitting method can not be used for this model.
continue;
}
$modeltimesplittingmethods[] = [
'id' => \tool_analytics\output\helper::class_to_option($timesplitting->get_id()),
'text' => $timesplitting->get_name()->out(),
];
}
return $modeltimesplittingmethods;
}
}
@@ -0,0 +1,259 @@
<?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/>.
/**
* Renderer.
*
* @package tool_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\output;
defined('MOODLE_INTERNAL') || die();
use plugin_renderer_base;
/**
* Renderer class.
*
* @package tool_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class renderer extends plugin_renderer_base {
/**
* Defer to template.
*
* @param \tool_analytics\output\models_list $modelslist
* @return string HTML
*/
protected function render_models_list(\tool_analytics\output\models_list $modelslist) {
$data = $modelslist->export_for_template($this);
return parent::render_from_template('tool_analytics/models_list', $data);
}
/**
* Renders a table.
*
* @param \table_sql $table
* @return string HTML
*/
public function render_table(\table_sql $table) {
ob_start();
$table->out(10, true);
$output = ob_get_contents();
ob_end_clean();
return $output;
}
/**
* Web interface evaluate results.
*
* @param \stdClass[] $results
* @param string[] $logs
* @return string HTML
*/
public function render_evaluate_results($results, $logs = array()) {
$output = '';
foreach ($results as $timesplittingid => $result) {
if (!CLI_SCRIPT) {
$output .= $this->output->box_start('generalbox mb-3');
}
// Check that the array key is a string, not all results depend on time splitting methods (e.g. general errors).
if (!is_numeric($timesplittingid)) {
$timesplitting = \core_analytics\manager::get_time_splitting($timesplittingid);
$langstrdata = (object)array('name' => $timesplitting->get_name(), 'id' => $timesplittingid);
if (CLI_SCRIPT) {
$output .= $this->output->heading(get_string('scheduledanalysisresultscli', 'tool_analytics', $langstrdata), 3);
} else {
$output .= $this->output->heading(get_string('scheduledanalysisresults', 'tool_analytics', $langstrdata), 3);
}
}
if ($result->status == 0) {
$output .= $this->output->notification(get_string('goodmodel', 'tool_analytics'),
\core\output\notification::NOTIFY_SUCCESS);
} else if ($result->status === \core_analytics\model::NO_DATASET) {
$output .= $this->output->notification(get_string('nodatatoevaluate', 'tool_analytics'),
\core\output\notification::NOTIFY_WARNING);
}
if (isset($result->score)) {
// Score.
$output .= $this->output->heading(get_string('accuracy', 'tool_analytics') . ': ' .
round(floatval($result->score), 4) * 100 . '%', 4);
}
if (!empty($result->info)) {
foreach ($result->info as $message) {
$output .= $this->output->notification($message, \core\output\notification::NOTIFY_WARNING);
}
}
if (!CLI_SCRIPT) {
$output .= $this->output->box_end();
}
}
// Info logged during evaluation.
if (!empty($logs) && debugging()) {
$output .= $this->output->heading(get_string('extrainfo', 'tool_analytics'), 3);
foreach ($logs as $log) {
$output .= $this->output->notification($log, \core\output\notification::NOTIFY_WARNING);
}
}
if (!CLI_SCRIPT) {
$output .= $this->output->single_button(new \moodle_url('/admin/tool/analytics/index.php'),
get_string('continue'), 'get');
}
return $output;
}
/**
* Web interface training & prediction results.
*
* @param \stdClass|false $trainresults
* @param string[] $trainlogs
* @param \stdClass|false $predictresults
* @param string[] $predictlogs
* @return string HTML
*/
public function render_get_predictions_results($trainresults = false, $trainlogs = array(), $predictresults = false, $predictlogs = array()) {
$output = '';
if ($trainresults || (!empty($trainlogs) && debugging())) {
$output .= $this->output->heading(get_string('trainingresults', 'tool_analytics'), 3);
}
if ($trainresults) {
if ($trainresults->status == 0) {
$output .= $this->output->notification(
get_string('trainingprocessfinished', 'tool_analytics'),
\core\output\notification::NOTIFY_SUCCESS);
} else if ($trainresults->status === \core_analytics\model::NO_DATASET ||
$trainresults->status === \core_analytics\model::NOT_ENOUGH_DATA) {
$output .= $this->output->notification(
get_string('nodatatotrain', 'tool_analytics'),
\core\output\notification::NOTIFY_WARNING);
} else {
$output .= $this->output->notification(
get_string('generalerror', 'tool_analytics', $trainresults->status),
\core\output\notification::NOTIFY_ERROR);
}
}
if (!empty($trainlogs) && debugging()) {
$output .= $this->output->heading(get_string('extrainfo', 'tool_analytics'), 4);
foreach ($trainlogs as $log) {
$output .= $this->output->notification($log, \core\output\notification::NOTIFY_WARNING);
}
}
if ($predictresults || (!empty($predictlogs) && debugging())) {
$output .= $this->output->heading(
get_string('predictionresults', 'tool_analytics'), 3, 'main mt-3');
}
if ($predictresults) {
if ($predictresults->status == 0) {
$output .= $this->output->notification(
get_string('predictionprocessfinished', 'tool_analytics'),
\core\output\notification::NOTIFY_SUCCESS);
} else if ($predictresults->status === \core_analytics\model::NO_DATASET ||
$predictresults->status === \core_analytics\model::NOT_ENOUGH_DATA) {
$output .= $this->output->notification(
get_string('nodatatopredict', 'tool_analytics'),
\core\output\notification::NOTIFY_WARNING);
} else {
$output .= $this->output->notification(
get_string('generalerror', 'tool_analytics', $predictresults->status),
\core\output\notification::NOTIFY_ERROR);
}
}
if (!empty($predictlogs) && debugging()) {
$output .= $this->output->heading(get_string('extrainfo', 'tool_analytics'), 4);
foreach ($predictlogs as $log) {
$output .= $this->output->notification($log, \core\output\notification::NOTIFY_WARNING);
}
}
if (!CLI_SCRIPT) {
$output .= $this->output->single_button(new \moodle_url('/admin/tool/analytics/index.php'),
get_string('continue'), 'get');
}
return $output;
}
/**
* Defer to template.
*
* @param \tool_analytics\output\insights_report $insightsreport
* @return string HTML
*/
protected function render_insights_report(\tool_analytics\output\insights_report $insightsreport): string {
$data = $insightsreport->export_for_template($this);
return parent::render_from_template('tool_analytics/insights_report', $data);
}
/**
* Defer to template.
*
* @param \tool_analytics\output\invalid_analysables $invalidanalysables
* @return string HTML
*/
protected function render_invalid_analysables(\tool_analytics\output\invalid_analysables $invalidanalysables) {
$data = $invalidanalysables->export_for_template($this);
return parent::render_from_template('tool_analytics/invalid_analysables', $data);
}
/**
* Renders an analytics disabled notification.
*
* @return string HTML
*/
public function render_analytics_disabled() {
global $FULLME;
$this->page->set_url($FULLME);
$this->page->set_title(get_string('pluginname', 'tool_analytics'));
$this->page->set_heading(get_string('pluginname', 'tool_analytics'));
$output = $this->output->header();
$output .= $this->output->notification(get_string('analyticsdisabled', 'analytics'),
\core\output\notification::NOTIFY_INFO);
$output .= \html_writer::tag('a', get_string('continue'), ['class' => 'btn btn-primary',
'href' => (new \moodle_url('/'))->out()]);
$output .= $this->output->footer();
return $output;
}
}
@@ -0,0 +1,142 @@
<?php
// This file is part of Moodle - https://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/>.
/**
* Provides {@link \tool_analytics\output\restorable_models} class.
*
* @package tool_analytics
* @category output
* @copyright 2019 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\output;
defined('MOODLE_INTERNAL') || die();
/**
* Represents the list of default models that can be eventually restored.
*
* @copyright 2019 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class restorable_models implements \renderable, \templatable {
/** @var array */
protected $models;
/**
* Instantiate an object of this class.
*
* @param array $models List of models as returned by {@link \core_analytics\manager::load_default_models_for_all_components()}
*/
public function __construct(array $models) {
$this->models = $models;
}
/**
* Export the list of models to be rendered.
*
* @param \renderer_base $output
* @return string
*/
public function export_for_template(\renderer_base $output) {
$components = [];
foreach ($this->models as $componentname => $modelslist) {
$component = [
'name' => $this->component_name($componentname),
'component' => $componentname,
'models' => [],
];
foreach ($modelslist as $definition) {
list($target, $indicators) = \core_analytics\manager::get_declared_target_and_indicators_instances($definition);
if (\core_analytics\model::exists($target, $indicators)) {
continue;
}
$targetnamelangstring = $target->get_name();
$model = [
'defid' => \core_analytics\manager::model_declaration_identifier($definition),
'targetname' => $targetnamelangstring,
'targetclass' => $definition['target'],
'indicatorsnum' => count($definition['indicators']),
'indicators' => [],
];
if (get_string_manager()->string_exists($targetnamelangstring->get_identifier().'_help',
$targetnamelangstring->get_component())) {
$helpicon = new \help_icon($targetnamelangstring->get_identifier(), $targetnamelangstring->get_component());
$model['targethelp'] = $helpicon->export_for_template($output);
}
foreach ($indicators as $indicator) {
$indicatornamelangstring = $indicator->get_name();
$indicatordata = [
'name' => $indicatornamelangstring,
'classname' => $indicator->get_id(),
];
if (get_string_manager()->string_exists($indicatornamelangstring->get_identifier().'_help',
$indicatornamelangstring->get_component())) {
$helpicon = new \help_icon($indicatornamelangstring->get_identifier(),
$indicatornamelangstring->get_component());
$indicatordata['indicatorhelp'] = $helpicon->export_for_template($output);
}
$model['indicators'][] = $indicatordata;
}
$component['models'][] = $model;
}
if (!empty($component['models'])) {
$components[] = $component;
}
}
$result = [
'hasdata' => !empty($components),
'components' => array_values($components),
'submiturl' => new \moodle_url('/admin/tool/analytics/restoredefault.php'),
'backurl' => new \moodle_url('/admin/tool/analytics/index.php'),
'sesskey' => sesskey(),
];
return $result;
}
/**
* Return a human readable name for the given frankenstyle component.
*
* @param string $component Frankenstyle component such as 'core', 'core_analytics' or 'mod_workshop'
* @return string Human readable name of the component
*/
protected function component_name(string $component): string {
if ($component === 'core' || strpos($component, 'core_')) {
return get_string('componentcore', 'tool_analytics');
} else {
return get_string('pluginname', $component);
}
}
}
@@ -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 tool_analytics.
*
* @package tool_analytics
* @copyright 2018 Zig Tan <zig@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for tool_analytics implementing null_provider.
*
* @copyright 2018 Zig Tan <zig@moodle.com>
* @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,82 @@
<?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/>.
/**
* Predict system models with new data available.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\task;
defined('MOODLE_INTERNAL') || die();
/**
* Predict system models with new data available.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class predict_models extends \core\task\scheduled_task {
/**
* get_name
*
* @return string
*/
public function get_name() {
return get_string('predictmodels', 'tool_analytics');
}
/**
* Executes the prediction task.
*
* @return void
*/
public function execute() {
global $OUTPUT, $PAGE;
if (!\core_analytics\manager::is_analytics_enabled()) {
mtrace(get_string('analyticsdisabled', 'analytics'));
return;
}
$models = \core_analytics\manager::get_all_models(true, true);
if (!$models) {
mtrace(get_string('errornoenabledandtrainedmodels', 'tool_analytics'));
return;
}
foreach ($models as $model) {
$renderer = $PAGE->get_renderer('tool_analytics');
$result = $model->predict();
// Reset the page as some indicators may call external functions that overwrite the page context.
\tool_analytics\output\helper::reset_page();
if ($result) {
echo $OUTPUT->heading(get_string('modelresults', 'tool_analytics', $model->get_name()));
echo $renderer->render_get_predictions_results(false, array(), $result, $model->get_analyser()->get_logs());
}
}
}
}
@@ -0,0 +1,91 @@
<?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/>.
/**
* Train system models with new data available.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace tool_analytics\task;
defined('MOODLE_INTERNAL') || die();
/**
* Train system models with new data available.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class train_models extends \core\task\scheduled_task {
/**
* get_name
*
* @return string
*/
public function get_name() {
return get_string('trainmodels', 'tool_analytics');
}
/**
* Executes the prediction task.
*
* @return void
*/
public function execute() {
global $OUTPUT, $PAGE;
if (!\core_analytics\manager::is_analytics_enabled()) {
mtrace(get_string('analyticsdisabled', 'analytics'));
return;
}
$models = \core_analytics\manager::get_all_models(true);
if (!$models) {
mtrace(get_string('errornoenabledmodels', 'tool_analytics'));
return;
}
foreach ($models as $model) {
if ($model->is_static()) {
// Skip models based on assumptions.
continue;
}
if (!$model->get_time_splitting()) {
// Can not train if there is no time splitting method selected.
continue;
}
$renderer = $PAGE->get_renderer('tool_analytics');
$result = $model->train();
// Reset the page as some indicators may call external functions that overwrite the page context.
\tool_analytics\output\helper::reset_page();
if ($result) {
echo $OUTPUT->heading(get_string('modelresults', 'tool_analytics', $model->get_name()));
echo $renderer->render_get_predictions_results($result, $model->get_analyser()->get_logs());
}
}
}
}
+84
View File
@@ -0,0 +1,84 @@
<?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/>.
/**
* Enables the provided model.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require_once(__DIR__ . '/../../../../config.php');
require_once($CFG->libdir.'/clilib.php');
$help = "Enables the provided model.
Options:
--modelid Model id
--list List models
--analysisinterval Time splitting method full class name
-h, --help Print out this help
Example:
\$ php admin/tool/analytics/cli/enable_model.php --modelid=1 --analysisinterval=\"\\core\\analytics\\time_splitting\\quarters\"
";
// Now get cli options.
list($options, $unrecognized) = cli_get_params(
array(
'help' => false,
'list' => false,
'modelid' => false,
'analysisinterval' => false
),
array(
'h' => 'help',
)
);
if ($options['help']) {
echo $help;
exit(0);
}
if (!\core_analytics\manager::is_analytics_enabled()) {
echo get_string('analyticsdisabled', 'analytics') . PHP_EOL;
exit(0);
}
if ($options['list'] || $options['modelid'] === false) {
\tool_analytics\clihelper::list_models();
exit(0);
}
if ($options['analysisinterval'] === false) {
echo $help;
exit(0);
}
// We need admin permissions.
\core\session\manager::set_user(get_admin());
$model = new \core_analytics\model($options['modelid']);
// Evaluate its suitability to predict accurately.
$model->enable($options['analysisinterval']);
cli_heading(get_string('success'));
exit(0);
+152
View File
@@ -0,0 +1,152 @@
<?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/>.
/**
* Evaluates the provided model.
*
* @package tool_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require_once(__DIR__ . '/../../../../config.php');
require_once($CFG->libdir.'/clilib.php');
$help = "Evaluates the provided model.
Options:
--modelid Model id
--list List models
--non-interactive Not interactive questions
--analysisinterval Restrict the evaluation to 1 single analysis interval (Optional)
--mode 'configuration' or 'trainedmodel'. You can only use mode=trainedmodel when the trained" .
" model was imported" . "
--reuse-prev-analysed Reuse recently analysed courses instead of analysing the whole site. Set it to false while" .
" coding indicators. Defaults to true (Optional)" . "
-h, --help Print out this help
Example:
\$ php admin/tool/analytics/cli/evaluate_model.php --modelid=1 --analysisinterval='\\core\\analytics\\time_splitting\\quarters'
";
// Now get cli options.
list($options, $unrecognized) = cli_get_params(
array(
'help' => false,
'modelid' => false,
'list' => false,
'analysisinterval' => false,
'mode' => 'configuration',
'reuse-prev-analysed' => true,
'non-interactive' => false,
),
array(
'h' => 'help',
)
);
if ($options['help']) {
echo $help;
exit(0);
}
if (!\core_analytics\manager::is_analytics_enabled()) {
echo get_string('analyticsdisabled', 'analytics') . PHP_EOL;
exit(0);
}
if ($options['list']) {
\tool_analytics\clihelper::list_models();
exit(0);
}
if ($options['modelid'] === false) {
// All actions but --list require a modelid.
echo $help;
exit(0);
}
if ($options['mode'] !== 'configuration' && $options['mode'] !== 'trainedmodel') {
cli_error('Error: The provided mode is not supported');
}
if ($options['mode'] == 'trainedmodel' && $options['analysisinterval']) {
cli_error('Sorry, no analysis interval can be specified when using \'trainedmodel\' mode.');
}
// We need admin permissions.
\core\session\manager::set_user(get_admin());
$model = new \core_analytics\model($options['modelid']);
mtrace(get_string('analysingsitedata', 'tool_analytics'));
if ($options['reuse-prev-analysed']) {
mtrace(get_string('evaluationinbatches', 'tool_analytics'));
}
$renderer = $PAGE->get_renderer('tool_analytics');
$analyseroptions = array(
'timesplitting' => $options['analysisinterval'],
'reuseprevanalysed' => $options['reuse-prev-analysed'],
'mode' => $options['mode'],
);
// Evaluate its suitability to predict accurately.
$results = $model->evaluate($analyseroptions);
// Reset the page as some indicators may call external functions that overwrite the page context.
\tool_analytics\output\helper::reset_page();
echo $renderer->render_evaluate_results($results, $model->get_analyser()->get_logs());
// Check that we have, at leasa,t 1 valid dataset (not necessarily good) to use.
foreach ($results as $result) {
if ($result->status !== \core_analytics\model::NO_DATASET &&
$result->status !== \core_analytics\model::GENERAL_ERROR) {
$validdatasets = true;
}
}
if (!empty($validdatasets) && !$model->is_enabled() && $options['non-interactive'] === false) {
// Select a dataset, train and enable the model.
$input = cli_input(get_string('clienablemodel', 'tool_analytics'));
while (!\core_analytics\manager::is_valid($input, '\core_analytics\local\time_splitting\base') && $input !== 'none') {
mtrace(get_string('errorunexistingtimesplitting', 'analytics'));
$input = cli_input(get_string('clienablemodel', 'tool_analytics'));
}
if ($input === 'none') {
exit(0);
}
// Refresh the instance to prevent unexpected issues.
$model = new \core_analytics\model($options['modelid']);
// Set the time splitting method file and enable it.
$model->enable($input);
mtrace(get_string('trainandpredictmodel', 'tool_analytics'));
// Train the model with the selected time splitting method and start predicting.
$model->train();
$model->predict();
}
exit(0);
@@ -0,0 +1,229 @@
<?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/>.
/**
* Guesses course start and end dates based on activity logs.
*
* @package tool_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
define('CLI_SCRIPT', true);
require_once(__DIR__ . '/../../../../config.php');
require_once($CFG->libdir.'/clilib.php');
require_once($CFG->dirroot . '/course/lib.php');
require_once($CFG->dirroot . '/course/format/weeks/lib.php');
$help = "Guesses course start and end dates based on activity logs.
IMPORTANT: Don't use this script if you keep previous academic years users enrolled in courses. Guesses would not be accurate.
Options:
--guessstart Guess the course start date (default to true)
--guessend Guess the course end date (default to true)
--guessall Guess all start and end dates, even if they are already set (default to false)
--update Update the db or just notify the guess (default to false)
--filter Analyser dependant. e.g. A courseid would evaluate the model using a single course (Optional)
-h, --help Print out this help
Example:
\$ php admin/tool/analytics/cli/guess_course_start_and_end_dates.php --update=1 --filter=123,321
";
// Now get cli options.
list($options, $unrecognized) = cli_get_params(
array(
'help' => false,
'guessstart' => true,
'guessend' => true,
'guessall' => false,
'update' => false,
'filter' => false
),
array(
'h' => 'help',
)
);
if ($options['help']) {
echo $help;
exit(0);
}
if ($options['guessstart'] === false && $options['guessend'] === false && $options['guessall'] === false) {
echo $help;
exit(0);
}
// Reformat them as an array.
if ($options['filter'] !== false) {
$options['filter'] = explode(',', clean_param($options['filter'], PARAM_SEQUENCE));
}
// We need admin permissions.
\core\session\manager::set_user(get_admin());
$conditions = array('id != 1');
if (!$options['guessall']) {
if ($options['guessstart']) {
$conditions[] = '(startdate is null or startdate = 0)';
}
if ($options['guessend']) {
$conditions[] = '(enddate is null or enddate = 0)';
}
}
$coursessql = '';
$params = null;
if ($options['filter']) {
list($coursessql, $params) = $DB->get_in_or_equal($options['filter'], SQL_PARAMS_NAMED);
$conditions[] = 'id ' . $coursessql;
}
$courses = $DB->get_recordset_select('course', implode(' AND ', $conditions), $params, 'sortorder ASC');
foreach ($courses as $course) {
tool_analytics_calculate_course_dates($course, $options);
}
$courses->close();
/**
* tool_analytics_calculate_course_dates
*
* @param stdClass $course
* @param array $options CLI options
* @return void
*/
function tool_analytics_calculate_course_dates($course, $options) {
global $DB, $OUTPUT;
$courseman = new \core_analytics\course($course);
$notification = $course->shortname . ' (id = ' . $course->id . '): ';
$originalenddate = null;
$guessedstartdate = null;
$guessedenddate = null;
$samestartdate = null;
$lowerenddate = null;
if ($options['guessstart'] || $options['guessall']) {
$originalstartdate = $course->startdate;
$guessedstartdate = $courseman->guess_start();
$samestartdate = ($guessedstartdate == $originalstartdate);
$lowerenddate = ($course->enddate && ($course->enddate < $guessedstartdate));
if ($samestartdate) {
if (!$guessedstartdate) {
$notification .= PHP_EOL . ' ' . get_string('cantguessstartdate', 'tool_analytics');
} else {
// No need to update.
$notification .= PHP_EOL . ' ' . get_string('samestartdate', 'tool_analytics') . ': ' . userdate($guessedstartdate);
}
} else if (!$guessedstartdate) {
$notification .= PHP_EOL . ' ' . get_string('cantguessstartdate', 'tool_analytics');
} else if ($lowerenddate) {
$notification .= PHP_EOL . ' ' . get_string('cantguessstartdate', 'tool_analytics') . ': ' .
get_string('enddatebeforestartdate', 'error') . ' - ' . userdate($guessedstartdate);
} else {
// Update it to something we guess.
// We set it to $course even if we don't update because may be needed to guess the end one.
$course->startdate = $guessedstartdate;
$notification .= PHP_EOL . ' ' . get_string('startdate') . ': ' . userdate($guessedstartdate);
// Two different course updates because week's end date may be recalculated after setting the start date.
if ($options['update']) {
update_course($course);
// Refresh course data as end date may have been updated.
$course = $DB->get_record('course', array('id' => $course->id));
$courseman = new \core_analytics\course($course);
}
}
}
if ($options['guessend'] || $options['guessall']) {
if (!empty($lowerenddate) && !empty($guessedstartdate)) {
$course->startdate = $guessedstartdate;
}
$originalenddate = $course->enddate;
$format = course_get_format($course);
$formatoptions = $format->get_format_options();
// Change this for a course formats API level call in MDL-60702.
if ((get_class($format) == 'format_weeks' || is_subclass_of($format, 'format_weeks')) &&
method_exists($format, 'update_end_date') && $formatoptions['automaticenddate']) {
// Special treatment for weeks-based formats with automatic end date.
if ($options['update']) {
$format::update_end_date($course->id);
$course->enddate = $DB->get_field('course', 'enddate', array('id' => $course->id));
$notification .= PHP_EOL . ' ' . get_string('weeksenddateautomaticallyset', 'tool_analytics') . ': ' .
userdate($course->enddate);
} else {
// We can't provide more info without actually updating it in db.
$notification .= PHP_EOL . ' ' . get_string('weeksenddatedefault', 'tool_analytics');
}
} else {
$guessedenddate = $courseman->guess_end();
if ($guessedenddate == $originalenddate) {
if (!$guessedenddate) {
$notification .= PHP_EOL . ' ' . get_string('cantguessenddate', 'tool_analytics');
} else {
// No need to update.
$notification .= PHP_EOL . ' ' . get_string('sameenddate', 'tool_analytics') . ': ' . userdate($guessedenddate);
}
} else if (!$guessedenddate) {
$notification .= PHP_EOL . ' ' . get_string('cantguessenddate', 'tool_analytics');
} else {
// Update it to something we guess.
$course->enddate = $guessedenddate;
$updateit = false;
if ($course->enddate < $course->startdate) {
$notification .= PHP_EOL . ' ' . get_string('errorendbeforestart', 'course', userdate($course->enddate));
} else if ($course->startdate + (YEARSECS + (WEEKSECS * 4)) > $course->enddate) {
$notification .= PHP_EOL . ' ' . get_string('coursetoolong', 'course');
} else {
$notification .= PHP_EOL . ' ' . get_string('enddate') . ': ' . userdate($course->enddate);
$updateit = true;
}
if ($options['update'] && $updateit) {
update_course($course);
}
}
}
}
mtrace($notification);
}
mtrace(get_string('success'));
exit(0);
+105
View File
@@ -0,0 +1,105 @@
<?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/>.
/**
* Create model form.
*
* @package tool_analytics
* @copyright 2019 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../../../config.php');
require_login();
\core_analytics\manager::check_can_manage_models();
if (!\core_analytics\manager::is_analytics_enabled()) {
$PAGE->set_context(\context_system::instance());
$renderer = $PAGE->get_renderer('tool_analytics');
echo $renderer->render_analytics_disabled();
exit(0);
}
$returnurl = new \moodle_url('/admin/tool/analytics/index.php');
$url = new \moodle_url('/admin/tool/analytics/createmodel.php');
$title = get_string('createmodel', 'tool_analytics');
\tool_analytics\output\helper::set_navbar($title, $url);
// Static targets are not editable, we discard them.
$targets = array_filter(\core_analytics\manager::get_all_targets(), function($target) {
return (!$target->based_on_assumptions());
});
// Set 'supportscontexts' to true as at this stage we don't know if the contexts are supported by
// the selected target.
$customdata = array(
'trainedmodel' => false,
'staticmodel' => false,
'targets' => $targets,
'indicators' => \core_analytics\manager::get_all_indicators(),
'timesplittings' => \core_analytics\manager::get_all_time_splittings(),
'predictionprocessors' => \core_analytics\manager::get_all_prediction_processors(),
'supportscontexts' => true,
);
$mform = new \tool_analytics\output\form\edit_model(null, $customdata);
if ($mform->is_cancelled()) {
redirect($returnurl);
} else if ($data = $mform->get_data()) {
// Converting option names to class names.
$targetclass = \tool_analytics\output\helper::option_to_class($data->target);
if (empty($targets[$targetclass])) {
throw new \moodle_exception('errorinvalidtarget', 'analytics', '', $targetclass);
}
$target = $targets[$targetclass];
$indicators = array();
foreach ($data->indicators as $indicator) {
$indicatorinstance = \core_analytics\manager::get_indicator(
\tool_analytics\output\helper::option_to_class($indicator)
);
$indicators[$indicatorinstance->get_id()] = $indicatorinstance;
}
$timesplitting = \tool_analytics\output\helper::option_to_class($data->timesplitting);
$predictionsprocessor = \tool_analytics\output\helper::option_to_class($data->predictionsprocessor);
// Insert the model into db.
$model = \core_analytics\model::create($target, []);
// Filter out indicators that can not be used by this target.
$invalidindicators = array_diff_key($indicators, $model->get_potential_indicators());
if ($invalidindicators) {
$indicators = array_diff_key($indicators, $invalidindicators);
}
// Update the model with the rest of the data provided in the form.
$model->update($data->enabled, $indicators, $timesplitting, $predictionsprocessor, $data->contexts);
$message = '';
$messagetype = \core\output\notification::NOTIFY_SUCCESS;
if (!empty($invalidindicators)) {
$message = get_string('invalidindicatorsremoved', 'tool_analytics');
}
redirect($returnurl, $message, 0, $messagetype);
}
echo $OUTPUT->header();
$mform->display();
echo $OUTPUT->footer();
+37
View File
@@ -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/>.
/**
* Tool analytics webservice definitions.
*
* @package tool_analytics
* @copyright 2019 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$functions = array(
'tool_analytics_potential_contexts' => array(
'classname' => 'tool_analytics\external',
'methodname' => 'potential_contexts',
'description' => 'Retrieve the list of potential contexts for a model.',
'type' => 'read',
'ajax' => true,
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
),
);
+47
View File
@@ -0,0 +1,47 @@
<?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 defines tasks performed by the tool.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
// List of tasks.
$tasks = array(
array(
'classname' => 'tool_analytics\task\train_models',
'blocking' => 0,
'minute' => '0',
'hour' => 'R',
'day' => '*',
'dayofweek' => '*',
'month' => '*'
),
array(
'classname' => 'tool_analytics\task\predict_models',
'blocking' => 0,
'minute' => '0',
'hour' => 'R',
'day' => '*',
'dayofweek' => '*',
'month' => '*'
),
);
+66
View File
@@ -0,0 +1,66 @@
<?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/>.
/**
* Import models tool frontend.
*
* @package tool_analytics
* @copyright 2017 onwards Ankit Agarwal
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../../../config.php');
require_login();
\core_analytics\manager::check_can_manage_models();
if (!\core_analytics\manager::is_analytics_enabled()) {
$PAGE->set_context(\context_system::instance());
$renderer = $PAGE->get_renderer('tool_analytics');
echo $renderer->render_analytics_disabled();
exit(0);
}
$returnurl = new \moodle_url('/admin/tool/analytics/index.php');
$url = new \moodle_url('/admin/tool/analytics/importmodel.php');
$title = get_string('importmodel', 'tool_analytics');
\tool_analytics\output\helper::set_navbar($title, $url);
$form = new \tool_analytics\output\form\import_model();
if ($form->is_cancelled()) {
redirect($returnurl);
} else if ($data = $form->get_data()) {
$modelconfig = new \core_analytics\model_config();
$zipfilepath = $form->save_temp_file('modelfile');
list ($modeldata, $unused) = $modelconfig->extract_import_contents($zipfilepath);
if ($error = $modelconfig->check_dependencies($modeldata, $data->ignoreversionmismatches)) {
// The file is not available until the form is validated so we need an alternative method to show errors.
redirect($url, $error, 0, \core\output\notification::NOTIFY_ERROR);
}
\core_analytics\model::import_model($zipfilepath);
redirect($returnurl, get_string('importedsuccessfully', 'tool_analytics'), 0,
\core\output\notification::NOTIFY_SUCCESS);
}
echo $OUTPUT->header();
$form->display();
echo $OUTPUT->footer();
+37
View File
@@ -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/>.
/**
* Prediction models tool frontend.
*
* @package tool_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../../../config.php');
require_once($CFG->libdir . '/adminlib.php');
admin_externalpage_setup('analyticmodels', '', null, '', array('pagelayout' => 'report'));
$models = \core_analytics\manager::get_all_models();
echo $OUTPUT->header();
$templatable = new \tool_analytics\output\models_list($models);
echo $PAGE->get_renderer('tool_analytics')->render($templatable);
echo $OUTPUT->footer();
@@ -0,0 +1,154 @@
<?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 tool_analytics.
*
* @package tool_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['accuracy'] = 'Accuracy';
$string['actions'] = 'Actions';
$string['actionsexecutedbyusers'] = 'Actions executed by users';
$string['actionsexecutedbyusersfor'] = 'Actions executed by users for "{$a}" model';
$string['actionexecutedgroupedusefulness'] = 'Grouped actions';
$string['allpredictions'] = 'All predictions';
$string['alltimesplittingmethods'] = 'All analysis intervals';
$string['analysingsitedata'] = 'Analysing the site';
$string['analysis'] = 'Analysis';
$string['analyticmodels'] = 'Analytics models';
$string['bettercli'] = 'Evaluating models and generating predictions may involve heavy processing. It is recommended to run these actions from the command line.';
$string['cantguessstartdate'] = 'Can\'t guess the start date';
$string['cantguessenddate'] = 'Can\'t guess the end date';
$string['classdoesnotexist'] = 'Class {$a} does not exist';
$string['clearpredictions'] = 'Clear predictions';
$string['clearmodelpredictions'] = 'Are you sure you want to clear all "{$a}" predictions?';
$string['clienablemodel'] = 'You can enable the model by selecting an analysis interval by its ID. Note that you can also enable it later using the web interface (\'none\' to exit).';
$string['clievaluationandpredictions'] = 'A scheduled task iterates through enabled models and gets predictions. Models evaluation via the web interface is disabled. You can allow these processes to be executed manually via the web interface by disabling the <a href="{$a}">\'onlycli\'</a> analytics setting.';
$string['clievaluationandpredictionsnoadmin'] = 'A scheduled task iterates through enabled models and gets predictions. Models evaluation via the web interface is disabled. It may be enabled by a site administrator.';
$string['component'] = 'Component';
$string['componentcore'] = 'Core';
$string['componentselect'] = 'Select all models provided by the component \'{$a}\'';
$string['componentselectnone'] = 'Unselect all';
$string['contexts'] = 'Contexts';
$string['contexts_help'] = 'The model will be limited to this set of contexts. No context restrictions will be applied if no contexts are selected.';
$string['createmodel'] = 'Create model';
$string['currenttimesplitting'] = 'Current analysis interval';
$string['delete'] = 'Delete';
$string['deletemodelconfirmation'] = 'Are you sure you want to delete "{$a}"? These changes cannot be reverted.';
$string['disabled'] = 'Disabled';
$string['editmodel'] = 'Edit "{$a}" model';
$string['edittrainedwarning'] = 'This model has already been trained. Note that changing its indicators or its analysis interval will delete its previous predictions and start generating new predictions.';
$string['enabled'] = 'Enabled';
$string['errorcantenablenotimesplitting'] = 'You need to select an analysis interval before enabling the model';
$string['errornoenabledandtrainedmodels'] = 'There are no enabled and trained models to predict.';
$string['errornoenabledmodels'] = 'There are no enabled models to train.';
$string['errornoexport'] = 'Only trained models can be exported';
$string['errornostaticevaluated'] = 'Models based on assumptions cannot be evaluated. They are always 100% correct according to how they were defined.';
$string['errornostaticlog'] = 'Models based on assumptions cannot be evaluated because there is no performance log.';
$string['erroronlycli'] = 'Execution only allowed via command line';
$string['errortrainingdataexport'] = 'The model training data could not be exported';
$string['evaluate'] = 'Evaluate';
$string['evaluatemodel'] = 'Evaluate model';
$string['evaluationmode'] = 'Evaluation mode';
$string['evaluationmode_help'] = 'There are two evaluation modes:
* Trained model - Site data is used as testing data to evaluate the accuracy of the trained model.
* Configuration - Site data is split into training and testing data, to both train and test the accuracy of the model configuration.
Trained model is only available if a trained model has been imported into the site, and has not yet been re-trained using site data.';
$string['evaluationmodeinfo'] = 'This model has been imported into the site. You can either evaluate the performance of the model, or you can evaluate the performance of the model configuration using site data.';
$string['evaluationmodetrainedmodel'] = 'Evaluate the trained model';
$string['evaluationmodecoltrainedmodel'] = 'Trained model';
$string['evaluationmodecolconfiguration'] = 'Configuration';
$string['evaluationmodeconfiguration'] = 'Evaluate the model configuration';
$string['evaluationinbatches'] = 'The site contents are calculated and stored in batches. The evaluation process may be stopped at any time. The next time it is run, it will continue from the point when it was stopped.';
$string['executescheduledanalysis'] = 'Execute scheduled analysis';
$string['export'] = 'Export';
$string['exportincludeweights'] = 'Include the weights of the trained model';
$string['exportmodel'] = 'Export configuration';
$string['exporttrainingdata'] = 'Export training data';
$string['extrainfo'] = 'Info';
$string['generalerror'] = 'Evaluation error. Status code {$a}';
$string['goodmodel'] = 'This is a good model for using to obtain predictions. Enable it to start obtaining predictions.';
$string['importmodel'] = 'Import model';
$string['indicators'] = 'Indicators';
$string['indicators_help'] = 'The indicators are what you think will lead to an accurate prediction of the target.';
$string['indicators_link'] = 'Indicators';
$string['indicatorsnum'] = 'Number of indicators: {$a}';
$string['info'] = 'Info';
$string['insightsreport'] = 'Insights report';
$string['ignoreversionmismatches'] = 'Ignore version mismatches';
$string['ignoreversionmismatchescheckbox'] = 'Ignore the differences between this site version and the original site version.';
$string['importedsuccessfully'] = 'The model has been successfully imported.';
$string['insights'] = 'Insights';
$string['invalidanalysables'] = 'Invalid site elements';
$string['invalidanalysablesinfo'] = 'This page lists analysable elements that can\'t be used by this prediction model. The listed elements can\'t be used either to train the prediction model nor can the prediction model obtain predictions for them.';
$string['invalidanalysablestable'] = 'Invalid site analysable elements table';
$string['invalidcurrenttimesplitting'] = 'The current analysis interval is invalid for the target of this model. Please select a different interval.';
$string['invalidindicatorsremoved'] = 'A new model has been added. Indicators that don\'t work with the selected target have been automatically removed.';
$string['invalidtimesplitting'] = 'The selected analysis interval is invalid for the selected target.';
$string['invalidtimesplittinginmodels'] = 'The analysis interval used by some of the models is invalid. Please select a different interval for the following models: {$a}';
$string['invalidprediction'] = 'Invalid to get predictions';
$string['invalidtraining'] = 'Invalid to train the model';
$string['loginfo'] = 'Log extra info';
$string['missingmoodleversion'] = 'Imported file doesn\'t define a version number';
$string['modelid'] = 'Model ID';
$string['modelinvalidanalysables'] = 'Invalid analysable elements for "{$a}" model';
$string['modelname'] = 'Model name';
$string['modelresults'] = '{$a} results';
$string['modeltimesplitting'] = 'Analysis interval';
$string['newmodel'] = 'New model';
$string['nextpage'] = 'Next page';
$string['noactionsfound'] = 'Users have not executed any actions on the generated insights.';
$string['nodatatoevaluate'] = 'There is no data to evaluate the model';
$string['nodatatopredict'] = 'No new elements to get predictions for.';
$string['nodatatotrain'] = 'There is no new data that can be used for training.';
$string['noinvalidanalysables'] = 'This site does not contain any invalid analysable element.';
$string['notdefined'] = 'Not yet defined';
$string['pluginname'] = 'Analytic models';
$string['predictionresults'] = 'Prediction results';
$string['predictmodels'] = 'Predict models';
$string['predictorresultsin'] = 'Predictor logged information in {$a} directory';
$string['predictionprocessfinished'] = 'Prediction process finished';
$string['previouspage'] = 'Previous page';
$string['restoredefault'] = 'Restore default models';
$string['restoredefaultempty'] = 'Please select models to be restored.';
$string['restoredefaultinfo'] = 'These default models are missing or have changed since being installed. You can restore selected default models.';
$string['restoredefaultnone'] = 'All default models provided by core and installed plugins have been created. No new models were found; there is nothing to restore.';
$string['restoredefaultsome'] = 'Succesfully re-created {$a->count} new model(s).';
$string['restoredefaultsubmit'] = 'Restore selected';
$string['samestartdate'] = 'Current start date is good';
$string['sameenddate'] = 'Current end date is good';
$string['scheduledanalysisresults'] = 'Results using {$a->name} analysis interval';
$string['scheduledanalysisresultscli'] = 'Results using {$a->name} (id: {$a->id}) analysis interval';
$string['selecttimesplittingforevaluation'] = 'Select the analysis interval you want to use to evaluate the model configuration.';
$string['target'] = 'Target';
$string['target_help'] = 'The target is what the model will predict.';
$string['target_link'] = 'Targets';
$string['timesplittingnotdefined'] = 'No analysis interval is defined.';
$string['timesplittingnotdefined_help'] = 'You need to select an analysis interval before enabling the model.';
$string['trainandpredictmodel'] = 'Training model and calculating predictions';
$string['trainingprocessfinished'] = 'Training process finished';
$string['trainingresults'] = 'Training results';
$string['trainmodels'] = 'Train models';
$string['versionnotsame'] = 'Imported file was from a different version ({$a->importedversion}) than the current one ({$a->version})';
$string['viewlog'] = 'Evaluation log';
$string['weeksenddateautomaticallyset'] = 'End date automatically set based on start date and the number of sections';
$string['weeksenddatedefault'] = 'End date automatically calculated from the course start date.';
$string['privacy:metadata'] = 'The Analytic models plugin does not store any personal data.';
+328
View File
@@ -0,0 +1,328 @@
<?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/>.
/**
* Model-related actions.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../../../config.php');
require_once($CFG->libdir . '/filelib.php');
$id = required_param('id', PARAM_INT);
$action = required_param('action', PARAM_ALPHANUMEXT);
require_login();
$model = new \core_analytics\model($id);
\core_analytics\manager::check_can_manage_models();
if (!\core_analytics\manager::is_analytics_enabled()) {
$PAGE->set_context(\context_system::instance());
$renderer = $PAGE->get_renderer('tool_analytics');
echo $renderer->render_analytics_disabled();
exit(0);
}
$returnurl = new \moodle_url('/admin/tool/analytics/index.php');
$params = array('id' => $id, 'action' => $action);
$url = new \moodle_url('/admin/tool/analytics/model.php', $params);
switch ($action) {
case 'edit':
$title = get_string('editmodel', 'tool_analytics', $model->get_name());
break;
case 'evaluate':
$title = get_string('evaluatemodel', 'tool_analytics');
break;
case 'scheduledanalysis':
$title = get_string('analysis', 'tool_analytics');
break;
case 'log':
$title = get_string('viewlog', 'tool_analytics');
break;
case 'enable':
$title = get_string('enable');
break;
case 'disable':
$title = get_string('disable');
break;
case 'delete':
$title = get_string('delete');
break;
case 'exportdata':
$title = get_string('exporttrainingdata', 'tool_analytics');
break;
case 'exportmodel':
$title = get_string('exportmodel', 'tool_analytics');
break;
case 'clear':
$title = get_string('clearpredictions', 'tool_analytics');
break;
case 'insightsreport':
$title = get_string('insightsreport', 'tool_analytics');
break;
case 'invalidanalysables':
$title = get_string('invalidanalysables', 'tool_analytics');
break;
default:
throw new moodle_exception('errorunknownaction', 'analytics');
}
\tool_analytics\output\helper::set_navbar($title, $url);
$onlycli = get_config('analytics', 'onlycli');
if ($onlycli === false) {
// Default applied if no config found.
$onlycli = 1;
}
switch ($action) {
case 'enable':
require_sesskey();
$model->enable();
redirect($returnurl);
break;
case 'disable':
require_sesskey();
$model->update(0, false, false);
redirect($returnurl);
break;
case 'delete':
require_sesskey();
$model->delete();
redirect($returnurl);
break;
case 'edit':
require_sesskey();
$invalidcurrenttimesplitting = $model->invalid_timesplitting_selected();
$potentialtimesplittings = $model->get_potential_timesplittings();
$customdata = array(
'id' => $model->get_id(),
'trainedmodel' => $model->is_trained(),
'staticmodel' => $model->is_static(),
'invalidcurrenttimesplitting' => (!empty($invalidcurrenttimesplitting)),
'targetclass' => $model->get_target()->get_id(),
'targetname' => $model->get_target()->get_name(),
'indicators' => $model->get_potential_indicators(),
'timesplittings' => $potentialtimesplittings,
'predictionprocessors' => \core_analytics\manager::get_all_prediction_processors(),
'supportscontexts' => ($model->get_analyser(['notimesplitting' => true]))::context_restriction_support(),
'contexts' => $model->get_contexts(),
);
$mform = new \tool_analytics\output\form\edit_model(null, $customdata);
if ($mform->is_cancelled()) {
redirect($returnurl);
} else if ($data = $mform->get_data()) {
$timesplitting = \tool_analytics\output\helper::option_to_class($data->timesplitting);
if (!$model->is_static()) {
// Converting option names to class names.
$indicators = array();
foreach ($data->indicators as $indicator) {
$indicatorclass = \tool_analytics\output\helper::option_to_class($indicator);
$indicators[] = \core_analytics\manager::get_indicator($indicatorclass);
}
$predictionsprocessor = \tool_analytics\output\helper::option_to_class($data->predictionsprocessor);
} else {
// These fields can not be modified.
$indicators = false;
$predictionsprocessor = false;
}
if (!isset($data->contexts)) {
$data->contexts = null;
}
$model->update($data->enabled, $indicators, $timesplitting, $predictionsprocessor, $data->contexts);
redirect($returnurl);
}
echo $OUTPUT->header();
$modelobj = $model->get_model_obj();
$callable = array('\tool_analytics\output\helper', 'class_to_option');
$modelobj->indicators = array_map($callable, json_decode($modelobj->indicators));
$modelobj->timesplitting = \tool_analytics\output\helper::class_to_option($modelobj->timesplitting);
if ($modelobj->contextids) {
$modelobj->contexts = array_map($callable, json_decode($modelobj->contextids));
}
$modelobj->predictionsprocessor = \tool_analytics\output\helper::class_to_option($modelobj->predictionsprocessor);
$mform->set_data($modelobj);
$mform->display();
break;
case 'evaluate':
require_sesskey();
if ($model->is_static()) {
throw new moodle_exception('errornostaticevaluate', 'tool_analytics');
}
if ($onlycli) {
throw new moodle_exception('erroronlycli', 'tool_analytics');
}
// Web interface is used by people who can not use CLI nor code stuff, always use
// cached stuff as they will change the model through the web interface as well
// which invalidates the previously analysed stuff.
$options = ['reuseprevanalysed' => true];
$mode = optional_param('mode', false, PARAM_ALPHANUM);
if ($mode == 'trainedmodel') {
$options['mode'] = 'trainedmodel';
} else {
// All is the default in core_analytics\model::evaluate() as well.
$timesplitting = optional_param('timesplitting', 'all', PARAM_ALPHANUMEXT);
if ($timesplitting === 'current') {
$options['timesplitting'] = \core_analytics\manager::get_time_splitting($model->get_model_obj()->timesplitting);
} else if ($timesplitting !== 'all') {
$options['timesplitting'] = \core_analytics\manager::get_time_splitting(
\tool_analytics\output\helper::option_to_class($timesplitting)
);
}
}
$results = $model->evaluate($options);
// We reset the theme and the output as some indicators may be using external functions
// which reset $PAGE.
\tool_analytics\output\helper::reset_page();
echo $OUTPUT->header();
$renderer = $PAGE->get_renderer('tool_analytics');
echo $renderer->render_evaluate_results($results, $model->get_analyser()->get_logs());
break;
case 'scheduledanalysis':
require_sesskey();
if ($onlycli) {
throw new moodle_exception('erroronlycli', 'tool_analytics');
}
$trainresults = $model->train();
$trainlogs = $model->get_analyser()->get_logs();
// Looks dumb to get a new instance but better be conservative.
$model = new \core_analytics\model($model->get_model_obj());
if ($model->is_trained()) {
$predictresults = $model->predict();
$predictlogs = $model->get_analyser()->get_logs();
} else {
$predictresults = false;
$predictlogs = array();
}
// We reset the theme and the output as some indicators may be using external functions
// which reset $PAGE.
\tool_analytics\output\helper::reset_page();
echo $OUTPUT->header();
$renderer = $PAGE->get_renderer('tool_analytics');
echo $renderer->render_get_predictions_results($trainresults, $trainlogs, $predictresults, $predictlogs);
break;
case 'log':
echo $OUTPUT->header();
if ($model->is_static()) {
throw new moodle_exception('errornostaticlog', 'tool_analytics');
}
$renderer = $PAGE->get_renderer('tool_analytics');
$modellogstable = new \tool_analytics\output\model_logs('model-' . $model->get_id(), $model);
echo $renderer->render_table($modellogstable);
break;
case 'exportdata':
if ($model->is_static() || !$model->is_trained()) {
throw new moodle_exception('errornoexport', 'tool_analytics');
}
$file = $model->get_training_data();
if (!$file) {
redirect($returnurl, get_string('errortrainingdataexport', 'tool_analytics'),
null, \core\output\notification::NOTIFY_ERROR);
}
$filename = 'training-data.' . $model->get_id() . '.' . time() . '.csv';
send_file($file, $filename, null, 0, false, true);
break;
case 'exportmodel':
$includeweights = optional_param('includeweights', 1, PARAM_INT);
$zipfilename = 'model-' . $model->get_unique_id() . '-' . microtime(false) . '.zip';
$zipfilepath = $model->export_model($zipfilename, $includeweights);
send_temp_file($zipfilepath, $zipfilename);
break;
case 'clear':
require_sesskey();
$model->clear();
redirect($returnurl);
break;
case 'insightsreport':
$contextid = optional_param('contextid', null, PARAM_INT);
echo $OUTPUT->header();
$renderable = new \tool_analytics\output\insights_report($model, $contextid);
$renderer = $PAGE->get_renderer('tool_analytics');
echo $renderer->render($renderable);
break;
case 'invalidanalysables':
echo $OUTPUT->header();
$page = optional_param('page', 0, PARAM_INT);
// No option in the UI to change this, only for url hackers ;).
$perpage = optional_param('perpage', 10, PARAM_INT);
$renderable = new \tool_analytics\output\invalid_analysables($model, $page, $perpage);
$renderer = $PAGE->get_renderer('tool_analytics');
echo $renderer->render($renderable);
break;
}
echo $OUTPUT->footer();
+86
View File
@@ -0,0 +1,86 @@
<?php
// This file is part of Moodle - https://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/>.
/**
* Check and create missing default prediction models.
*
* @package tool_analytics
* @copyright 2019 David Mudrák <david@moodle.com>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(__DIR__ . '/../../../config.php');
require_login();
\core_analytics\manager::check_can_manage_models();
if (!\core_analytics\manager::is_analytics_enabled()) {
$PAGE->set_context(\context_system::instance());
$renderer = $PAGE->get_renderer('tool_analytics');
echo $renderer->render_analytics_disabled();
exit(0);
}
$confirmed = optional_param('confirmed', false, PARAM_BOOL);
$restoreids = optional_param_array('restoreid', [], PARAM_ALPHANUM);
$returnurl = new \moodle_url('/admin/tool/analytics/index.php');
$myurl = new \moodle_url('/admin/tool/analytics/restoredefault.php');
\tool_analytics\output\helper::set_navbar(get_string('restoredefault', 'tool_analytics'), $myurl);
if (data_submitted()) {
require_sesskey();
if (empty($restoreids)) {
$message = get_string('restoredefaultempty', 'tool_analytics');
$type = \core\output\notification::NOTIFY_WARNING;
redirect($myurl, $message, null, $type);
}
$numcreated = 0;
foreach (\core_analytics\manager::load_default_models_for_all_components() as $componentname => $modelslist) {
foreach ($modelslist as $definition) {
if (!in_array(\core_analytics\manager::model_declaration_identifier($definition), $restoreids)) {
// This model has not been selected by the user.
continue;
}
list($target, $indicators) = \core_analytics\manager::get_declared_target_and_indicators_instances($definition);
if (\core_analytics\model::exists($target, $indicators)) {
// This model exists (normally this should not happen as we do not show such models in the UI to select).
continue;
}
\core_analytics\manager::create_declared_model($definition);
$numcreated++;
}
}
$message = get_string('restoredefaultsome', 'tool_analytics', ['count' => $numcreated]);
$type = \core\output\notification::NOTIFY_SUCCESS;
redirect($returnurl, $message, null, $type);
}
$models = \core_analytics\manager::load_default_models_for_all_components();
$ui = new \tool_analytics\output\restorable_models($models);
echo $OUTPUT->header();
echo $PAGE->get_renderer('tool_analytics')->render($ui);
echo $OUTPUT->footer();
+30
View File
@@ -0,0 +1,30 @@
<?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/>.
/**
* Adds settings links to admin tree.
*
* @package tool_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
if (\core_analytics\manager::is_analytics_enabled()) {
$ADMIN->add('analytics', new admin_externalpage('analyticmodels', get_string('analyticmodels', 'tool_analytics'),
"$CFG->wwwroot/$CFG->admin/tool/analytics/index.php", 'moodle/analytics:managemodels'));
}
@@ -0,0 +1,82 @@
{{!
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/>.
}}
{{!
@template tool_analytics/evaluation_options
Evaluation selector.
The purpose of this template is to render the evaluation mode options.
Classes required for JS:
* none
Data attributes required for JS:
* none
Example context (json):
{
"trainedexternally": "1",
"timesplittingmethods": [
{
"id": "ou",
"name": "Quarters"
}, {
"id": "yeah",
"name": "Tenths"
}
]
}
}}
{{#trainedexternally}}
<div class="box mb-4">{{#str}} evaluationmodeinfo, tool_analytics {{/str}}</div>
<div class="custom-control custom-radio">
<input class="custom-control-input" type="radio" name="evaluationmode" id="id-mode-trainedmodel" value="trainedmodel" checked>
<label class="custom-control-label" for="id-mode-trainedmodel">{{#str}} evaluationmodetrainedmodel, tool_analytics {{/str}}</label>
</div>
<div class="custom-control custom-radio">
<input class="custom-control-input" type="radio" name="evaluationmode" id="id-mode-configuration" value="configuration">
<label class="custom-control-label" for="id-mode-configuration">{{#str}} evaluationmodeconfiguration, tool_analytics {{/str}}</label>
</div>
{{/trainedexternally}}
{{! Hidden by default if #trainedexternally as the default option is trainedmodel in this case.}}
<div id="id-evaluation-timesplitting-container" class="mt-3 {{#trainedexternally}}hidden{{/trainedexternally}}">
{{#str}} selecttimesplittingforevaluation, tool_analytics {{/str}}
<div>
<select id="id-evaluation-timesplitting" name="timesplitting" class="custom-select mt-3">
{{#timesplittingmethods}}
<option value="{{id}}">{{text}}</option>
{{/timesplittingmethods}}
</select>
</div>
</div>
{{#js}}
require(['jquery'], function($) {
$("input[name='evaluationmode']:radio").change(function() {
if ($(this).val() == 'configuration') {
$('#id-evaluation-timesplitting-container').show();
} else {
$('#id-evaluation-timesplitting-container').hide();
}
});
});
{{/js}}
@@ -0,0 +1,57 @@
{{!
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/>.
}}
{{!
@template tool_analytics/export_options
Export options.
The purpose of this template is to render the exporting options.
Classes required for JS:
* none
Data attributes required for JS:
* none
Example context (json):
{
}
}}
<div class="custom-control custom-radio">
<input class="custom-control-input" type="radio" name="exportoption" id="id-mode-exportdata" value="exportdata">
<label class="custom-control-label" for="id-mode-exportdata">{{#str}} exporttrainingdata, tool_analytics {{/str}}</label>
</div>
<div class="custom-control custom-radio">
<input class="custom-control-input" type="radio" name="exportoption" id="id-mode-exportmodel" value="exportmodel" checked>
<label class="custom-control-label" for="id-mode-exportmodel">{{#str}} exportmodel, tool_analytics {{/str}}</label>
</div>
<div class="custom-control custom-checkbox ml-5" id="id-includeweights-container">
<input class="custom-control-input" type="checkbox" id="id-includeweights" value="1" checked>
<label class="custom-control-label" for="id-includeweights">{{#str}} exportincludeweights, tool_analytics {{/str}}</label>
</div>
{{#js}}
require(['jquery'], function($) {
$("input[name='exportoption']:radio").change(function() {
if ($(this).val() == 'exportdata') {
$('#id-includeweights-container').hide();
} else {
$('#id-includeweights-container').show();
}
});
});
{{/js}}
@@ -0,0 +1,65 @@
{{!
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/>.
}}
{{!
@template tool_analytics/insights_report
Template for the insights report.
Classes required for JS:
* none
Data attributes required for JS:
* none
Context variables required for this template:
* none
Example context (json):
{
"modelname": "Not engaging courses",
"noactions": {
"message": "Users have not executed any actions on the generated insights.",
"announce": "true"
}
}
}}
<div class="box">
<h3>{{#str}}actionsexecutedbyusersfor, tool_analytics, {{modelname}}{{/str}}</h3>
{{#contextselect}}
<div class="mt-3">
{{> core/single_select }}
</div>
{{/contextselect}}
{{#noactions}}
<div class="mt-3 mb-1">
{{> core/notification_info}}
</div>
{{/noactions}}
{{^noanalysables}}
<div class="row">
<div class="col-xl-6">
{{{separatedchart}}}
</div>
<div class="col-xl-6">
{{{groupedchart}}}
</div>
</div>
{{/noanalysables}}
</div>
@@ -0,0 +1,85 @@
{{!
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/>.
}}
{{!
@template tool_analytics/invalid_analysables
Template for invalid analysables.
Classes required for JS:
* none
Data attributes required for JS:
* none
Context variables required for this template:
* none
Example context (json):
{
"modelname": "Not engaging courses",
"analysables": [
{
"url": "<a href=\"#\">Maths</a>",
"validtraining": "Ongoing course",
"validprediction": "Not enough students activity"
}, {
"url": "<a href=\"#\">Psichology</a>",
"validtraining": "No students",
"validprediction": "No students"
}
]
}
}}
<div class="box">
<h3>{{#str}}modelinvalidanalysables, tool_analytics, {{modelname}}{{/str}}</h3>
<div>{{#str}}invalidanalysablesinfo, tool_analytics{{/str}}</div>
{{#noanalysables}}
<div class="mt-2 mb-1">
{{> core/notification_info}}
</div>
{{/noanalysables}}
{{^noanalysables}}
<div class="mt-2 mb-1">
<span>{{#prev}}{{> core/single_button}}{{/prev}}</span>
<span>{{#next}}{{> core/single_button}}{{/next}}</span>
</div>
<table class="generaltable fullwidth">
<caption class="accesshide">{{#str}}invalidanalysablestable, tool_analytics{{/str}}</caption>
<thead>
<tr>
<th scope="col">{{#str}}name{{/str}}</th>
<th scope="col">{{#str}}invalidtraining, tool_analytics{{/str}}</th>
<th scope="col">{{#str}}invalidprediction, tool_analytics{{/str}}</th>
</tr>
</thead>
<tbody>
{{#analysables}}
<tr>
<td>{{{url}}}</td>
<td>{{validtraining}}</td>
<td>{{validprediction}}</td>
</tr>
{{/analysables}}
</tbody>
</table>
<div class="mt-1 mb-2">
<span>{{#prev}}{{> core/single_button}}{{/prev}}</span>
<span>{{#next}}{{> core/single_button}}{{/next}}</span>
</div>
{{/noanalysables}}
</div>
@@ -0,0 +1,260 @@
{{!
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/>.
}}
{{!
@template tool_analytics/models_list
Template for models list.
Classes required for JS:
* The list od models wrapped within a id="predictionmodelslist" element.
Data attributes required for JS:
* [data-widget="toggle"] indicates the clickable element for expanding/collapsing
the list of indicators used by the given model.
* [data-model-name="..."] should be provided by an element wrapping the model's actions menu
and contain the plain text name of the model.
Context variables required for this template:
* models: array - list of models to display
- id: int - model unique identifier
- modelname: string - name of the model
- name: object - data for the inplace editable element template
- target: string - name of the target associated with the model
- targetclass: string - fully qualified name of the target class
- targethelp: object - data for the help tooltip template
- enabled: bool - is the model enabled
- indicatorsnum: int - number of indicators
- indicators: array - list of indicators used by the model
+ name: string - name of the indicator
+ help: object - data for the help tooltip template
- insights: object - data for the single select template
- noinsights: string - text to display instead of insights
* warnings: array - list of data for notification warning template
* infos: array - list of data for notification info template
* createmodelurl: string - URL to create a new model
* importmodelurl: string - URL to import a model
Example context (json):
{
"models": [
{
"id": 11,
"modelname": "Prevent devs at risk",
"name": {
"component": "local_analyticsdemo",
"itemtype": "modelname",
"itemid": 42,
"displayvalue": "Prevent devs at risk",
"value": ""
},
"target": "Prevent devs at risk",
"targetclass": "\\local_analyticsdemo\\analytics\\target\\dev_risk",
"targethelp": {
"title": "Help with Prevent devs at risk",
"text": "This target blah blah ...",
"url": "http://example.org/help",
"linktext": "",
"icon": {
"extraclasses": "iconhelp",
"attributes": [
{"name": "src", "value": "../../../pix/help.svg"},
{"name": "alt", "value": "Help icon"}
]
}
},
"enabled": 1,
"indicatorsnum": 2,
"indicators": [
{
"name": "Indicator 1",
"help": {
"text": "This indicator blah blah ...",
"title": "Help with Indicator 1",
"url": "http://example.org/help",
"linktext": "",
"icon": {
"extraclasses": "iconhelp",
"attributes": [
{"name": "src", "value": "../../../pix/help.svg"},
{"name": "alt", "value": "Help icon"}
]
}
}
},
{
"name": "Indicator 2",
"help": {
"text": "This indicator blah blah ...",
"title": "Help with Indicator 2",
"url": "http://example.org/help",
"linktext": "",
"icon": {
"extraclasses": "iconhelp",
"attributes": [
{"name": "src", "value": "../../../pix/help.svg"},
{"name": "alt", "value": "Help icon"}
]
}
}
}
],
"timesplitting": "Quarters",
"timesplittinghelp": {
"text": "This time splitting methof blah blah ...",
"title": "Help with Quarters",
"url": "http://example.org/help",
"linktext": "",
"icon": {
"extraclasses": "iconhelp",
"attributes": [
{"name": "src", "value": "../../../pix/help.svg"},
{"name": "alt", "value": "Help icon"}
]
}
},
"noinsights": "No insights available yet"
}
],
"warnings": [
{
"message": "Be ware, this is just an example!"
}
],
"createmodelurl": "#",
"importmodelurl": "#"
}
}}
{{#warnings}}
{{> core/notification_warning}}
{{/warnings}}
{{#infos}}
{{> core/notification_info}}
{{/infos}}
<div class="box">
<div class="top-nav d-flex">
{{#newmodelmenu}}
{{>core/action_menu}}
{{/newmodelmenu}}
</div>
<table id="predictionmodelslist" class="generaltable fullwidth">
<caption>{{#str}}analyticmodels, tool_analytics{{/str}}</caption>
<thead>
<tr>
<th scope="col">{{#str}}modelname, tool_analytics{{/str}}</th>
<th scope="col">{{#str}}enabled, tool_analytics{{/str}}</th>
<th scope="col">{{#str}}indicators, tool_analytics{{/str}}</th>
<th scope="col">{{#str}}modeltimesplitting, tool_analytics{{/str}}</th>
<th scope="col">{{#str}}insights, tool_analytics{{/str}}</th>
<th scope="col">{{#str}}actions{{/str}}</th>
</tr>
</thead>
<tbody>
{{#models}}
<tr data-model-name="{{modelname}}">
<td>
{{#name}}
<span class="model-name">{{>core/inplace_editable}}</span>
{{/name}}
<div>
<small class="target-class">{{targetclass}}</small>
{{#targethelp}}
{{>core/help_icon}}
{{/targethelp}}
</div>
</td>
<td>
{{#enabled}}
{{#pix}}i/checked, core, {{#str}}yes{{/str}}{{/pix}}
{{/enabled}}
{{^enabled}}
{{#str}}no{{/str}}
{{/enabled}}
</td>
<td>
<a data-widget="toggle"
title="{{#str}} clicktohideshow {{/str}}"
aria-expanded="false"
aria-controls="indicators-{{id}}"
role="button"
href="">
{{#str}} indicatorsnum, tool_analytics, {{indicatorsnum}} {{/str}}
</a>
<ul class="hidden" id="indicators-{{id}}">
{{#indicators}}
<li>
{{name}}
{{#help}}
{{>core/help_icon}}
{{/help}}
</li>
{{/indicators}}
</ul>
</td>
<td>
{{#timesplitting}}
{{timesplitting}}
{{#timesplittinghelp}}
{{>core/help_icon}}
{{/timesplittinghelp}}
{{/timesplitting}}
{{^timesplitting}}
{{#str}}notdefined, tool_analytics{{/str}}
{{#timesplittinghelp}}
{{>core/help_icon}}
{{/timesplittinghelp}}
{{/timesplitting}}
</td>
<td>
{{! models_list renderer is responsible of sending one or the other}}
{{#insights}}
{{> core/single_select }}
{{/insights}}
{{#noinsights}}
{{.}}
{{/noinsights}}
</td>
<td>
{{#actions}}
{{> core/action_menu}}
{{/actions}}
</td>
</tr>
{{/models}}
</tbody>
</table>
</div>
{{#js}}
require(['jquery'], function($) {
// Toggle the visibility of the indicators list.
$('#predictionmodelslist').on('click', '[data-widget="toggle"]', function(e) {
e.preventDefault();
var toggle = $(e.currentTarget);
var listid = toggle.attr('aria-controls');
$(document.getElementById(listid)).toggle();
if (toggle.attr('aria-expanded') == 'false') {
toggle.attr('aria-expanded', 'true');
} else {
toggle.attr('aria-expanded', 'false');
}
});
});
{{/js}}
@@ -0,0 +1,225 @@
{{!
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/>.
}}
{{!
@template tool_analytics/restorable_models
Displays the list of missing prediction models that can be restored.
Classes required for JS:
* The list should be wrapped within a id="restorablemodelslist" element.
Data attributes required for JS:
* [data-widget="toggle"] indicates the clickable element for expanding/collapsing
the list of indicators used by the given model.
* [data-select] indicates a clickable element used for selecting multiple checkboxes.
* [data-component] should be set for checkboxes that select the particular model.
Context variables required for this template:
* hasdata: boolean - do we have data to display
* submiturl: string - URL where the form should be submitted
* backurl: string - URL where the user should be sent without making any changes
* sesskey: string
* components: array - list of components to display
- name: string - human readable name of the component
- component: string - frankenstyle name of the component
- models: array - list of restorable models provided by the component
+ defid: string - model definition identifier
+ targetname: string - human readable name of the target
+ targetclass: string - fully qualified classname of the target
+ indicatorsnum: int - number of indicators
+ indicators: array - list of indicators
~ name: string - human readable name of the indicator
~ classname: string - fully qualified classname of the indicator
Example context (json):
{
"hasdata": true,
"submiturl": "https://example.com/moodle/admin/tool/analytics/restoredefault.php",
"backurl": "https://example.com/moodle/admin/tool/analytics/index.php",
"sesskey": "abcdefg123456",
"components": [
{
"name": "Core",
"component": "core",
"models": [
{
"defid": "id24680aceg",
"targetname": "Courses at risk of not starting",
"targetclass": "\\core\\analytics\\target\\no_teaching",
"indicatorsnum": 2,
"indicators": [
{
"name": "There are no teachers",
"classname": "\\core\\analytics\\indicator\\no_teacher"
},
{
"name": "There are no students",
"classname": "\\core\\analytics\\indicator\\no_students"
}
]
},
{
"defid": "id13579bdfi",
"targetname": "Students at risk of dropping out",
"targetclass": "\\core\\analytics\\target\\course_dropout",
"indicatorsnum": 1,
"indicators": [
{
"name": "Read actions amount",
"classname": "\\core\\analytics\\indicator\\read_actions"
}
]
}
]
},
{
"name": "Custom analytics plugin",
"component": "tool_customanalytics",
"models": [
{
"defid": "id566dsgffg655",
"targetname": "Cheater",
"targetclass": "\\tool_customanalytics\\analytics\\target\\cheater",
"indicatorsnum": 1,
"indicators": [
{
"name": "Copy-pasted submissions",
"classname": "\\tool_customanalytics\\analytics\\indicator\\copy_paster_submissions"
}
]
}
]
}
]
}
}}
<div class="box">
{{^hasdata}}
<p>{{#str}} restoredefaultnone, tool_analytics {{/str}}</p>
<div><a href="{{backurl}}" class="btn btn-secondary">{{#str}} back {{/str}}</a></div>
{{/hasdata}}
{{#hasdata}}
<p>{{#str}} restoredefaultinfo, tool_analytics {{/str}}</p>
<form method="post" action="{{submiturl}}">
<table id="restorablemodelslist" class="generaltable fullwidth">
<colgroup>
<col width="10%">
<col width="45%">
<col width="45%">
</colgroup>
<thead>
<tr>
<th scope="col"><a href="" data-select="*">{{#str}} selectall {{/str}}</a></th>
<th scope="col">{{#str}} target, tool_analytics {{/str}}</th>
<th scope="col">{{#str}} indicators, tool_analytics {{/str}}</th>
</tr>
</thead>
<tbody>
{{#components}}
<tr>
<th scope="rowgroup" colspan="3">
<span class="component-name">
<a href=""
title="{{#str}} componentselect, tool_analytics, {{name}} {{/str}}"
data-select="{{component}}">
{{name}}
</a>
</span>
<div><small class="component-frankenstyle">{{component}}</small></div>
</th>
</tr>
{{#models}}
<tr>
<td>
<input data-component="{{component}}" type="checkbox" name="restoreid[]" value="{{defid}}">
</td>
<td>
<span class="target-name">{{targetname}}</span>
{{#targethelp}}
{{>core/help_icon}}
{{/targethelp}}
<div><small class="target-class">{{targetclass}}</small></div>
</td>
<td>
<a data-widget="toggle"
title="{{#str}} clicktohideshow {{/str}}"
aria-expanded="false"
aria-controls="indicators-{{defid}}"
role="button"
href="">
{{#str}} indicatorsnum, tool_analytics, {{indicatorsnum}} {{/str}}
</a>
<ul class="hidden listunstyled" id="indicators-{{defid}}">
{{#indicators}}
<li>
{{name}}
{{#indicatorhelp}}
{{>core/help_icon}}
{{/indicatorhelp}}
<div><small>{{classname}}</small></div>
</li>
{{/indicators}}
</ul>
</td>
</tr>
{{/models}}
{{/components}}
</tbody>
</table>
<div>
<input class="btn btn-primary" type="submit" value="{{#str}} restoredefaultsubmit, tool_analytics {{/str}}">
<input class="btn btn-secondary" type="reset" value="{{#str}} componentselectnone, tool_analytics {{/str}}">
<a href="{{backurl}}" class="btn btn-secondary">{{#str}} back {{/str}}</a>
<input type="hidden" name="sesskey" value="{{sesskey}}">
</div>
</form>
{{/hasdata}}
</div>
{{#js}}
require(['jquery'], function($) {
// Toggle the visibility of the indicators list.
$('#restorablemodelslist').on('click', '[data-widget="toggle"]', function(e) {
e.preventDefault();
var toggle = $(e.currentTarget);
var listid = toggle.attr('aria-controls');
$(document.getElementById(listid)).toggle();
if (toggle.attr('aria-expanded') == 'false') {
toggle.attr('aria-expanded', 'true');
} else {
toggle.attr('aria-expanded', 'false');
}
});
// Selecting all / all in component checkboxes.
$('#restorablemodelslist').on('click', '[data-select]', function(e) {
e.preventDefault();
var handler = $(e.currentTarget);
var component = handler.attr('data-select');
if (component == '*') {
$('input[data-component]').prop('checked', true);
} else {
$('input[data-component="' + component + '"]').prop('checked', true);
}
});
});
{{/js}}
@@ -0,0 +1,103 @@
@tool @tool_analytics
Feature: Restoring default models
In order to get prediction models into their initial state
As a manager
I need to be able to restore deleted default models
Background:
Given the following "users" exist:
| username | firstname | lastname | email |
| manager | Max | Manager | man@example.com |
And the following "role assigns" exist:
| user | role | contextlevel | reference |
| manager | manager | System | |
Scenario: Restore a single deleted default model
Given I log in as "manager"
And I navigate to "Analytics > Analytics models" in site administration
# Delete 'Courses at risk of not starting' model.
And I click on "Delete" "link" in the "Courses at risk of not starting" "table_row"
And I should see "Analytics models"
And I should not see "Courses at risk of not starting"
# Delete 'Students at risk of dropping out' model.
And I click on "Delete" "link" in the "Students at risk of dropping out" "table_row"
And I should see "Analytics models"
And I should not see "Students at risk of dropping out"
# Go to the page for restoring deleted models.
When I click on "Restore default models" "link"
And I should see "Courses at risk of not starting"
And I should see "Students at risk of dropping out"
# Select and restore the 'Courses at risk of not starting' model.
And I set the field with xpath "//tr[contains(normalize-space(.), 'Courses at risk of not starting')]//input[@type='checkbox']" to "1"
And I click on "Restore selected" "button"
Then I should see "Succesfully re-created 1 new model(s)."
And I should see "Analytics models"
And I should see "Courses at risk of not starting"
And I should not see "Students at risk of dropping out"
Scenario: Restore multiple deleted default models at once
Given I log in as "manager"
And I navigate to "Analytics > Analytics models" in site administration
# Delete 'Courses at risk of not starting' model.
And I click on "Delete" "link" in the "Courses at risk of not starting" "table_row"
And I should see "Analytics models"
And I should not see "Courses at risk of not starting"
# Delete 'Students at risk of dropping out' model.
And I click on "Delete" "link" in the "Students at risk of dropping out" "table_row"
And I should see "Analytics models"
And I should not see "Students at risk of dropping out"
# Go to the page for restoring deleted models.
When I click on "Restore default models" "link"
And I should see "Courses at risk of not starting"
And I should see "Students at risk of dropping out"
# Select and restore both models.
And I set the field with xpath "//tr[contains(normalize-space(.), 'Courses at risk of not starting')]//input[@type='checkbox']" to "1"
And I set the field with xpath "//tr[contains(normalize-space(.), 'Students at risk of dropping out')]//input[@type='checkbox']" to "1"
And I click on "Restore selected" "button"
Then I should see "Succesfully re-created 2 new model(s)."
And I should see "Analytics models"
And I should see "Courses at risk of not starting"
And I should see "Students at risk of dropping out"
Scenario: Going to the restore page while no models can be restored
Given I log in as "manager"
And I navigate to "Analytics > Analytics models" in site administration
And I should see "Analytics models"
And I should see "Courses at risk of not starting"
When I click on "Restore default models" "link"
Then I should see "All default models provided by core and installed plugins have been created. No new models were found; there is nothing to restore."
And I click on "Back" "link"
And I should see "Analytics models"
@javascript
Scenario: User can select and restore all missing models
Given I log in as "manager"
And I navigate to "Analytics > Analytics models" in site administration
# Delete 'Courses at risk of not starting' model.
And I click on "Actions" "link" in the "Courses at risk of not starting" "table_row"
And I click on "Delete" "link" in the "Courses at risk of not starting" "table_row"
And I click on "Delete" "button" in the "Delete" "dialogue"
And I should see "Analytics models"
And I should not see "Courses at risk of not starting"
# Delete 'Students at risk of dropping out' model.
And I click on "Actions" "link" in the "Students at risk of dropping out" "table_row"
And I click on "Delete" "link" in the "Students at risk of dropping out" "table_row"
And I click on "Delete" "button" in the "Delete" "dialogue"
And I should see "Analytics models"
And I should not see "Courses at risk of not starting"
And I should not see "Students at risk of dropping out"
# Go to the page for restoring deleted models.
And I click on "New model" "link"
And I click on "Restore default models" "link"
And I should see "Courses at risk of not starting"
And I should see "Students at risk of dropping out"
# Attempt to submit the form without selecting any model.
And I click on "Restore selected" "button"
And I should see "Please select models to be restored."
# Select all models.
When I click on "Select all" "link"
And I click on "Restore selected" "button"
Then I should see "Succesfully re-created 2 new model(s)."
And I should see "Analytics models"
And I should see "Courses at risk of not starting"
And I should see "Students at risk of dropping out"
@@ -0,0 +1,37 @@
@core @tool @tool_analytics @javascript
Feature: Verify the breadcrumbs in analytics site administration pages
Whenever I navigate to analytics page in site administration to create, import, edit or restore models
As an admin
The breadcrumbs should be visible
Background:
Given I log in as "admin"
Scenario: Verify the breadcrumbs in analytics models page by visiting the create model, import model, restore model and edit page
Given I navigate to "Analytics > Analytics models" in site administration
And I click on "New model" "link"
When I click on "Create model" "link"
Then "Create model" "text" should exist in the ".breadcrumb" "css_element"
And "Analytics model" "link" should exist in the ".breadcrumb" "css_element"
And "Analytics" "link" should exist in the ".breadcrumb" "css_element"
And I press "Cancel"
# Testing import model page
And I click on "New model" "link"
And I click on "Import model" "link"
And "Import model" "text" should exist in the ".breadcrumb" "css_element"
And "Analytics model" "link" should exist in the ".breadcrumb" "css_element"
And "Analytics" "link" should exist in the ".breadcrumb" "css_element"
And I press "Cancel"
# Testing restore defaults
And I click on "New model" "link"
And I click on "Restore default models" "link"
And "Restore default models" "text" should exist in the ".breadcrumb" "css_element"
And "Analytics model" "link" should exist in the ".breadcrumb" "css_element"
And "Analytics" "link" should exist in the ".breadcrumb" "css_element"
And I click on "Back" "link"
# Testing edit page
And I click on "Actions" "link"
And I click on "Edit" "link"
And "Edit \"Courses at risk of not starting\" model" "text" should exist in the ".breadcrumb" "css_element"
And "Analytics model" "link" should exist in the ".breadcrumb" "css_element"
And "Analytics" "link" should exist in the ".breadcrumb" "css_element"
+83
View File
@@ -0,0 +1,83 @@
<?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/>.
/**
* Tool analytics external functions tests.
*
* @package tool_analytics
* @category external
* @copyright 2019 David Monllaó {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 3.8
*/
namespace tool_analytics\external;
use externallib_advanced_testcase;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once($CFG->dirroot . '/webservice/tests/helpers.php');
require_once($CFG->dirroot . '/analytics/tests/fixtures/test_indicator_max.php');
require_once($CFG->dirroot . '/analytics/tests/fixtures/test_target_course_level_shortname.php');
/**
* Tool analytics external functions tests
*
* @package tool_analytics
* @category external
* @copyright 2019 David Monllaó {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
* @since Moodle 3.8
*/
class external_test extends externallib_advanced_testcase {
/**
* test_potential_contexts description
*/
public function test_potential_contexts(): void {
$this->resetAfterTest();
$this->setAdminUser();
// Include the all context levels so the misc. category get included.
$this->assertCount(1, \tool_analytics\external::potential_contexts());
// The frontpage is not included.
$this->assertCount(0, \tool_analytics\external::potential_contexts('PHPUnit'));
$target = \core_analytics\manager::get_target('test_target_course_level_shortname');
$indicators = ['test_indicator_max' => \core_analytics\manager::get_indicator('test_indicator_max')];
$model = \core_analytics\model::create($target, $indicators);
$this->assertCount(1, \tool_analytics\external::potential_contexts(null, $model->get_id()));
}
/**
* test_potential_contexts description
*/
public function test_potential_contexts_no_manager(): void {
$this->resetAfterTest();
$user = $this->getDataGenerator()->create_user();
$this->setUser($user);
$this->expectException(\required_capability_exception::class);
$this->assertCount(2, \tool_analytics\external::potential_contexts());
}
}
+29
View File
@@ -0,0 +1,29 @@
<?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/>.
/**
* Version details.
*
* @package tool_analytics
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com/}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2024042200; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2024041600; // Requires this Moodle version.
$plugin->component = 'tool_analytics'; // Full name of the plugin (used for diagnostics).