Merge branch 'MDL-64777-master-modelsrestore' of git://github.com/mudrd8mz/moodle
This commit is contained in:
@@ -62,8 +62,33 @@ class models_list implements \renderable, \templatable {
|
||||
global $PAGE;
|
||||
|
||||
$data = new \stdClass();
|
||||
$data->importmodelurl = new \moodle_url('/admin/tool/analytics/importmodel.php');
|
||||
$data->createmodelurl = new \moodle_url('/admin/tool/analytics/createmodel.php');
|
||||
|
||||
$newmodelmenu = new \action_menu();
|
||||
$newmodelmenu->set_menu_trigger(get_string('newmodel', 'tool_analytics'), 'btn btn-default');
|
||||
$newmodelmenu->set_alignment(\action_menu::TL, \action_menu::BL);
|
||||
|
||||
$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) {
|
||||
|
||||
@@ -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 <[email protected]>
|
||||
* @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 <[email protected]>
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -36,6 +36,10 @@ $string['clearmodelpredictions'] = 'Are you sure you want to clear all "{$a}" pr
|
||||
$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['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['createmodel'] = 'Create model';
|
||||
$string['currenttimesplitting'] = 'Current time-splitting method';
|
||||
$string['delete'] = 'Delete';
|
||||
@@ -100,6 +104,7 @@ $string['modelinvalidanalysables'] = 'Invalid analysable elements for "{$a}" mod
|
||||
$string['modelname'] = 'Model name';
|
||||
$string['modelresults'] = '{$a} results';
|
||||
$string['modeltimesplitting'] = 'Time splitting';
|
||||
$string['newmodel'] = 'New model';
|
||||
$string['nextpage'] = 'Next page';
|
||||
$string['nodatatoevaluate'] = 'There is no data to evaluate the model';
|
||||
$string['nodatatopredict'] = 'No new elements to get predictions for';
|
||||
@@ -112,6 +117,12 @@ $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 the Moodle core and installed plugins have been already 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['selecttimesplittingforevaluation'] = 'Select the time-splitting method you want to use to evaluate the model configuration.';
|
||||
|
||||
@@ -0,0 +1,79 @@
|
||||
<?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 <[email protected]>
|
||||
* @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();
|
||||
|
||||
$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();
|
||||
@@ -144,8 +144,9 @@
|
||||
|
||||
<div class="box">
|
||||
<div class="top-nav d-flex">
|
||||
<a href="{{createmodelurl}}" class="btn btn-secondary mr-2">{{#str}}createmodel, tool_analytics{{/str}}</a>
|
||||
<a href="{{importmodelurl}}" class="btn btn-secondary">{{#str}}importmodel, tool_analytics{{/str}}</a>
|
||||
{{#newmodelmenu}}
|
||||
{{>core/action_menu}}
|
||||
{{/newmodelmenu}}
|
||||
</div>
|
||||
<table id="predictionmodelslist" class="generaltable fullwidth">
|
||||
<caption>{{#str}}analyticmodels, tool_analytics{{/str}}</caption>
|
||||
|
||||
@@ -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": "No teaching",
|
||||
"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 'No teaching' model.
|
||||
And I click on "Delete" "link" in the "No teaching" "table_row"
|
||||
And I should see "Analytics models"
|
||||
And I should not see "No teaching"
|
||||
# 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 "No teaching"
|
||||
And I should see "Students at risk of dropping out"
|
||||
# Select and restore the 'No teaching' model.
|
||||
And I set the field with xpath "//tr[contains(normalize-space(.), 'No teaching')]//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 "No teaching"
|
||||
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 'No teaching' model.
|
||||
And I click on "Delete" "link" in the "No teaching" "table_row"
|
||||
And I should see "Analytics models"
|
||||
And I should not see "No teaching"
|
||||
# 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 "No teaching"
|
||||
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(.), 'No teaching')]//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 "No teaching"
|
||||
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 "No teaching"
|
||||
When I click on "Restore default models" "link"
|
||||
Then I should see "All default models provided by the Moodle core and installed plugins have been already 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 'No teaching' model.
|
||||
And I click on "Actions" "link" in the "No teaching" "table_row"
|
||||
And I click on "Delete" "link" in the "No teaching" "table_row"
|
||||
And I click on "Delete" "button" in the "Delete" "dialogue"
|
||||
And I should see "Analytics models"
|
||||
And I should not see "No teaching"
|
||||
# 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 "No teaching"
|
||||
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 "No teaching"
|
||||
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 "No teaching"
|
||||
And I should see "Students at risk of dropping out"
|
||||
@@ -24,6 +24,6 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2018120300; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2018112800; // Requires this Moodle version.
|
||||
$plugin->version = 2019032800; // The current plugin version (Date: YYYYMMDDXX).
|
||||
$plugin->requires = 2019032200; // Requires this Moodle version.
|
||||
$plugin->component = 'tool_analytics'; // Full name of the plugin (used for diagnostics).
|
||||
|
||||
@@ -683,6 +683,43 @@ class manager {
|
||||
return $models;
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the list of all the models declared anywhere in this Moodle installation.
|
||||
*
|
||||
* Models defined by the core and core subsystems come first, followed by those provided by plugins.
|
||||
*
|
||||
* @return array indexed by the frankenstyle component
|
||||
*/
|
||||
public static function load_default_models_for_all_components(): array {
|
||||
|
||||
$tmp = [];
|
||||
|
||||
foreach (\core_component::get_component_list() as $type => $components) {
|
||||
foreach (array_keys($components) as $component) {
|
||||
if ($loaded = static::load_default_models_for_component($component)) {
|
||||
$tmp[$type][$component] = $loaded;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$result = [];
|
||||
|
||||
if ($loaded = static::load_default_models_for_component('core')) {
|
||||
$result['core'] = $loaded;
|
||||
}
|
||||
|
||||
if (!empty($tmp['core'])) {
|
||||
$result += $tmp['core'];
|
||||
unset($tmp['core']);
|
||||
}
|
||||
|
||||
foreach ($tmp as $components) {
|
||||
$result += $components;
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate the declaration of prediction models according the syntax expected in the component's db folder.
|
||||
*
|
||||
@@ -747,14 +784,7 @@ class manager {
|
||||
*/
|
||||
public static function create_declared_model(array $definition): \core_analytics\model {
|
||||
|
||||
$target = static::get_target($definition['target']);
|
||||
|
||||
$indicators = [];
|
||||
|
||||
foreach ($definition['indicators'] as $indicatorname) {
|
||||
$indicator = static::get_indicator($indicatorname);
|
||||
$indicators[$indicator->get_id()] = $indicator;
|
||||
}
|
||||
list($target, $indicators) = static::get_declared_target_and_indicators_instances($definition);
|
||||
|
||||
if (isset($definition['timesplitting'])) {
|
||||
$timesplitting = $definition['timesplitting'];
|
||||
@@ -770,4 +800,34 @@ class manager {
|
||||
|
||||
return $created;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a string uniquely representing the given model declaration.
|
||||
*
|
||||
* @param array $model Model declaration
|
||||
* @return string complying with PARAM_ALPHANUM rules and starting with an 'id' prefix
|
||||
*/
|
||||
public static function model_declaration_identifier(array $model) : string {
|
||||
return 'id'.sha1(serialize($model));
|
||||
}
|
||||
|
||||
/**
|
||||
* Given a model definition, return actual target and indicators instances.
|
||||
*
|
||||
* @param array $definition See {@link self::validate_models_declaration()} for the syntax.
|
||||
* @return array [0] => target instance, [1] => array of indicators instances
|
||||
*/
|
||||
public static function get_declared_target_and_indicators_instances(array $definition): array {
|
||||
|
||||
$target = static::get_target($definition['target']);
|
||||
|
||||
$indicators = [];
|
||||
|
||||
foreach ($definition['indicators'] as $indicatorname) {
|
||||
$indicator = static::get_indicator($indicatorname);
|
||||
$indicators[$indicator->get_id()] = $indicator;
|
||||
}
|
||||
|
||||
return [$target, $indicators];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -172,6 +172,20 @@ class analytics_manager_testcase extends advanced_testcase {
|
||||
$this->assertSame([], \core_analytics\manager::load_default_models_for_component('foo_bar2776327736558'));
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for the {@link \core_analytics\manager::load_default_models_for_all_components()} implementation.
|
||||
*/
|
||||
public function test_load_default_models_for_all_components() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$models = \core_analytics\manager::load_default_models_for_all_components();
|
||||
|
||||
$this->assertTrue(is_array($models['core']));
|
||||
$this->assertNotEmpty($models['core']);
|
||||
$this->assertNotEmpty($models['core'][0]['target']);
|
||||
$this->assertNotEmpty($models['core'][0]['indicators']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for the successful execution of the {@link \core_analytics\manager::validate_models_declaration()}.
|
||||
*/
|
||||
@@ -403,4 +417,62 @@ class analytics_manager_testcase extends advanced_testcase {
|
||||
$defaultforevaluation = \core_analytics\manager::get_time_splitting_methods_for_evaluation(false);
|
||||
$this->assertArrayNotHasKey('\core\analytics\time_splitting\quarters', $defaultforevaluation);
|
||||
}
|
||||
|
||||
/**
|
||||
* Test the implementation of the {@link \core_analytics\manager::model_declaration_identifier()}.
|
||||
*/
|
||||
public function test_model_declaration_identifier() {
|
||||
|
||||
$noteaching1 = $this->load_models_from_fixture_file('no_teaching');
|
||||
$noteaching2 = $this->load_models_from_fixture_file('no_teaching');
|
||||
$noteaching3 = $this->load_models_from_fixture_file('no_teaching');
|
||||
|
||||
// Same model declaration should always lead to same identifier.
|
||||
$this->assertEquals(
|
||||
\core_analytics\manager::model_declaration_identifier(reset($noteaching1)),
|
||||
\core_analytics\manager::model_declaration_identifier(reset($noteaching2))
|
||||
);
|
||||
|
||||
// If something is changed, the identifier should change, too.
|
||||
$noteaching2[0]['target'] .= '_';
|
||||
$this->assertNotEquals(
|
||||
\core_analytics\manager::model_declaration_identifier(reset($noteaching1)),
|
||||
\core_analytics\manager::model_declaration_identifier(reset($noteaching2))
|
||||
);
|
||||
|
||||
$noteaching3[0]['indicators'][] = '\core_analytics\local\indicator\binary';
|
||||
$this->assertNotEquals(
|
||||
\core_analytics\manager::model_declaration_identifier(reset($noteaching1)),
|
||||
\core_analytics\manager::model_declaration_identifier(reset($noteaching3))
|
||||
);
|
||||
|
||||
// The identifier is supposed to contain PARAM_ALPHANUM only.
|
||||
$this->assertEquals(
|
||||
\core_analytics\manager::model_declaration_identifier(reset($noteaching1)),
|
||||
clean_param(\core_analytics\manager::model_declaration_identifier(reset($noteaching1)), PARAM_ALPHANUM)
|
||||
);
|
||||
$this->assertEquals(
|
||||
\core_analytics\manager::model_declaration_identifier(reset($noteaching2)),
|
||||
clean_param(\core_analytics\manager::model_declaration_identifier(reset($noteaching2)), PARAM_ALPHANUM)
|
||||
);
|
||||
$this->assertEquals(
|
||||
\core_analytics\manager::model_declaration_identifier(reset($noteaching3)),
|
||||
clean_param(\core_analytics\manager::model_declaration_identifier(reset($noteaching3)), PARAM_ALPHANUM)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Tests for the {@link \core_analytics\manager::get_declared_target_and_indicators_instances()}.
|
||||
*/
|
||||
public function test_get_declared_target_and_indicators_instances() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$definition = $this->load_models_from_fixture_file('no_teaching');
|
||||
|
||||
list($target, $indicators) = \core_analytics\manager::get_declared_target_and_indicators_instances($definition[0]);
|
||||
|
||||
$this->assertTrue($target instanceof \core_analytics\local\target\base);
|
||||
$this->assertNotEmpty($indicators);
|
||||
$this->assertContainsOnlyInstancesOf(\core_analytics\local\indicator\base::class, $indicators);
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user