MDL-57791 tool_analytics: From tool_models to tool_analytics

This commit is contained in:
David Monllao
2017-07-24 08:37:03 +02:00
parent 690ad8759d
commit e10b29ed0c
26 changed files with 218 additions and 209 deletions
+1
View File
@@ -0,0 +1 @@
define(["jquery","core/str","core/modal_factory","core/notification"],function(a,b,c,d){return{loadInfo:function(e,f){var g=a('[data-model-log-id="'+e+'"]');b.get_string("loginfo","tool_analytics").then(function(b){var d=a("<ul>");return f.forEach(function(a){d.append("<li>"+a+"</li>")}),d.append("</ul>"),c.create({title:b,body:d.html(),large:!0},g)})["catch"](d.exception)}}});
@@ -16,15 +16,15 @@
/**
* Shows a dialogue with info about this logs.
*
* @module tool_models/log_info
* @module tool_analytics/log_info
* @class log_info
* @package tool_models
* @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(['jquery', 'core/str', 'core/modal_factory', 'core/notification'], function($, str, ModalFactory, Notification) {
return /** @alias module:tool_models/log_info */ {
return /** @alias module:tool_analytics/log_info */ {
/**
* Prepares a modal info for a log's results.
@@ -36,7 +36,7 @@ define(['jquery', 'core/str', 'core/modal_factory', 'core/notification'], functi
loadInfo: function(id, info) {
var link = $('[data-model-log-id="' + id + '"]');
str.get_string('loginfo', 'tool_models').then(function(langString) {
str.get_string('loginfo', 'tool_analytics').then(function(langString) {
var bodyInfo = $("<ul>");
info.forEach(function(item) {
@@ -17,12 +17,12 @@
/**
* Model edit form.
*
* @package tool_models
* @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_models\output\form;
namespace tool_analytics\output\form;
defined('MOODLE_INTERNAL') || die();
@@ -31,7 +31,7 @@ require_once($CFG->dirroot.'/lib/formslib.php');
/**
* Model edit form.
*
* @package tool_models
* @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
*/
@@ -46,26 +46,26 @@ class edit_model extends \moodleform {
$mform = $this->_form;
if ($this->_customdata['model']->get_model_obj()->trained == 1) {
$message = get_string('edittrainedwarning', 'tool_models');
$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_models'));
$mform->addElement('advcheckbox', 'enabled', get_string('enabled', 'tool_analytics'));
$indicators = array();
foreach ($this->_customdata['indicators'] as $classname => $indicator) {
$optionname = \tool_models\output\helper::class_to_option($classname);
$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_models'), $indicators, $options);
$mform->addElement('autocomplete', 'indicators', get_string('indicators', 'tool_analytics'), $indicators, $options);
$mform->setType('indicators', PARAM_ALPHANUMEXT);
$timesplittings = array('' => '');
foreach ($this->_customdata['timesplittings'] as $classname => $timesplitting) {
$optionname = \tool_models\output\helper::class_to_option($classname);
$optionname = \tool_analytics\output\helper::class_to_option($classname);
$timesplittings[$optionname] = $timesplitting->get_name();
}
@@ -93,7 +93,7 @@ class edit_model extends \moodleform {
$errors = parent::validation($data, $files);
if (!empty($data['timesplitting'])) {
$realtimesplitting = \tool_models\output\helper::option_to_class($data['timesplitting']);
$realtimesplitting = \tool_analytics\output\helper::option_to_class($data['timesplitting']);
if (\core_analytics\manager::is_valid($realtimesplitting, '\core_analytics\local\time_splitting\base') === false) {
$errors['timesplitting'] = get_string('errorinvalidtimesplitting', 'analytics');
}
@@ -103,7 +103,7 @@ class edit_model extends \moodleform {
$errors['indicators'] = get_string('errornoindicators', 'analytics');
} else {
foreach ($data['indicators'] as $indicator) {
$realindicatorname = \tool_models\output\helper::option_to_class($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);
}
@@ -111,7 +111,7 @@ class edit_model extends \moodleform {
}
if (!empty($data['enabled']) && empty($data['timesplitting'])) {
$errors['enabled'] = get_string('errorcantenablenotimesplitting', 'tool_models');
$errors['enabled'] = get_string('errorcantenablenotimesplitting', 'tool_analytics');
}
return $errors;
@@ -17,19 +17,19 @@
/**
* Typical crappy helper class with tiny functions.
*
* @package tool_models
* @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_models\output;
namespace tool_analytics\output;
defined('MOODLE_INTERNAL') || die();
/**
* Helper class with general purpose tiny functions.
*
* @package tool_models
* @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
*/
@@ -17,12 +17,12 @@
/**
* Model logs table class.
*
* @package tool_models
* @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_models\output;
namespace tool_analytics\output;
defined('MOODLE_INTERNAL') || die;
require_once($CFG->libdir . '/tablelib.php');
@@ -30,7 +30,7 @@ require_once($CFG->libdir . '/tablelib.php');
/**
* Model logs table class.
*
* @package tool_models
* @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
*/
@@ -61,10 +61,10 @@ class model_logs extends \table_sql {
$this->define_headers(array(
get_string('time'),
get_string('version'),
get_string('indicators', 'tool_models'),
get_string('indicators', 'tool_analytics'),
get_string('timesplittingmethod', 'analytics'),
get_string('accuracy', 'tool_models'),
get_string('info', 'tool_models'),
get_string('accuracy', 'tool_analytics'),
get_string('info', 'tool_analytics'),
get_string('fullnameuser'),
));
$this->pageable(true);
@@ -156,9 +156,9 @@ class model_logs extends \table_sql {
$info = json_decode($log->info);
}
if (!empty($log->dir)) {
$info[] = get_string('predictorresultsin', 'tool_models', $log->dir);
$info[] = get_string('predictorresultsin', 'tool_analytics', $log->dir);
}
$PAGE->requires->js_call_amd('tool_models/log_info', 'loadInfo', array($log->id, $info));
$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));
}
@@ -17,19 +17,19 @@
/**
* Prediction models list page.
*
* @package tool_models
* @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_models\output;
namespace tool_analytics\output;
defined('MOODLE_INTERNAL') || die();
/**
* Shows tool_models models list.
* Shows tool_analytics models list.
*
* @package tool_models
* @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
*/
@@ -82,7 +82,7 @@ class models_list implements \renderable, \templatable {
// Special name for system level predictions as showing "System is not visually nice".
if ($contextid == SYSCONTEXTID) {
$contextname = get_string('allpredictions', 'tool_models');
$contextname = get_string('allpredictions', 'tool_analytics');
} else {
$contextname = shorten_text($context->get_context_name(true, true), 90);
}
@@ -125,23 +125,23 @@ class models_list implements \renderable, \templatable {
// Evaluate machine-learning-based models.
if ($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_models')),
get_string('evaluate', 'tool_models'));
$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);
}
if ($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_models')), get_string('getpredictions', 'tool_models'));
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_models')),
get_string('viewlog', 'tool_models'));
$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);
}
@@ -151,7 +151,7 @@ class models_list implements \renderable, \templatable {
}
$data->warnings = array(
(object)array('message' => get_string('bettercli', 'tool_models'), 'closebutton' => true)
(object)array('message' => get_string('bettercli', 'tool_analytics'), 'closebutton' => true)
);
return $data;
@@ -17,12 +17,12 @@
/**
* Renderer.
*
* @package tool_models
* @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_models\output;
namespace tool_analytics\output;
defined('MOODLE_INTERNAL') || die();
@@ -33,7 +33,7 @@ use renderable;
/**
* Renderer class.
*
* @package tool_models
* @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
*/
@@ -42,12 +42,12 @@ class renderer extends plugin_renderer_base {
/**
* Defer to template.
*
* @param \tool_models\output\models_list $modelslist
* @param \tool_analytics\output\models_list $modelslist
* @return string HTML
*/
protected function render_models_list(\tool_models\output\models_list $modelslist) {
protected function render_models_list(\tool_analytics\output\models_list $modelslist) {
$data = $modelslist->export_for_template($this);
return parent::render_from_template('tool_models/models_list', $data);
return parent::render_from_template('tool_analytics/models_list', $data);
}
/**
@@ -90,23 +90,23 @@ class renderer extends plugin_renderer_base {
$langstrdata = (object)array('name' => $timesplitting->get_name(), 'id' => $timesplittingid);
if (CLI_SCRIPT) {
$output .= $OUTPUT->heading(get_string('getpredictionsresultscli', 'tool_models', $langstrdata), 3);
$output .= $OUTPUT->heading(get_string('getpredictionsresultscli', 'tool_analytics', $langstrdata), 3);
} else {
$output .= $OUTPUT->heading(get_string('getpredictionsresults', 'tool_models', $langstrdata), 3);
$output .= $OUTPUT->heading(get_string('getpredictionsresults', 'tool_analytics', $langstrdata), 3);
}
}
if ($result->status == 0) {
$output .= $OUTPUT->notification(get_string('goodmodel', 'tool_models'),
$output .= $OUTPUT->notification(get_string('goodmodel', 'tool_analytics'),
\core\output\notification::NOTIFY_SUCCESS);
} else if ($result->status === \core_analytics\model::NO_DATASET) {
$output .= $OUTPUT->notification(get_string('nodatatoevaluate', 'tool_models'),
$output .= $OUTPUT->notification(get_string('nodatatoevaluate', 'tool_analytics'),
\core\output\notification::NOTIFY_WARNING);
}
if (isset($result->score)) {
// Score.
$output .= $OUTPUT->heading(get_string('accuracy', 'tool_models') . ': ' .
$output .= $OUTPUT->heading(get_string('accuracy', 'tool_analytics') . ': ' .
round(floatval($result->score), 4) * 100 . '%', 4);
}
@@ -123,14 +123,14 @@ class renderer extends plugin_renderer_base {
// Info logged during evaluation.
if (!empty($logs) && debugging()) {
$output .= $OUTPUT->heading(get_string('extrainfo', 'tool_models'), 3);
$output .= $OUTPUT->heading(get_string('extrainfo', 'tool_analytics'), 3);
foreach ($logs as $log) {
$output .= $OUTPUT->notification($log, \core\output\notification::NOTIFY_WARNING);
}
}
if (!CLI_SCRIPT) {
$output .= $OUTPUT->single_button(new \moodle_url('/admin/tool/models/index.php'), get_string('continue'));
$output .= $OUTPUT->single_button(new \moodle_url('/admin/tool/analytics/index.php'), get_string('continue'));
}
return $output;
@@ -152,15 +152,15 @@ class renderer extends plugin_renderer_base {
$output = '';
if ($trainresults || (!empty($trainlogs) && debugging())) {
$output .= $OUTPUT->heading(get_string('trainingresults', 'tool_models'), 3);
$output .= $OUTPUT->heading(get_string('trainingresults', 'tool_analytics'), 3);
}
if ($trainresults) {
if ($trainresults->status == 0) {
$output .= $OUTPUT->notification(get_string('trainingprocessfinished', 'tool_models'),
$output .= $OUTPUT->notification(get_string('trainingprocessfinished', 'tool_analytics'),
\core\output\notification::NOTIFY_SUCCESS);
} else if ($trainresults->status === \core_analytics\model::NO_DATASET) {
$output .= $OUTPUT->notification(get_string('nodatatotrain', 'tool_models'),
$output .= $OUTPUT->notification(get_string('nodatatotrain', 'tool_analytics'),
\core\output\notification::NOTIFY_WARNING);
} else {
$output .= $OUTPUT->notification(get_string('generalerror', 'analytics', $trainresults->status),
@@ -169,22 +169,22 @@ class renderer extends plugin_renderer_base {
}
if (!empty($trainlogs) && debugging()) {
$output .= $OUTPUT->heading(get_string('extrainfo', 'tool_models'), 4);
$output .= $OUTPUT->heading(get_string('extrainfo', 'tool_analytics'), 4);
foreach ($trainlogs as $log) {
$output .= $OUTPUT->notification($log, \core\output\notification::NOTIFY_WARNING);
}
}
if ($predictresults || (!empty($predictlogs) && debugging())) {
$output .= $OUTPUT->heading(get_string('predictionresults', 'tool_models'), 3, 'main m-t-3');
$output .= $OUTPUT->heading(get_string('predictionresults', 'tool_analytics'), 3, 'main m-t-3');
}
if ($predictresults) {
if ($predictresults->status == 0) {
$output .= $OUTPUT->notification(get_string('predictionprocessfinished', 'tool_models'),
$output .= $OUTPUT->notification(get_string('predictionprocessfinished', 'tool_analytics'),
\core\output\notification::NOTIFY_SUCCESS);
} else if ($predictresults->status === \core_analytics\model::NO_DATASET) {
$output .= $OUTPUT->notification(get_string('nodatatopredict', 'tool_models'),
$output .= $OUTPUT->notification(get_string('nodatatopredict', 'tool_analytics'),
\core\output\notification::NOTIFY_WARNING);
} else {
$output .= $OUTPUT->notification(get_string('generalerror', 'analytics', $predictresults->status),
@@ -193,14 +193,14 @@ class renderer extends plugin_renderer_base {
}
if (!empty($predictlogs) && debugging()) {
$output .= $OUTPUT->heading(get_string('extrainfo', 'tool_models'), 4);
$output .= $OUTPUT->heading(get_string('extrainfo', 'tool_analytics'), 4);
foreach ($predictlogs as $log) {
$output .= $OUTPUT->notification($log, \core\output\notification::NOTIFY_WARNING);
}
}
if (!CLI_SCRIPT) {
$output .= $OUTPUT->single_button(new \moodle_url('/admin/tool/models/index.php'), get_string('continue'));
$output .= $OUTPUT->single_button(new \moodle_url('/admin/tool/analytics/index.php'), get_string('continue'));
}
return $output;
@@ -17,19 +17,19 @@
/**
* Predict system models with new data available.
*
* @package tool_models
* @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_models\task;
namespace tool_analytics\task;
defined('MOODLE_INTERNAL') || die();
/**
* Predict system models with new data available.
*
* @package tool_models
* @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
*/
@@ -41,7 +41,7 @@ class predict_models extends \core\task\scheduled_task {
* @return string
*/
public function get_name() {
return get_string('predictmodels', 'tool_models');
return get_string('predictmodels', 'tool_analytics');
}
/**
@@ -54,15 +54,15 @@ class predict_models extends \core\task\scheduled_task {
$models = \core_analytics\manager::get_all_models(true, true);
if (!$models) {
mtrace(get_string('errornoenabledandtrainedmodels', 'tool_models'));
mtrace(get_string('errornoenabledandtrainedmodels', 'tool_analytics'));
return;
}
foreach ($models as $model) {
$result = $model->predict();
if ($result) {
echo $OUTPUT->heading(get_string('modelresults', 'tool_models', $model->get_target()->get_name()));
$renderer = $PAGE->get_renderer('tool_models');
echo $OUTPUT->heading(get_string('modelresults', 'tool_analytics', $model->get_target()->get_name()));
$renderer = $PAGE->get_renderer('tool_analytics');
echo $renderer->render_get_predictions_results(false, array(), $result, $model->get_analyser()->get_logs());
}
}
@@ -17,19 +17,19 @@
/**
* Train system models with new data available.
*
* @package tool_models
* @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_models\task;
namespace tool_analytics\task;
defined('MOODLE_INTERNAL') || die();
/**
* Train system models with new data available.
*
* @package tool_models
* @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
*/
@@ -41,7 +41,7 @@ class train_models extends \core\task\scheduled_task {
* @return string
*/
public function get_name() {
return get_string('trainmodels', 'tool_models');
return get_string('trainmodels', 'tool_analytics');
}
/**
@@ -54,7 +54,7 @@ class train_models extends \core\task\scheduled_task {
$models = \core_analytics\manager::get_all_models(true);
if (!$models) {
mtrace(get_string('errornoenabledmodels', 'tool_models'));
mtrace(get_string('errornoenabledmodels', 'tool_analytics'));
return;
}
@@ -72,9 +72,9 @@ class train_models extends \core\task\scheduled_task {
$result = $model->train();
if ($result) {
echo $OUTPUT->heading(get_string('modelresults', 'tool_models', $model->get_target()->get_name()));
echo $OUTPUT->heading(get_string('modelresults', 'tool_analytics', $model->get_target()->get_name()));
$renderer = $PAGE->get_renderer('tool_models');
$renderer = $PAGE->get_renderer('tool_analytics');
echo $renderer->render_get_predictions_results($result, $model->get_analyser()->get_logs());
}
}
@@ -17,7 +17,7 @@
/**
* Enables the provided model.
*
* @package tool_models
* @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
*/
@@ -35,7 +35,7 @@ Options:
-h, --help Print out this help
Example:
\$ php admin/tool/models/cli/enable_model.php --modelid=1 --timesplitting=\"\\core\\analytics\\time_splitting\\quarters\"
\$ php admin/tool/analytics/cli/enable_model.php --modelid=1 --timesplitting=\"\\core\\analytics\\time_splitting\\quarters\"
";
// Now get cli options.
@@ -17,7 +17,7 @@
/**
* Evaluates the provided model.
*
* @package tool_models
* @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
*/
@@ -39,7 +39,7 @@ Options:
-h, --help Print out this help
Example:
\$ php admin/tool/models/cli/evaluate_model.php --modelid=1 --timesplitting='\\core\\analytics\\time_splitting\\quarters' --filter=123,321
\$ php admin/tool/analytics/cli/evaluate_model.php --modelid=1 --timesplitting='\\core\\analytics\\time_splitting\\quarters' --filter=123,321
";
// Now get cli options.
@@ -77,10 +77,10 @@ if ($options['filter'] !== false) {
$model = new \core_analytics\model($options['modelid']);
mtrace(get_string('analysingsitedata', 'tool_models'));
mtrace(get_string('analysingsitedata', 'tool_analytics'));
if ($options['reuse-prev-analysed']) {
mtrace(get_string('evaluationinbatches', 'tool_models'));
mtrace(get_string('evaluationinbatches', 'tool_analytics'));
}
$analyseroptions = array(
@@ -91,7 +91,7 @@ $analyseroptions = array(
// Evaluate its suitability to predict accurately.
$results = $model->evaluate($analyseroptions);
$renderer = $PAGE->get_renderer('tool_models');
$renderer = $PAGE->get_renderer('tool_analytics');
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.
@@ -105,10 +105,10 @@ foreach ($results as $result) {
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_models'));
$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_models'));
$input = cli_input(get_string('clienablemodel', 'tool_analytics'));
}
if ($input === 'none') {
@@ -121,7 +121,7 @@ if (!empty($validdatasets) && !$model->is_enabled() && $options['non-interactive
// Set the time splitting method file and enable it.
$model->enable($input);
mtrace(get_string('trainandpredictmodel', 'tool_models'));
mtrace(get_string('trainandpredictmodel', 'tool_analytics'));
// Train the model with the selected time splitting method and start predicting.
$model->train();
@@ -17,7 +17,7 @@
/**
* Guesses course start and end dates based on activity logs.
*
* @package tool_models
* @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
*/
@@ -41,7 +41,7 @@ Options:
-h, --help Print out this help
Example:
\$ php admin/tool/models/cli/guess_course_start_and_end_dates.php --update=1 --filter=123,321
\$ php admin/tool/analytics/cli/guess_course_start_and_end_dates.php --update=1 --filter=123,321
";
// Now get cli options.
@@ -96,19 +96,19 @@ if ($options['filter']) {
$courses = $DB->get_recordset_select('course', implode(' AND ', $conditions), $params, 'sortorder ASC');
foreach ($courses as $course) {
tool_models_calculate_course_dates($course, $options);
tool_analytics_calculate_course_dates($course, $options);
}
$courses->close();
/**
* tool_models_calculate_course_dates
* tool_analytics_calculate_course_dates
*
* @param stdClass $course
* @param array $options CLI options
* @return void
*/
function tool_models_calculate_course_dates($course, $options) {
function tool_analytics_calculate_course_dates($course, $options) {
global $DB, $OUTPUT;
$courseman = new \core_analytics\course($course);
@@ -122,13 +122,13 @@ function tool_models_calculate_course_dates($course, $options) {
$guessedstartdate = $courseman->guess_start();
if ($guessedstartdate == $originalstartdate) {
if (!$guessedstartdate) {
$notification .= PHP_EOL . ' ' . get_string('cantguessstartdate', 'tool_models');
$notification .= PHP_EOL . ' ' . get_string('cantguessstartdate', 'tool_analytics');
} else {
// No need to update.
$notification .= PHP_EOL . ' ' . get_string('samestartdate', 'tool_models') . ': ' . userdate($guessedstartdate);
$notification .= PHP_EOL . ' ' . get_string('samestartdate', 'tool_analytics') . ': ' . userdate($guessedstartdate);
}
} else if (!$guessedstartdate) {
$notification .= PHP_EOL . ' ' . get_string('cantguessstartdate', 'tool_models');
$notification .= PHP_EOL . ' ' . get_string('cantguessstartdate', 'tool_analytics');
} else {
// Update it to something we guess.
@@ -160,24 +160,24 @@ function tool_models_calculate_course_dates($course, $options) {
if ($options['update']) {
format_weeks::update_end_date($course->id);
$course->enddate = $DB->get_field('course', 'enddate', array('id' => $course->id));
$notification .= PHP_EOL . ' ' . get_string('weeksenddateautomaticallyset', 'tool_models') . ': ' .
$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_models');
$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_models');
$notification .= PHP_EOL . ' ' . get_string('cantguessenddate', 'tool_analytics');
} else {
// No need to update.
$notification .= PHP_EOL . ' ' . get_string('sameenddate', 'tool_models') . ': ' . userdate($guessedenddate);
$notification .= PHP_EOL . ' ' . get_string('sameenddate', 'tool_analytics') . ': ' . userdate($guessedenddate);
}
} else if (!$guessedenddate) {
$notification .= PHP_EOL . ' ' . get_string('cantguessenddate', 'tool_models');
$notification .= PHP_EOL . ' ' . get_string('cantguessenddate', 'tool_analytics');
} else {
// Update it to something we guess.
@@ -17,7 +17,7 @@
/**
* This file defines tasks performed by the tool.
*
* @package tool_models
* @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
*/
@@ -27,7 +27,7 @@ defined('MOODLE_INTERNAL') || die();
// List of tasks.
$tasks = array(
array(
'classname' => 'tool_models\task\train_models',
'classname' => 'tool_analytics\task\train_models',
'blocking' => 0,
'minute' => '0',
'hour' => 'R',
@@ -36,7 +36,7 @@ $tasks = array(
'month' => '*'
),
array(
'classname' => 'tool_models\task\predict_models',
'classname' => 'tool_analytics\task\predict_models',
'blocking' => 0,
'minute' => '0',
'hour' => 'R',
@@ -17,7 +17,7 @@
/**
* Prediction models tool frontend.
*
* @package tool_models
* @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
*/
@@ -31,7 +31,7 @@ $models = \core_analytics\manager::get_all_models();
echo $OUTPUT->header();
$templatable = new \tool_models\output\models_list($models);
echo $PAGE->get_renderer('tool_models')->render($templatable);
$templatable = new \tool_analytics\output\models_list($models);
echo $PAGE->get_renderer('tool_analytics')->render($templatable);
echo $OUTPUT->footer();
@@ -15,9 +15,9 @@
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for tool_models.
* Strings for tool_analytics.
*
* @package tool_models
* @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
*/
@@ -17,7 +17,7 @@
/**
* Model-related actions.
*
* @package tool_models
* @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
*/
@@ -35,21 +35,21 @@ $model = new \core_analytics\model($id);
\core_analytics\manager::check_can_manage_models();
$params = array('id' => $id, 'action' => $action);
$url = new \moodle_url('/admin/tool/models/model.php', $params);
$url = new \moodle_url('/admin/tool/analytics/model.php', $params);
switch ($action) {
case 'edit':
$title = get_string('editmodel', 'tool_models', $model->get_target()->get_name());
$title = get_string('editmodel', 'tool_analytics', $model->get_target()->get_name());
break;
case 'evaluate':
$title = get_string('evaluatemodel', 'tool_models');
$title = get_string('evaluatemodel', 'tool_analytics');
break;
case 'getpredictions':
$title = get_string('getpredictions', 'tool_models');
$title = get_string('getpredictions', 'tool_analytics');
break;
case 'log':
$title = get_string('viewlog', 'tool_models');
$title = get_string('viewlog', 'tool_analytics');
break;
default:
throw new moodle_exception('errorunknownaction', 'analytics');
@@ -67,7 +67,7 @@ switch ($action) {
if ($model->is_static()) {
echo $OUTPUT->header();
throw new moodle_exception('errornostaticedit', 'tool_models');
throw new moodle_exception('errornostaticedit', 'tool_analytics');
}
$customdata = array(
@@ -76,10 +76,10 @@ switch ($action) {
'indicators' => $model->get_potential_indicators(),
'timesplittings' => \core_analytics\manager::get_enabled_time_splitting_methods()
);
$mform = new \tool_models\output\form\edit_model(null, $customdata);
$mform = new \tool_analytics\output\form\edit_model(null, $customdata);
if ($mform->is_cancelled()) {
redirect(new \moodle_url('/admin/tool/models/index.php'));
redirect(new \moodle_url('/admin/tool/analytics/index.php'));
} else if ($data = $mform->get_data()) {
confirm_sesskey();
@@ -87,21 +87,21 @@ switch ($action) {
// Converting option names to class names.
$indicators = array();
foreach ($data->indicators as $indicator) {
$indicatorclass = \tool_models\output\helper::option_to_class($indicator);
$indicatorclass = \tool_analytics\output\helper::option_to_class($indicator);
$indicators[] = \core_analytics\manager::get_indicator($indicatorclass);
}
$timesplitting = \tool_models\output\helper::option_to_class($data->timesplitting);
$timesplitting = \tool_analytics\output\helper::option_to_class($data->timesplitting);
$model->update($data->enabled, $indicators, $timesplitting);
redirect(new \moodle_url('/admin/tool/models/index.php'));
redirect(new \moodle_url('/admin/tool/analytics/index.php'));
}
echo $OUTPUT->header();
$modelobj = $model->get_model_obj();
$callable = array('\tool_models\output\helper', 'class_to_option');
$callable = array('\tool_analytics\output\helper', 'class_to_option');
$modelobj->indicators = array_map($callable, json_decode($modelobj->indicators));
$modelobj->timesplitting = \tool_models\output\helper::class_to_option($modelobj->timesplitting);
$modelobj->timesplitting = \tool_analytics\output\helper::class_to_option($modelobj->timesplitting);
$mform->set_data($modelobj);
$mform->display();
break;
@@ -110,14 +110,14 @@ switch ($action) {
echo $OUTPUT->header();
if ($model->is_static()) {
throw new moodle_exception('errornostaticevaluate', 'tool_models');
throw new moodle_exception('errornostaticevaluate', '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.
$results = $model->evaluate(array('reuseprevanalysed' => true));
$renderer = $PAGE->get_renderer('tool_models');
$renderer = $PAGE->get_renderer('tool_analytics');
echo $renderer->render_evaluate_results($results, $model->get_analyser()->get_logs());
break;
@@ -132,7 +132,7 @@ switch ($action) {
$predictresults = $model->predict();
$predictlogs = $model->get_analyser()->get_logs();
$renderer = $PAGE->get_renderer('tool_models');
$renderer = $PAGE->get_renderer('tool_analytics');
echo $renderer->render_get_predictions_results($trainresults, $trainlogs, $predictresults, $predictlogs);
break;
@@ -140,11 +140,11 @@ switch ($action) {
echo $OUTPUT->header();
if ($model->is_static()) {
throw new moodle_exception('errornostaticlog', 'tool_models');
throw new moodle_exception('errornostaticlog', 'tool_analytics');
}
$renderer = $PAGE->get_renderer('tool_models');
$modellogstable = new \tool_models\output\model_logs('model-' . $model->get_id(), $model);
$renderer = $PAGE->get_renderer('tool_analytics');
$modellogstable = new \tool_analytics\output\model_logs('model-' . $model->get_id(), $model);
echo $renderer->render_table($modellogstable);
break;
}
@@ -17,12 +17,12 @@
/**
* Adds settings links to admin tree.
*
* @package tool_models
* @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();
$ADMIN->add('reports', new admin_externalpage('analyticmodels', get_string('analyticmodels', 'tool_models'),
"$CFG->wwwroot/$CFG->admin/tool/models/index.php", 'moodle/analytics:managemodels'));
$ADMIN->add('reports', new admin_externalpage('analyticmodels', get_string('analyticmodels', 'tool_analytics'),
"$CFG->wwwroot/$CFG->admin/tool/analytics/index.php", 'moodle/analytics:managemodels'));
@@ -15,7 +15,7 @@
along with Moodle. If not, see <http://www.gnu.org/licenses/>.
}}
{{!
@template tool_models/models_list
@template tool_analytics/models_list
Template for models list.
@@ -55,14 +55,14 @@
{{/warnings}}
<div class="box">
<table class="generaltable fullwidth">
<caption>{{#str}}modelslist, tool_models{{/str}}</caption>
<caption>{{#str}}modelslist, tool_analytics{{/str}}</caption>
<thead>
<tr>
<th scope="col">{{#str}}target, tool_models{{/str}}</th>
<th scope="col">{{#str}}enabled, tool_models{{/str}}</th>
<th scope="col">{{#str}}indicators, tool_models{{/str}}</th>
<th scope="col">{{#str}}modeltimesplitting, tool_models{{/str}}</th>
<th scope="col">{{#str}}insights, tool_models{{/str}}</th>
<th scope="col">{{#str}}target, 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>
@@ -86,7 +86,7 @@
</ul>
</td>
<td>
{{#timesplitting}}{{timesplitting}}{{/timesplitting}}{{^timesplitting}}{{#str}}notdefined, tool_models{{/str}}{{/timesplitting}}
{{#timesplitting}}{{timesplitting}}{{/timesplitting}}{{^timesplitting}}{{#str}}notdefined, tool_analytics{{/str}}{{/timesplitting}}
</td>
<td>
{{! models_list renderer is responsible of sending one or the other}}
@@ -17,7 +17,7 @@
/**
* Version details.
*
* @package tool_models
* @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
*/
@@ -26,4 +26,4 @@ defined('MOODLE_INTERNAL') || die();
$plugin->version = 2017051500; // The current plugin version (Date: YYYYMMDDXX).
$plugin->requires = 2017050500; // Requires this Moodle version.
$plugin->component = 'tool_models'; // Full name of the plugin (used for diagnostics).
$plugin->component = 'tool_analytics'; // Full name of the plugin (used for diagnostics).
-1
View File
@@ -1 +0,0 @@
define(["jquery","core/str","core/modal_factory","core/notification"],function(a,b,c,d){return{loadInfo:function(e,f){var g=a('[data-model-log-id="'+e+'"]');b.get_string("loginfo","tool_models").then(function(b){var d=a("<ul>");return f.forEach(function(a){d.append("<li>"+a+"</li>")}),d.append("</ul>"),c.create({title:b,body:d.html(),large:!0},g)})["catch"](d.exception)}}});
+1 -1
View File
@@ -387,7 +387,7 @@ class dataset_manager {
$filename = self::EVALUATION_FILENAME;
} else {
// Incremental time, the lock will make sure we don't have concurrency problems.
$filename = time() . '.csv';
$filename = microtime(false) . '.csv';
}
return $filename;
+1 -1
View File
@@ -259,7 +259,7 @@ abstract class base {
// We need to check that the analysable is valid for the target even if we don't include targets
// as we still need to discard invalid analysables for the target.
$result = $target->is_valid_analysable($analysable, $includetarget, true);
$result = $target->is_valid_analysable($analysable, $includetarget);
if ($result !== true) {
$a = new \stdClass();
$a->analysableid = $analysable->get_id();
+3 -1
View File
@@ -100,8 +100,10 @@ class manager {
$conditions[] = 'am.trained = :trained';
$params['trained'] = 1;
}
$sql .= ' WHERE ' . implode(' AND ', $conditions) . ' ORDER BY am.timemodified DESC';
$sql .= ' WHERE ' . implode(' AND ', $conditions);
}
$sql .= ' ORDER BY am.enabled DESC, am.timemodified DESC';
$modelobjs = $DB->get_records_sql($sql, $params);
$models = array();
+2 -2
View File
@@ -1904,9 +1904,9 @@ class core_plugin_manager {
),
'tool' => array(
'assignmentupgrade', 'availabilityconditions', 'behat', 'capability', 'cohortroles', 'customlang',
'analytics', 'assignmentupgrade', 'availabilityconditions', 'behat', 'capability', 'cohortroles', 'customlang',
'dbtransfer', 'filetypes', 'generator', 'health', 'innodb', 'installaddon',
'langimport', 'log', 'lp', 'lpimportcsv', 'lpmigrate', 'messageinbound', 'mobile', 'models', 'multilangupgrade',
'langimport', 'log', 'lp', 'lpimportcsv', 'lpmigrate', 'messageinbound', 'mobile', 'multilangupgrade',
'monitor', 'oauth2', 'phpunit', 'profiling', 'recyclebin', 'replace', 'spamcleaner', 'task', 'templatelibrary',
'unittest', 'uploadcourse', 'uploaduser', 'unsuproles', 'usertours', 'xmldb'
),
+72 -65
View File
@@ -1988,7 +1988,7 @@ function xmldb_main_upgrade($oldversion) {
upgrade_main_savepoint(true, 2017071100.00);
}
if ($oldversion < 2017072000.01) {
if ($oldversion < 2017072000.02) {
// Define table analytics_models to be created.
$table = new xmldb_table('analytics_models');
@@ -2138,73 +2138,80 @@ function xmldb_main_upgrade($oldversion) {
$now = time();
$admin = get_admin();
// We can not use API calls to create the built-in models.
$modelobj = new stdClass();
$modelobj->target = '\core\analytics\target\course_dropout';
$modelobj->indicators = json_encode(array(
'\mod_assign\analytics\indicator\cognitive_depth',
'\mod_assign\analytics\indicator\social_breadth',
'\mod_book\analytics\indicator\cognitive_depth',
'\mod_book\analytics\indicator\social_breadth',
'\mod_chat\analytics\indicator\cognitive_depth',
'\mod_chat\analytics\indicator\social_breadth',
'\mod_choice\analytics\indicator\cognitive_depth',
'\mod_choice\analytics\indicator\social_breadth',
'\mod_data\analytics\indicator\cognitive_depth',
'\mod_data\analytics\indicator\social_breadth',
'\mod_feedback\analytics\indicator\cognitive_depth',
'\mod_feedback\analytics\indicator\social_breadth',
'\mod_folder\analytics\indicator\cognitive_depth',
'\mod_folder\analytics\indicator\social_breadth',
'\mod_forum\analytics\indicator\cognitive_depth',
'\mod_forum\analytics\indicator\social_breadth',
'\mod_glossary\analytics\indicator\cognitive_depth',
'\mod_glossary\analytics\indicator\social_breadth',
'\mod_imscp\analytics\indicator\cognitive_depth',
'\mod_imscp\analytics\indicator\social_breadth',
'\mod_label\analytics\indicator\cognitive_depth',
'\mod_label\analytics\indicator\social_breadth',
'\mod_lesson\analytics\indicator\cognitive_depth',
'\mod_lesson\analytics\indicator\social_breadth',
'\mod_lti\analytics\indicator\cognitive_depth',
'\mod_lti\analytics\indicator\social_breadth',
'\mod_page\analytics\indicator\cognitive_depth',
'\mod_page\analytics\indicator\social_breadth',
'\mod_quiz\analytics\indicator\cognitive_depth',
'\mod_quiz\analytics\indicator\social_breadth',
'\mod_resource\analytics\indicator\cognitive_depth',
'\mod_resource\analytics\indicator\social_breadth',
'\mod_scorm\analytics\indicator\cognitive_depth',
'\mod_scorm\analytics\indicator\social_breadth',
'\mod_survey\analytics\indicator\cognitive_depth',
'\mod_survey\analytics\indicator\social_breadth',
'\mod_url\analytics\indicator\cognitive_depth',
'\mod_url\analytics\indicator\social_breadth',
'\mod_wiki\analytics\indicator\cognitive_depth',
'\mod_wiki\analytics\indicator\social_breadth',
'\mod_workshop\analytics\indicator\cognitive_depth',
'\mod_workshop\analytics\indicator\social_breadth',
));
$modelobj->version = $now;
$modelobj->timecreated = $now;
$modelobj->timemodified = $now;
$modelobj->usermodified = $admin->id;
$DB->insert_record('analytics_models', $modelobj);
$modelobj = new stdClass();
$modelobj->enabled = 1;
$modelobj->trained = 1;
$modelobj->target = '\core\analytics\target\no_teaching';
$modelobj->indicators = json_encode(array('\core_course\analytics\indicator\no_teacher'));
$modelobj->timesplitting = '\core\analytics\time_splitting\single_range';
$modelobj->version = $now;
$modelobj->timecreated = $now;
$modelobj->timemodified = $now;
$modelobj->usermodified = $admin->id;
$DB->insert_record('analytics_models', $modelobj);
$targetname = '\core\analytics\target\course_dropout';
if (!$DB->record_exists('analytics_models', array('target' => $targetname))) {
// We can not use API calls to create the built-in models.
$modelobj = new stdClass();
$modelobj->target = $targetname;
$modelobj->indicators = json_encode(array(
'\mod_assign\analytics\indicator\cognitive_depth',
'\mod_assign\analytics\indicator\social_breadth',
'\mod_book\analytics\indicator\cognitive_depth',
'\mod_book\analytics\indicator\social_breadth',
'\mod_chat\analytics\indicator\cognitive_depth',
'\mod_chat\analytics\indicator\social_breadth',
'\mod_choice\analytics\indicator\cognitive_depth',
'\mod_choice\analytics\indicator\social_breadth',
'\mod_data\analytics\indicator\cognitive_depth',
'\mod_data\analytics\indicator\social_breadth',
'\mod_feedback\analytics\indicator\cognitive_depth',
'\mod_feedback\analytics\indicator\social_breadth',
'\mod_folder\analytics\indicator\cognitive_depth',
'\mod_folder\analytics\indicator\social_breadth',
'\mod_forum\analytics\indicator\cognitive_depth',
'\mod_forum\analytics\indicator\social_breadth',
'\mod_glossary\analytics\indicator\cognitive_depth',
'\mod_glossary\analytics\indicator\social_breadth',
'\mod_imscp\analytics\indicator\cognitive_depth',
'\mod_imscp\analytics\indicator\social_breadth',
'\mod_label\analytics\indicator\cognitive_depth',
'\mod_label\analytics\indicator\social_breadth',
'\mod_lesson\analytics\indicator\cognitive_depth',
'\mod_lesson\analytics\indicator\social_breadth',
'\mod_lti\analytics\indicator\cognitive_depth',
'\mod_lti\analytics\indicator\social_breadth',
'\mod_page\analytics\indicator\cognitive_depth',
'\mod_page\analytics\indicator\social_breadth',
'\mod_quiz\analytics\indicator\cognitive_depth',
'\mod_quiz\analytics\indicator\social_breadth',
'\mod_resource\analytics\indicator\cognitive_depth',
'\mod_resource\analytics\indicator\social_breadth',
'\mod_scorm\analytics\indicator\cognitive_depth',
'\mod_scorm\analytics\indicator\social_breadth',
'\mod_survey\analytics\indicator\cognitive_depth',
'\mod_survey\analytics\indicator\social_breadth',
'\mod_url\analytics\indicator\cognitive_depth',
'\mod_url\analytics\indicator\social_breadth',
'\mod_wiki\analytics\indicator\cognitive_depth',
'\mod_wiki\analytics\indicator\social_breadth',
'\mod_workshop\analytics\indicator\cognitive_depth',
'\mod_workshop\analytics\indicator\social_breadth',
));
$modelobj->version = $now;
$modelobj->timecreated = $now;
$modelobj->timemodified = $now;
$modelobj->usermodified = $admin->id;
$DB->insert_record('analytics_models', $modelobj);
}
$targetname = '\core\analytics\target\no_teaching';
if (!$DB->record_exists('analytics_models', array('target' => $targetname))) {
$modelobj = new stdClass();
$modelobj->enabled = 1;
$modelobj->trained = 1;
$modelobj->target = $targetname;
$modelobj->indicators = json_encode(array('\core_course\analytics\indicator\no_teacher'));
$modelobj->timesplitting = '\core\analytics\time_splitting\single_range';
$modelobj->version = $now;
$modelobj->timecreated = $now;
$modelobj->timemodified = $now;
$modelobj->usermodified = $admin->id;
$DB->insert_record('analytics_models', $modelobj);
}
// Main savepoint reached.
upgrade_main_savepoint(true, 2017072000.01);
upgrade_main_savepoint(true, 2017072000.02);
}
return true;
+1 -1
View File
@@ -29,7 +29,7 @@
defined('MOODLE_INTERNAL') || die();
$version = 2017072000.01; // YYYYMMDD = weekly release date of this DEV branch.
$version = 2017072000.02; // YYYYMMDD = weekly release date of this DEV branch.
// RR = release increments - 00 in DEV branches.
// .XX = incremental changes.