From 8816022df741cbc76cc173ff599c726836fcc77b Mon Sep 17 00:00:00 2001 From: David Monllao Date: Tue, 12 Sep 2017 16:26:49 +0200 Subject: [PATCH] MDL-60022 analytics: Clear model predictions in UI --- admin/tool/analytics/amd/build/model.min.js | 1 + admin/tool/analytics/amd/src/model.js | 94 +++++++++++++++++++ .../analytics/classes/output/models_list.php | 79 ++++++++++------ .../tool/analytics/lang/en/tool_analytics.php | 3 +- admin/tool/analytics/model.php | 23 ++++- .../analytics/templates/models_list.mustache | 4 +- 6 files changed, 171 insertions(+), 33 deletions(-) create mode 100644 admin/tool/analytics/amd/build/model.min.js create mode 100644 admin/tool/analytics/amd/src/model.js diff --git a/admin/tool/analytics/amd/build/model.min.js b/admin/tool/analytics/amd/build/model.min.js new file mode 100644 index 00000000000..e9e2e3c4dec --- /dev/null +++ b/admin/tool/analytics/amd/build/model.min.js @@ -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)})}}}); \ No newline at end of file diff --git a/admin/tool/analytics/amd/src/model.js b/admin/tool/analytics/amd/src/model.js new file mode 100644 index 00000000000..891343143ff --- /dev/null +++ b/admin/tool/analytics/amd/src/model.js @@ -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 . + +/** + * 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); + }); + } + }; +}); diff --git a/admin/tool/analytics/classes/output/models_list.php b/admin/tool/analytics/classes/output/models_list.php index b05e102099c..cc499ef5345 100644 --- a/admin/tool/analytics/classes/output/models_list.php +++ b/admin/tool/analytics/classes/output/models_list.php @@ -59,6 +59,7 @@ class models_list implements \renderable, \templatable { * @return \stdClass */ public function export_for_template(\renderer_base $output) { + global $PAGE; $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. if (!$model->is_enabled()) { $modeldata->noinsights = get_string('disabledmodel', 'analytics'); } else if ($model->uses_insights()) { - $predictioncontexts = $model->get_predictions_contexts(); if ($predictioncontexts) { 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_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. 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')); $actionsmenu->add($icon); } @@ -183,42 +216,32 @@ class models_list implements \renderable, \templatable { $text = get_string('enable'); $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); $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. 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', get_string('exporttrainingdata', 'tool_analytics')), get_string('export', 'tool_analytics')); $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); $data->models[] = $modeldata; diff --git a/admin/tool/analytics/lang/en/tool_analytics.php b/admin/tool/analytics/lang/en/tool_analytics.php index 3cdb7232e9e..e1362fdca97 100644 --- a/admin/tool/analytics/lang/en/tool_analytics.php +++ b/admin/tool/analytics/lang/en/tool_analytics.php @@ -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['cantguessstartdate'] = 'Can\'t guess the start 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['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 \'onlycli\' analytics setting'; $string['editmodel'] = 'Edit "{$a}" model'; @@ -59,7 +61,6 @@ $string['info'] = 'Info'; $string['insights'] = 'Insights'; $string['loginfo'] = 'Log extra info'; $string['modelresults'] = '{$a} results'; -$string['modelslist'] = 'Models list'; $string['modeltimesplitting'] = 'Time splitting'; $string['nodatatoevaluate'] = 'There is no data to evaluate the model'; $string['nodatatopredict'] = 'No new elements to get predictions for'; diff --git a/admin/tool/analytics/model.php b/admin/tool/analytics/model.php index 6d64c428c67..e418b4656bb 100644 --- a/admin/tool/analytics/model.php +++ b/admin/tool/analytics/model.php @@ -60,7 +60,9 @@ switch ($action) { case 'export': $title = get_string('export', 'tool_analytics'); break; - + case 'clear': + $title = get_string('clearpredictions', 'tool_analytics'); + break; default: throw new moodle_exception('errorunknownaction', 'analytics'); } @@ -80,14 +82,21 @@ if ($onlycli === false) { switch ($action) { case 'enable': + confirm_sesskey(); + $model->enable(); redirect(new \moodle_url('/admin/tool/analytics/index.php')); + break; case 'disable': + confirm_sesskey(); + $model->update(0, false, false); redirect(new \moodle_url('/admin/tool/analytics/index.php')); + break; case 'edit': + confirm_sesskey(); if ($model->is_static()) { echo $OUTPUT->header(); @@ -106,7 +115,6 @@ switch ($action) { redirect(new \moodle_url('/admin/tool/analytics/index.php')); } else if ($data = $mform->get_data()) { - confirm_sesskey(); // Converting option names to class names. $indicators = array(); @@ -131,6 +139,8 @@ switch ($action) { break; case 'evaluate': + confirm_sesskey(); + echo $OUTPUT->header(); if ($model->is_static()) { @@ -150,6 +160,8 @@ switch ($action) { break; case 'getpredictions': + confirm_sesskey(); + echo $OUTPUT->header(); if ($onlycli) { @@ -200,6 +212,13 @@ switch ($action) { $filename = 'training-data.' . $model->get_id() . '.' . time() . '.csv'; send_file($file, $filename, null, 0, false, true); break; + + case 'clear': + confirm_sesskey(); + + $model->clear(); + redirect(new \moodle_url('/admin/tool/analytics/index.php')); + break; } echo $OUTPUT->footer(); diff --git a/admin/tool/analytics/templates/models_list.mustache b/admin/tool/analytics/templates/models_list.mustache index 9330c5446d4..8eb6211a138 100644 --- a/admin/tool/analytics/templates/models_list.mustache +++ b/admin/tool/analytics/templates/models_list.mustache @@ -111,7 +111,7 @@
- + @@ -126,7 +126,7 @@ {{#models}}
{{#str}}modelslist, tool_analytics{{/str}}{{#str}}analyticmodels, tool_analytics{{/str}}
{{#str}}target, tool_analytics{{/str}}
- {{target}} + {{target}} {{#targethelp}} {{>core/help_icon}} {{/targethelp}}