Merge branch 'MDL-60022_master' of git://github.com/dmonllao/moodle

This commit is contained in:
Andrew Nicols 2017-10-18 12:36:00 +08:00
commit f150071901
6 changed files with 171 additions and 33 deletions

View file

@ -0,0 +1 @@
define(["jquery","core/str","core/log","core/notification","core/modal_factory","core/modal_events"],function(a,b,c,d,e,f){var g={clear:{title:{key:"clearpredictions",component:"tool_analytics"},body:{key:"clearmodelpredictions",component:"tool_analytics"}}},h=function(b){return a(b.closest("tr")[0]).find("span.target-name").text()};return{confirmAction:function(i,j){a('[data-action-id="'+i+'"]').on("click",function(i){i.preventDefault();var k=a(i.currentTarget);if("undefined"==typeof g[j])return void c.error('Action "'+j+'" is not allowed.');var l=[g[j].title,g[j].body];l[1].param=h(k);var m=b.get_strings(l),n=e.create({type:e.types.SAVE_CANCEL});a.when(m,n).then(function(a,b){return b.setTitle(a[0]),b.setBody(a[1]),b.setSaveButtonText(a[0]),b.getRoot().on(f.save,function(){window.location.href=k.attr("href")}),b.show(),b}).fail(d.exception)})}}});

View file

@ -0,0 +1,94 @@
// 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_factory', 'core/modal_events'],
function($, Str, log, Notification, ModalFactory, ModalEvents) {
/**
* List of actions that require confirmation and confirmation message.
*/
var actionsList = {
clear: {
title: {
key: 'clearpredictions',
component: 'tool_analytics'
}, body: {
key: 'clearmodelpredictions',
component: 'tool_analytics'
}
}
};
/**
* Returns the model name.
*
* @param {Object} actionItem The action item DOM node.
* @return {String}
*/
var getModelName = function(actionItem) {
return $(actionItem.closest('tr')[0]).find('span.target-name').text();
};
/** @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 = ModalFactory.create({type: ModalFactory.types.SAVE_CANCEL});
$.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);
});
}
};
});

View file

@ -59,6 +59,7 @@ class models_list implements \renderable, \templatable {
* @return \stdClass * @return \stdClass
*/ */
public function export_for_template(\renderer_base $output) { public function export_for_template(\renderer_base $output) {
global $PAGE;
$data = new \stdClass(); $data = new \stdClass();
@ -120,11 +121,13 @@ class models_list implements \renderable, \templatable {
} }
} }
// Has this model generated predictions?.
$predictioncontexts = $model->get_predictions_contexts();
// Model predictions list. // Model predictions list.
if (!$model->is_enabled()) { if (!$model->is_enabled()) {
$modeldata->noinsights = get_string('disabledmodel', 'analytics'); $modeldata->noinsights = get_string('disabledmodel', 'analytics');
} else if ($model->uses_insights()) { } else if ($model->uses_insights()) {
$predictioncontexts = $model->get_predictions_contexts();
if ($predictioncontexts) { if ($predictioncontexts) {
foreach ($predictioncontexts as $contextid => $unused) { foreach ($predictioncontexts as $contextid => $unused) {
@ -166,9 +169,39 @@ class models_list implements \renderable, \templatable {
$actionsmenu->set_owner_selector('model-actions-' . $model->get_id()); $actionsmenu->set_owner_selector('model-actions-' . $model->get_id());
$actionsmenu->set_alignment(\action_menu::TL, \action_menu::BL); $actionsmenu->set_alignment(\action_menu::TL, \action_menu::BL);
$urlparams = ['id' => $model->get_id(), 'sesskey' => sesskey()];
// Get predictions.
if (!$onlycli && $modeldata->enabled && !empty($modeldata->timesplitting)) {
$urlparams['action'] = 'getpredictions';
$url = new \moodle_url('model.php', $urlparams);
$icon = new \action_menu_link_secondary($url, new \pix_icon('i/notifications',
get_string('getpredictions', 'tool_analytics')), get_string('getpredictions', 'tool_analytics'));
$actionsmenu->add($icon);
}
// Evaluate machine-learning-based models.
if (!$onlycli && $model->get_indicators() && !$model->is_static()) {
$urlparams['action'] = 'evaluate';
$url = new \moodle_url('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'));
$actionsmenu->add($icon);
}
// Machine-learning-based models evaluation log.
if (!$model->is_static()) {
$urlparams['action'] = 'log';
$url = new \moodle_url('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. // Edit model.
if (!$model->is_static()) { if (!$model->is_static()) {
$url = new \moodle_url('model.php', array('action' => 'edit', 'id' => $model->get_id())); $urlparams['action'] = 'edit';
$url = new \moodle_url('model.php', $urlparams);
$icon = new \action_menu_link_secondary($url, new \pix_icon('t/edit', get_string('edit')), get_string('edit')); $icon = new \action_menu_link_secondary($url, new \pix_icon('t/edit', get_string('edit')), get_string('edit'));
$actionsmenu->add($icon); $actionsmenu->add($icon);
} }
@ -183,42 +216,32 @@ class models_list implements \renderable, \templatable {
$text = get_string('enable'); $text = get_string('enable');
$icontype = 'i/checked'; $icontype = 'i/checked';
} }
$url = new \moodle_url('model.php', array('action' => $action, 'id' => $model->get_id())); $urlparams['action'] = $action;
$url = new \moodle_url('model.php', $urlparams);
$icon = new \action_menu_link_secondary($url, new \pix_icon($icontype, $text), $text); $icon = new \action_menu_link_secondary($url, new \pix_icon($icontype, $text), $text);
$actionsmenu->add($icon); $actionsmenu->add($icon);
// Evaluate machine-learning-based models.
if (!$onlycli && $model->get_indicators() && !$model->is_static()) {
$url = new \moodle_url('model.php', array('action' => 'evaluate', 'id' => $model->get_id()));
$icon = new \action_menu_link_secondary($url, new \pix_icon('i/calc', get_string('evaluate', 'tool_analytics')),
get_string('evaluate', 'tool_analytics'));
$actionsmenu->add($icon);
}
// Get predictions.
if (!$onlycli && $modeldata->enabled && !empty($modeldata->timesplitting)) {
$url = new \moodle_url('model.php', array('action' => 'getpredictions', 'id' => $model->get_id()));
$icon = new \action_menu_link_secondary($url, new \pix_icon('i/notifications',
get_string('getpredictions', 'tool_analytics')), get_string('getpredictions', 'tool_analytics'));
$actionsmenu->add($icon);
}
// Machine-learning-based models evaluation log.
if (!$model->is_static()) {
$url = new \moodle_url('model.php', array('action' => 'log', 'id' => $model->get_id()));
$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);
}
// Export training data. // Export training data.
if (!$model->is_static() && $model->is_trained()) { if (!$model->is_static() && $model->is_trained()) {
$url = new \moodle_url('model.php', array('action' => 'export', 'id' => $model->get_id())); $urlparams['action'] = 'export';
$url = new \moodle_url('model.php', $urlparams);
$icon = new \action_menu_link_secondary($url, new \pix_icon('i/export', $icon = new \action_menu_link_secondary($url, new \pix_icon('i/export',
get_string('exporttrainingdata', 'tool_analytics')), get_string('export', 'tool_analytics')); get_string('exporttrainingdata', 'tool_analytics')), get_string('export', 'tool_analytics'));
$actionsmenu->add($icon); $actionsmenu->add($icon);
} }
// Clear model.
if (!empty($predictioncontexts)) {
$actionid = 'clear-' . $model->get_id();
$PAGE->requires->js_call_amd('tool_analytics/model', 'confirmAction', [$actionid, 'clear']);
$urlparams['action'] = 'clear';
$url = new \moodle_url('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);
}
$modeldata->actions = $actionsmenu->export_for_template($output); $modeldata->actions = $actionsmenu->export_for_template($output);
$data->models[] = $modeldata; $data->models[] = $modeldata;

View file

@ -29,6 +29,8 @@ $string['analyticmodels'] = 'Analytic models';
$string['bettercli'] = 'Evaluating models and generating predictions may involve heavy processing. It is recommended to run these actions from the command line.'; $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['cantguessstartdate'] = 'Can\'t guess the start date';
$string['cantguessenddate'] = 'Can\'t guess the end date'; $string['cantguessenddate'] = 'Can\'t guess the end date';
$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 a time-splitting method by its ID. Note that you can also enable it later using the web interface (\'none\' to exit).'; $string['clienablemodel'] = 'You can enable the model by selecting a time-splitting method 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['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['editmodel'] = 'Edit "{$a}" model'; $string['editmodel'] = 'Edit "{$a}" model';
@ -59,7 +61,6 @@ $string['info'] = 'Info';
$string['insights'] = 'Insights'; $string['insights'] = 'Insights';
$string['loginfo'] = 'Log extra info'; $string['loginfo'] = 'Log extra info';
$string['modelresults'] = '{$a} results'; $string['modelresults'] = '{$a} results';
$string['modelslist'] = 'Models list';
$string['modeltimesplitting'] = 'Time splitting'; $string['modeltimesplitting'] = 'Time splitting';
$string['nodatatoevaluate'] = 'There is no data to evaluate the model'; $string['nodatatoevaluate'] = 'There is no data to evaluate the model';
$string['nodatatopredict'] = 'No new elements to get predictions for'; $string['nodatatopredict'] = 'No new elements to get predictions for';

View file

@ -60,7 +60,9 @@ switch ($action) {
case 'export': case 'export':
$title = get_string('export', 'tool_analytics'); $title = get_string('export', 'tool_analytics');
break; break;
case 'clear':
$title = get_string('clearpredictions', 'tool_analytics');
break;
default: default:
throw new moodle_exception('errorunknownaction', 'analytics'); throw new moodle_exception('errorunknownaction', 'analytics');
} }
@ -80,14 +82,21 @@ if ($onlycli === false) {
switch ($action) { switch ($action) {
case 'enable': case 'enable':
confirm_sesskey();
$model->enable(); $model->enable();
redirect(new \moodle_url('/admin/tool/analytics/index.php')); redirect(new \moodle_url('/admin/tool/analytics/index.php'));
break;
case 'disable': case 'disable':
confirm_sesskey();
$model->update(0, false, false); $model->update(0, false, false);
redirect(new \moodle_url('/admin/tool/analytics/index.php')); redirect(new \moodle_url('/admin/tool/analytics/index.php'));
break;
case 'edit': case 'edit':
confirm_sesskey();
if ($model->is_static()) { if ($model->is_static()) {
echo $OUTPUT->header(); echo $OUTPUT->header();
@ -106,7 +115,6 @@ switch ($action) {
redirect(new \moodle_url('/admin/tool/analytics/index.php')); redirect(new \moodle_url('/admin/tool/analytics/index.php'));
} else if ($data = $mform->get_data()) { } else if ($data = $mform->get_data()) {
confirm_sesskey();
// Converting option names to class names. // Converting option names to class names.
$indicators = array(); $indicators = array();
@ -131,6 +139,8 @@ switch ($action) {
break; break;
case 'evaluate': case 'evaluate':
confirm_sesskey();
echo $OUTPUT->header(); echo $OUTPUT->header();
if ($model->is_static()) { if ($model->is_static()) {
@ -150,6 +160,8 @@ switch ($action) {
break; break;
case 'getpredictions': case 'getpredictions':
confirm_sesskey();
echo $OUTPUT->header(); echo $OUTPUT->header();
if ($onlycli) { if ($onlycli) {
@ -200,6 +212,13 @@ switch ($action) {
$filename = 'training-data.' . $model->get_id() . '.' . time() . '.csv'; $filename = 'training-data.' . $model->get_id() . '.' . time() . '.csv';
send_file($file, $filename, null, 0, false, true); send_file($file, $filename, null, 0, false, true);
break; break;
case 'clear':
confirm_sesskey();
$model->clear();
redirect(new \moodle_url('/admin/tool/analytics/index.php'));
break;
} }
echo $OUTPUT->footer(); echo $OUTPUT->footer();

View file

@ -111,7 +111,7 @@
<div class="box"> <div class="box">
<table class="generaltable fullwidth"> <table class="generaltable fullwidth">
<caption>{{#str}}modelslist, tool_analytics{{/str}}</caption> <caption>{{#str}}analyticmodels, tool_analytics{{/str}}</caption>
<thead> <thead>
<tr> <tr>
<th scope="col">{{#str}}target, tool_analytics{{/str}}</th> <th scope="col">{{#str}}target, tool_analytics{{/str}}</th>
@ -126,7 +126,7 @@
{{#models}} {{#models}}
<tr> <tr>
<td> <td>
{{target}} <span class="target-name">{{target}}</span>
{{#targethelp}} {{#targethelp}}
{{>core/help_icon}} {{>core/help_icon}}
{{/targethelp}} {{/targethelp}}