Merge branch 'MDL-60520_master' of git://github.com/dmonllao/moodle
This commit is contained in:
@@ -37,8 +37,8 @@ if ($hassiteconfig) {
|
||||
$predictors[$fullclassname] = new lang_string('pluginname', $pluginname);
|
||||
}
|
||||
$settings->add(new \core_analytics\admin_setting_predictor('analytics/predictionsprocessor',
|
||||
new lang_string('predictionsprocessor', 'analytics'), new lang_string('predictionsprocessor_help', 'analytics'),
|
||||
'\mlbackend_php\processor', $predictors)
|
||||
new lang_string('defaultpredictionsprocessor', 'analytics'), new lang_string('predictionsprocessor_help', 'analytics'),
|
||||
\core_analytics\manager::default_mlbackend(), $predictors)
|
||||
);
|
||||
|
||||
// Log store.
|
||||
|
||||
@@ -72,6 +72,22 @@ class edit_model extends \moodleform {
|
||||
$mform->addElement('select', 'timesplitting', get_string('timesplittingmethod', 'analytics'), $timesplittings);
|
||||
$mform->addHelpButton('timesplitting', 'timesplittingmethod', 'analytics');
|
||||
|
||||
$defaultprocessor = \core_analytics\manager::get_predictions_processor_name(
|
||||
\core_analytics\manager::get_predictions_processor()
|
||||
);
|
||||
$predictionprocessors = ['' => get_string('defaultpredictoroption', 'analytics', $defaultprocessor)];
|
||||
foreach ($this->_customdata['predictionprocessors'] as $classname => $predictionsprocessor) {
|
||||
if ($predictionsprocessor->is_ready() !== true) {
|
||||
continue;
|
||||
}
|
||||
$optionname = \tool_analytics\output\helper::class_to_option($classname);
|
||||
$predictionprocessors[$optionname] = \core_analytics\manager::get_predictions_processor_name($predictionsprocessor);
|
||||
}
|
||||
|
||||
$mform->addElement('select', 'predictionsprocessor', get_string('predictionsprocessor', 'analytics'),
|
||||
$predictionprocessors);
|
||||
$mform->addHelpButton('predictionsprocessor', 'predictionsprocessor', 'analytics');
|
||||
|
||||
$mform->addElement('hidden', 'id', $this->_customdata['id']);
|
||||
$mform->setType('id', PARAM_INT);
|
||||
|
||||
|
||||
@@ -110,7 +110,8 @@ switch ($action) {
|
||||
'id' => $model->get_id(),
|
||||
'model' => $model,
|
||||
'indicators' => $model->get_potential_indicators(),
|
||||
'timesplittings' => \core_analytics\manager::get_enabled_time_splitting_methods()
|
||||
'timesplittings' => \core_analytics\manager::get_enabled_time_splitting_methods(),
|
||||
'predictionprocessors' => \core_analytics\manager::get_all_prediction_processors()
|
||||
);
|
||||
$mform = new \tool_analytics\output\form\edit_model(null, $customdata);
|
||||
|
||||
@@ -126,7 +127,8 @@ switch ($action) {
|
||||
$indicators[] = \core_analytics\manager::get_indicator($indicatorclass);
|
||||
}
|
||||
$timesplitting = \tool_analytics\output\helper::option_to_class($data->timesplitting);
|
||||
$model->update($data->enabled, $indicators, $timesplitting);
|
||||
$predictionsprocessor = \tool_analytics\output\helper::option_to_class($data->predictionsprocessor);
|
||||
$model->update($data->enabled, $indicators, $timesplitting, $predictionsprocessor);
|
||||
redirect(new \moodle_url('/admin/tool/analytics/index.php'));
|
||||
}
|
||||
|
||||
@@ -137,6 +139,7 @@ switch ($action) {
|
||||
$callable = array('\tool_analytics\output\helper', 'class_to_option');
|
||||
$modelobj->indicators = array_map($callable, json_decode($modelobj->indicators));
|
||||
$modelobj->timesplitting = \tool_analytics\output\helper::class_to_option($modelobj->timesplitting);
|
||||
$modelobj->predictionsprocessor = \tool_analytics\output\helper::class_to_option($modelobj->predictionsprocessor);
|
||||
$mform->set_data($modelobj);
|
||||
$mform->display();
|
||||
break;
|
||||
|
||||
@@ -35,6 +35,11 @@ defined('MOODLE_INTERNAL') || die();
|
||||
*/
|
||||
class manager {
|
||||
|
||||
/**
|
||||
* Default mlbackend
|
||||
*/
|
||||
const DEFAULT_MLBACKEND = '\mlbackend_php\processor';
|
||||
|
||||
/**
|
||||
* @var \core_analytics\predictor[]
|
||||
*/
|
||||
@@ -117,9 +122,9 @@ class manager {
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the site selected predictions processor.
|
||||
* Returns the provided predictions processor class.
|
||||
*
|
||||
* @param string $predictionclass
|
||||
* @param false|string $predictionclass Returns the system default processor if false
|
||||
* @param bool $checkisready
|
||||
* @return \core_analytics\predictor
|
||||
*/
|
||||
@@ -128,13 +133,13 @@ class manager {
|
||||
// We want 0 or 1 so we can use it as an array key for caching.
|
||||
$checkisready = intval($checkisready);
|
||||
|
||||
if ($predictionclass === false) {
|
||||
if (!$predictionclass) {
|
||||
$predictionclass = get_config('analytics', 'predictionsprocessor');
|
||||
}
|
||||
|
||||
if (empty($predictionclass)) {
|
||||
// Use the default one if nothing set.
|
||||
$predictionclass = '\mlbackend_php\processor';
|
||||
$predictionclass = self::default_mlbackend();
|
||||
}
|
||||
|
||||
if (!class_exists($predictionclass)) {
|
||||
@@ -179,6 +184,44 @@ class manager {
|
||||
return $predictionprocessors;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name of the provided predictions processor.
|
||||
*
|
||||
* @param \core_analytics\predictor $predictionsprocessor
|
||||
* @return string
|
||||
*/
|
||||
public static function get_predictions_processor_name(\core_analytics\predictor $predictionsprocessor) {
|
||||
$component = substr(get_class($predictionsprocessor), 0, strpos(get_class($predictionsprocessor), '\\', 1));
|
||||
return get_string('pluginname', $component);
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether the provided plugin is used by any model.
|
||||
*
|
||||
* @param string $plugin
|
||||
* @return bool
|
||||
*/
|
||||
public static function is_mlbackend_used($plugin) {
|
||||
$models = self::get_all_models();
|
||||
foreach ($models as $model) {
|
||||
$processor = $model->get_predictions_processor();
|
||||
$noprefixnamespace = ltrim(get_class($processor), '\\');
|
||||
$processorplugin = substr($noprefixnamespace, 0, strpos($noprefixnamespace, '\\'));
|
||||
if ($processorplugin == $plugin) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
// Default predictions processor.
|
||||
$defaultprocessorclass = get_config('analytics', 'predictionsprocessor');
|
||||
$pluginclass = '\\' . $plugin . '\\processor';
|
||||
if ($pluginclass === $defaultprocessorclass) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get all available time splitting methods.
|
||||
*
|
||||
@@ -546,6 +589,15 @@ class manager {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Default system backend.
|
||||
*
|
||||
* @return string
|
||||
*/
|
||||
public static function default_mlbackend() {
|
||||
return self::DEFAULT_MLBACKEND;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the provided element classes in the site.
|
||||
*
|
||||
|
||||
+54
-10
@@ -110,6 +110,11 @@ class model {
|
||||
*/
|
||||
protected $target = null;
|
||||
|
||||
/**
|
||||
* @var \core_analytics\predictor
|
||||
*/
|
||||
protected $predictionsprocessor = null;
|
||||
|
||||
/**
|
||||
* @var \core_analytics\local\indicator\base[]
|
||||
*/
|
||||
@@ -336,7 +341,8 @@ class model {
|
||||
* @param string $timesplittingid The time splitting method id (its fully qualified class name)
|
||||
* @return \core_analytics\model
|
||||
*/
|
||||
public static function create(\core_analytics\local\target\base $target, array $indicators, $timesplittingid = false) {
|
||||
public static function create(\core_analytics\local\target\base $target, array $indicators,
|
||||
$timesplittingid = false, $processor = false) {
|
||||
global $USER, $DB;
|
||||
|
||||
\core_analytics\manager::check_can_manage_models();
|
||||
@@ -353,6 +359,14 @@ class model {
|
||||
$modelobj->timemodified = $now;
|
||||
$modelobj->usermodified = $USER->id;
|
||||
|
||||
if ($processor &&
|
||||
!self::is_valid($processor, '\core_analytics\classifier') &&
|
||||
!self::is_valid($processor, '\core_analytics\regressor')) {
|
||||
throw new \coding_exception('The provided predictions processor \\' . $processor . '\processor is not valid');
|
||||
} else {
|
||||
$modelobj->predictionsprocessor = $processor;
|
||||
}
|
||||
|
||||
$id = $DB->insert_record('analytics_models', $modelobj);
|
||||
|
||||
// Get db defaults.
|
||||
@@ -411,9 +425,10 @@ class model {
|
||||
* @param int|bool $enabled
|
||||
* @param \core_analytics\local\indicator\base[]|false $indicators False to respect current indicators
|
||||
* @param string|false $timesplittingid False to respect current time splitting method
|
||||
* @param string|false $predictionsprocessor False to respect current predictors processor value
|
||||
* @return void
|
||||
*/
|
||||
public function update($enabled, $indicators = false, $timesplittingid = '') {
|
||||
public function update($enabled, $indicators = false, $timesplittingid = '', $predictionsprocessor = false) {
|
||||
global $USER, $DB;
|
||||
|
||||
\core_analytics\manager::check_can_manage_models();
|
||||
@@ -433,8 +448,14 @@ class model {
|
||||
$timesplittingid = $this->model->timesplitting;
|
||||
}
|
||||
|
||||
if ($predictionsprocessor === false) {
|
||||
// Respect current value.
|
||||
$predictionsprocessor = $this->model->predictionsprocessor;
|
||||
}
|
||||
|
||||
if ($this->model->timesplitting !== $timesplittingid ||
|
||||
$this->model->indicators !== $indicatorsstr) {
|
||||
$this->model->indicators !== $indicatorsstr ||
|
||||
$this->model->predictionsprocessor !== $predictionsprocessor) {
|
||||
|
||||
// Delete generated predictions before changing the model version.
|
||||
$this->clear();
|
||||
@@ -458,6 +479,7 @@ class model {
|
||||
$this->model->enabled = intval($enabled);
|
||||
$this->model->indicators = $indicatorsstr;
|
||||
$this->model->timesplitting = $timesplittingid;
|
||||
$this->model->predictionsprocessor = $predictionsprocessor;
|
||||
$this->model->timemodified = $now;
|
||||
$this->model->usermodified = $USER->id;
|
||||
|
||||
@@ -477,8 +499,14 @@ class model {
|
||||
$this->clear();
|
||||
|
||||
// Method self::clear is already clearing the current model version.
|
||||
$predictor = \core_analytics\manager::get_predictions_processor();
|
||||
$predictor->delete_output_dir($this->get_output_dir(array(), true));
|
||||
$predictor = $this->get_predictions_processor(false);
|
||||
if ($predictor->is_ready() !== true) {
|
||||
$predictorname = \core_analytics\manager::get_predictions_processor_name($predictor);
|
||||
debugging('Prediction processor ' . $predictorname . ' is not ready to be used. Model ' .
|
||||
$this->model->id . ' could not be deleted.');
|
||||
} else {
|
||||
$predictor->delete_output_dir($this->get_output_dir(array(), true));
|
||||
}
|
||||
|
||||
$DB->delete_records('analytics_models', array('id' => $this->model->id));
|
||||
$DB->delete_records('analytics_models_log', array('modelid' => $this->model->id));
|
||||
@@ -516,7 +544,7 @@ class model {
|
||||
$this->heavy_duty_mode();
|
||||
|
||||
// Before get_labelled_data call so we get an early exception if it is not ready.
|
||||
$predictor = \core_analytics\manager::get_predictions_processor();
|
||||
$predictor = $this->get_predictions_processor();
|
||||
|
||||
$datasets = $this->get_analyser()->get_labelled_data();
|
||||
|
||||
@@ -608,7 +636,7 @@ class model {
|
||||
$outputdir = $this->get_output_dir(array('execution'));
|
||||
|
||||
// Before get_labelled_data call so we get an early exception if it is not ready.
|
||||
$predictor = \core_analytics\manager::get_predictions_processor();
|
||||
$predictor = $this->get_predictions_processor();
|
||||
|
||||
$datasets = $this->get_analyser()->get_labelled_data();
|
||||
|
||||
@@ -677,7 +705,7 @@ class model {
|
||||
|
||||
// Before get_unlabelled_data call so we get an early exception if it is not ready.
|
||||
if (!$this->is_static()) {
|
||||
$predictor = \core_analytics\manager::get_predictions_processor();
|
||||
$predictor = $this->get_predictions_processor();
|
||||
}
|
||||
|
||||
$samplesdata = $this->get_analyser()->get_unlabelled_data();
|
||||
@@ -738,6 +766,16 @@ class model {
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the model predictions processor.
|
||||
*
|
||||
* @param bool $checkisready
|
||||
* @return \core_analytics\predictor
|
||||
*/
|
||||
public function get_predictions_processor($checkisready = true) {
|
||||
return manager::get_predictions_processor($this->model->predictionsprocessor, $checkisready);
|
||||
}
|
||||
|
||||
/**
|
||||
* Formats the predictor results.
|
||||
*
|
||||
@@ -1457,8 +1495,14 @@ class model {
|
||||
\core_analytics\manager::check_can_manage_models();
|
||||
|
||||
// Delete current model version stored stuff.
|
||||
$predictor = \core_analytics\manager::get_predictions_processor();
|
||||
$predictor->clear_model($this->get_unique_id(), $this->get_output_dir());
|
||||
$predictor = $this->get_predictions_processor(false);
|
||||
if ($predictor->is_ready() !== true) {
|
||||
$predictorname = \core_analytics\manager::get_predictions_processor_name($predictor);
|
||||
debugging('Prediction processor ' . $predictorname . ' is not ready to be used. Model ' .
|
||||
$this->model->id . ' could not be cleared.');
|
||||
} else {
|
||||
$predictor->clear_model($this->get_unique_id(), $this->get_output_dir());
|
||||
}
|
||||
|
||||
$predictionids = $DB->get_fieldset_select('analytics_predictions', 'id', 'modelid = :modelid',
|
||||
array('modelid' => $this->get_id()));
|
||||
|
||||
@@ -143,10 +143,8 @@ class core_analytics_prediction_testcase extends advanced_testcase {
|
||||
$this->markTestSkipped('Skipping ' . $predictionsprocessorclass . ' as the predictor is not ready.');
|
||||
}
|
||||
|
||||
set_config('predictionsprocessor', $predictionsprocessorclass, 'analytics');
|
||||
|
||||
$model = $this->add_perfect_model();
|
||||
$model->enable($timesplittingid);
|
||||
$model->update(true, false, $timesplittingid, get_class($predictionsprocessor));
|
||||
|
||||
// No samples trained yet.
|
||||
$this->assertEquals(0, $DB->count_records('analytics_train_samples', array('modelid' => $model->get_id())));
|
||||
@@ -423,8 +421,7 @@ class core_analytics_prediction_testcase extends advanced_testcase {
|
||||
$this->markTestSkipped('Skipping ' . $predictionsprocessorclass . ' as the predictor is not ready.');
|
||||
}
|
||||
|
||||
set_config('predictionsprocessor', $predictionsprocessorclass, 'analytics');
|
||||
|
||||
$model->update(false, false, false, get_class($predictionsprocessor));
|
||||
$results = $model->evaluate();
|
||||
|
||||
// We check that the returned status includes at least $expectedcode code.
|
||||
|
||||
@@ -31,6 +31,8 @@ $string['analyticslogstore_help'] = 'The log store that will be used by the anal
|
||||
$string['analyticssettings'] = 'Analytics settings';
|
||||
$string['coursetoolong'] = 'The course is too long';
|
||||
$string['enabledtimesplittings'] = 'Time splitting methods';
|
||||
$string['defaultpredictionsprocessor'] = 'Default predictions processor';
|
||||
$string['defaultpredictoroption'] = 'Default processor ({$a})';
|
||||
$string['disabledmodel'] = 'Disabled model';
|
||||
$string['erroralreadypredict'] = 'File {$a} has already been used to generate predictions.';
|
||||
$string['errorcannotreaddataset'] = 'Dataset file {$a} can not be read';
|
||||
@@ -82,7 +84,7 @@ $string['novalidsamples'] = 'No valid samples available';
|
||||
$string['onlycli'] = 'Analytics processes execution via command line only';
|
||||
$string['onlycliinfo'] = 'Analytics processes like evaluating models, training machine learning algorithms or getting predictions can take some time, they will run as cron tasks and they can be forced via command line. Disable this setting if you want your site managers to be able to run these processes manually via web interface';
|
||||
$string['predictionsprocessor'] = 'Predictions processor';
|
||||
$string['predictionsprocessor_help'] = 'A predictions processor is the machine-learning backend that processes the datasets generated by calculating models\' indicators and targets. All trained algorithms and predictions will be deleted if you change to another predictions processor.';
|
||||
$string['predictionsprocessor_help'] = 'A predictions processor is the machine-learning backend that processes the datasets generated by calculating models\' indicators and targets. Each model can use a different processor, the one specified here will be the default value.';
|
||||
$string['privacy:metadata:analytics:indicatorcalc'] = 'Indicator calculations';
|
||||
$string['privacy:metadata:analytics:indicatorcalc:starttime'] = 'Calculation start time';
|
||||
$string['privacy:metadata:analytics:indicatorcalc:endtime'] = 'Calculation end time';
|
||||
|
||||
@@ -40,7 +40,8 @@ class mlbackend extends base {
|
||||
* @return bool
|
||||
*/
|
||||
public function is_uninstall_allowed() {
|
||||
return true;
|
||||
|
||||
return !\core_analytics\manager::is_mlbackend_used('mlbackend_' . $this->name);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
+3
-2
@@ -1,5 +1,5 @@
|
||||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<XMLDB PATH="lib/db" VERSION="20181018" COMMENT="XMLDB file for core Moodle tables"
|
||||
<XMLDB PATH="lib/db" VERSION="20181022" COMMENT="XMLDB file for core Moodle tables"
|
||||
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
|
||||
xsi:noNamespaceSchemaLocation="../../lib/xmldb/xmldb.xsd"
|
||||
>
|
||||
@@ -3729,6 +3729,7 @@
|
||||
<FIELD NAME="target" TYPE="char" LENGTH="255" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="indicators" TYPE="text" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="timesplitting" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="predictionsprocessor" TYPE="char" LENGTH="255" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="version" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
|
||||
<FIELD NAME="timecreated" TYPE="int" LENGTH="10" NOTNULL="false" SEQUENCE="false"/>
|
||||
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
|
||||
@@ -3931,4 +3932,4 @@
|
||||
</INDEXES>
|
||||
</TABLE>
|
||||
</TABLES>
|
||||
</XMLDB>
|
||||
</XMLDB>
|
||||
|
||||
@@ -2652,5 +2652,19 @@ function xmldb_main_upgrade($oldversion) {
|
||||
upgrade_main_savepoint(true, 2018102300.02);
|
||||
}
|
||||
|
||||
if ($oldversion < 2018102900.00) {
|
||||
// Define field predictionsprocessor to be added to analytics_models.
|
||||
$table = new xmldb_table('analytics_models');
|
||||
$field = new xmldb_field('predictionsprocessor', XMLDB_TYPE_CHAR, '255', null, null, null, null, 'timesplitting');
|
||||
|
||||
// Conditionally launch add field predictionsprocessor.
|
||||
if (!$dbman->field_exists($table, $field)) {
|
||||
$dbman->add_field($table, $field);
|
||||
}
|
||||
|
||||
// Main savepoint reached.
|
||||
upgrade_main_savepoint(true, 2018102900.00);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
+1
-1
@@ -29,7 +29,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$version = 2018102700.00; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
$version = 2018102900.00; // YYYYMMDD = weekly release date of this DEV branch.
|
||||
// RR = release increments - 00 in DEV branches.
|
||||
// .XX = incremental changes.
|
||||
|
||||
|
||||
Reference in New Issue
Block a user