Merge branch 'MDL-61667-master-modelsinstall' of git://github.com/mudrd8mz/moodle
This commit is contained in:
+164
-74
@@ -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
@@ -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) {
|
||||
|
||||
Reference in New Issue
Block a user