Merge branch 'MDL-61667-master-modelsinstall' of git://github.com/mudrd8mz/moodle

This commit is contained in:
Eloy Lafuente (stronk7)
2019-04-02 00:20:47 +02:00
18 changed files with 807 additions and 171 deletions
+164 -74
View File
@@ -40,6 +40,11 @@ class manager {
*/
const DEFAULT_MLBACKEND = '\mlbackend_php\processor';
/**
* Name of the file where components declare their models.
*/
const ANALYTICS_FILENAME = 'db/analytics.php';
/**
* @var \core_analytics\predictor[]
*/
@@ -107,7 +112,9 @@ class manager {
$params['trained'] = 1;
}
if ($predictioncontext) {
$conditions[] = "EXISTS (SELECT 'x' FROM {analytics_predictions} ap WHERE ap.modelid = am.id AND ap.contextid = :contextid)";
$conditions[] = "EXISTS (SELECT 'x'
FROM {analytics_predictions} ap
WHERE ap.modelid = am.id AND ap.contextid = :contextid)";
$params['contextid'] = $predictioncontext->id;
}
$sql .= ' WHERE ' . implode(' AND ', $conditions);
@@ -502,84 +509,16 @@ class manager {
}
/**
* Adds the models included with moodle core to the system.
* Used to be used to add models included with the Moodle core.
*
* @deprecated Deprecated since Moodle 3.7 (MDL-61667) - Use lib/db/analytics.php instead.
* @todo Remove this method in Moodle 4.1 (MDL-65186).
* @return void
*/
public static function add_builtin_models() {
$target = self::get_target('\core\analytics\target\course_dropout');
// Community of inquiry indicators.
$coiindicators = 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',
'\core_course\analytics\indicator\completion_enabled',
'\core_course\analytics\indicator\potential_cognitive_depth',
'\core_course\analytics\indicator\potential_social_breadth',
'\core\analytics\indicator\any_access_after_end',
'\core\analytics\indicator\any_access_before_start',
'\core\analytics\indicator\any_write_action_in_course',
'\core\analytics\indicator\read_actions',
);
$indicators = array();
foreach ($coiindicators as $coiindicator) {
$indicator = self::get_indicator($coiindicator);
$indicators[$indicator->get_id()] = $indicator;
}
if (!\core_analytics\model::exists($target, $indicators)) {
$model = \core_analytics\model::create($target, $indicators);
}
// No teaching model.
$target = self::get_target('\core\analytics\target\no_teaching');
$timesplittingmethod = '\core\analytics\time_splitting\single_range';
$noteacher = self::get_indicator('\core_course\analytics\indicator\no_teacher');
$nostudent = self::get_indicator('\core_course\analytics\indicator\no_student');
$indicators = array($noteacher->get_id() => $noteacher, $nostudent->get_id() => $nostudent);
if (!\core_analytics\model::exists($target, $indicators)) {
\core_analytics\model::create($target, $indicators, $timesplittingmethod);
}
debugging('core_analytics\manager::add_builtin_models() has been deprecated. Core models are now automatically '.
'updated according to their declaration in the lib/db/analytics.php file.', DEBUG_DEVELOPER);
}
/**
@@ -669,4 +608,155 @@ class manager {
return $classes;
}
/**
* Check that all the models declared by the component are up to date.
*
* This is intended to be called during the installation / upgrade to automatically create missing models.
*
* @param string $componentname The name of the component to load models for.
* @return array \core_analytics\model[] List of actually created models.
*/
public static function update_default_models_for_component(string $componentname): array {
$result = [];
foreach (static::load_default_models_for_component($componentname) as $definition) {
if (!\core_analytics\model::exists(static::get_target($definition['target']))) {
$result[] = static::create_declared_model($definition);
}
}
return $result;
}
/**
* Return the list of models declared by the given component.
*
* @param string $componentname The name of the component to load models for.
* @throws \coding_exception Exception thrown in case of invalid syntax.
* @return array The $models description array.
*/
public static function load_default_models_for_component(string $componentname): array {
$dir = \core_component::get_component_directory($componentname);
if (!$dir) {
// This is either an invalid component, or a core subsystem without its own root directory.
return [];
}
$file = $dir . '/' . self::ANALYTICS_FILENAME;
if (!is_readable($file)) {
return [];
}
$models = null;
include($file);
if (!isset($models) || !is_array($models) || empty($models)) {
return [];
}
foreach ($models as &$model) {
if (!isset($model['enabled'])) {
$model['enabled'] = false;
} else {
$model['enabled'] = clean_param($model['enabled'], PARAM_BOOL);
}
}
static::validate_models_declaration($models);
return $models;
}
/**
* Validate the declaration of prediction models according the syntax expected in the component's db folder.
*
* The expected structure looks like this:
*
* [
* [
* 'target' => '\fully\qualified\name\of\the\target\class',
* 'indicators' => [
* '\fully\qualified\name\of\the\first\indicator',
* '\fully\qualified\name\of\the\second\indicator',
* ],
* 'timesplitting' => '\optional\name\of\the\time_splitting\class',
* 'enabled' => true,
* ],
* ];
*
* @param array $models List of declared models.
* @throws \coding_exception Exception thrown in case of invalid syntax.
*/
public static function validate_models_declaration(array $models) {
foreach ($models as $model) {
if (!isset($model['target'])) {
throw new \coding_exception('Missing target declaration');
}
if (!static::is_valid($model['target'], '\core_analytics\local\target\base')) {
throw new \coding_exception('Invalid target classname', $model['target']);
}
if (empty($model['indicators']) || !is_array($model['indicators'])) {
throw new \coding_exception('Missing indicators declaration');
}
foreach ($model['indicators'] as $indicator) {
if (!static::is_valid($indicator, '\core_analytics\local\indicator\base')) {
throw new \coding_exception('Invalid indicator classname', $indicator);
}
}
if (isset($model['timesplitting'])) {
if (substr($model['timesplitting'], 0, 1) !== '\\') {
throw new \coding_exception('Expecting fully qualified time splitting classname', $model['timesplitting']);
}
if (!static::is_valid($model['timesplitting'], '\core_analytics\local\time_splitting\base')) {
throw new \coding_exception('Invalid time splitting classname', $model['timesplitting']);
}
}
if (!empty($model['enabled']) && !isset($model['timesplitting'])) {
throw new \coding_exception('Cannot enable a model without time splitting method specified');
}
}
}
/**
* Create the defined model.
*
* @param array $definition See {@link self::validate_models_declaration()} for the syntax.
* @return \core_analytics\model
*/
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;
}
if (isset($definition['timesplitting'])) {
$timesplitting = $definition['timesplitting'];
} else {
$timesplitting = false;
}
$created = \core_analytics\model::create($target, $indicators, $timesplitting);
if (!empty($definition['enabled'])) {
$created->enable();
}
return $created;
}
}
+18 -12
View File
@@ -346,8 +346,6 @@ class model {
$timesplittingid = false, $processor = null) {
global $USER, $DB;
\core_analytics\manager::check_can_manage_models();
$indicatorclasses = self::indicator_classes($indicators);
$now = time();
@@ -360,6 +358,20 @@ class model {
$modelobj->timemodified = $now;
$modelobj->usermodified = $USER->id;
if ($target->based_on_assumptions()) {
$modelobj->trained = 1;
}
if ($timesplittingid) {
if (!\core_analytics\manager::is_valid($timesplittingid, '\core_analytics\local\time_splitting\base')) {
throw new \moodle_exception('errorinvalidtimesplitting', 'analytics');
}
if (substr($timesplittingid, 0, 1) !== '\\') {
throw new \moodle_exception('errorinvalidtimesplitting', 'analytics');
}
$modelobj->timesplitting = $timesplittingid;
}
if ($processor &&
!manager::is_valid($processor, '\core_analytics\classifier') &&
!manager::is_valid($processor, '\core_analytics\regressor')) {
@@ -375,14 +387,6 @@ class model {
$model = new static($modelobj);
if ($timesplittingid) {
$model->enable($timesplittingid);
}
if ($model->is_static()) {
$model->mark_as_trained();
}
return $model;
}
@@ -401,6 +405,10 @@ class model {
$existingmodels = $DB->get_records('analytics_models', array('target' => $target->get_id()));
if (!$existingmodels) {
return false;
}
if (!$indicators && $existingmodels) {
return true;
}
@@ -1049,8 +1057,6 @@ class model {
public function enable($timesplittingid = false) {
global $DB, $USER;
\core_analytics\manager::check_can_manage_models();
$now = time();
if ($timesplittingid && $timesplittingid !== $this->model->timesplitting) {