MDL-57791 analytics: Second review round

This commit is contained in:
David Monllao
2017-07-24 08:36:50 +02:00
parent 413f19bc49
commit 1cc2b4bac3
38 changed files with 204 additions and 278 deletions
+5
View File
@@ -76,6 +76,11 @@ if ($hassiteconfig) {
// Predictions processor output dir.
$defaultmodeloutputdir = rtrim($CFG->dataroot, '/') . DIRECTORY_SEPARATOR . 'models';
if (empty(get_config('analytics', 'modeloutputdir')) && !file_exists($defaultmodeloutputdir) &&
is_writable($defaultmodeloutputdir)) {
// Automatically create the dir for them so users don't see the invalid value red cross.
mkdir($defaultmodeloutputdir, $CFG->directorypermissions, true);
}
$settings->add(new admin_setting_configdirectory('analytics/modeloutputdir', new lang_string('modeloutputdir', 'analytics'),
new lang_string('modeloutputdirinfo', 'analytics'), $defaultmodeloutputdir));
}
+22 -7
View File
@@ -1,20 +1,35 @@
/*
// 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/>.
/**
* Shows a dialogue with info about this logs.
*
* @module tool_models/log_info
* @class log_info
* @package tool_models
* @copyright 2017 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* @module tool_models/log_info
*/
define(['jquery', 'core/str', 'core/modal_factory', 'core/notification'], function($, str, ModalFactory, Notification) {
return {
return /** @alias module:tool_models/log_info */ {
/**
* Prepares a modal info for a log's results.
*
* @access public
* @method loadInfo
* @param {int} id
* @param {string[]} info
*/
@@ -135,6 +135,11 @@ class course_dropout extends \core_analytics\local\target\binary {
return get_string('nocoursesections', 'tool_models');
}
if ($course->get_start() == 0) {
// We require time start to be set.
return get_string('nocoursestarttime', 'tool_models');
}
if ($course->get_end() == 0) {
// We require time end to be set.
return get_string('nocourseendtime', 'tool_models');
@@ -61,7 +61,6 @@ class no_teaching extends \core_analytics\local\target\binary {
* @return \core_analytics\prediction_action[]
*/
public function prediction_actions(\core_analytics\prediction $prediction, $includedetailsaction = false) {
global $USER;
// No need to call the parent as the parent's action is view details and this target only have 1 feature.
$actions = array();
@@ -78,7 +78,7 @@ class model_logs extends \table_sql {
/**
* Generate the version column.
*
* @param stdClass $log log data.
* @param \stdClass $log log data.
* @return string HTML for the version column
*/
public function col_version($log) {
@@ -89,7 +89,7 @@ class model_logs extends \table_sql {
/**
* Generate the time column.
*
* @param stdClass $log log data.
* @param \stdClass $log log data.
* @return string HTML for the time column
*/
public function col_time($log) {
@@ -100,7 +100,7 @@ class model_logs extends \table_sql {
/**
* Generate the indicators column.
*
* @param stdClass $log log data.
* @param \stdClass $log log data.
* @return string HTML for the indicators column
*/
public function col_indicators($log) {
@@ -120,7 +120,7 @@ class model_logs extends \table_sql {
/**
* Generate the context column.
*
* @param stdClass $log log data.
* @param \stdClass $log log data.
* @return string HTML for the context column
*/
public function col_timesplitting($log) {
@@ -131,7 +131,7 @@ class model_logs extends \table_sql {
/**
* Generate the accuracy column.
*
* @param stdClass $log log data.
* @param \stdClass $log log data.
* @return string HTML for the accuracy column
*/
public function col_accuracy($log) {
@@ -141,7 +141,7 @@ class model_logs extends \table_sql {
/**
* Generate the info column.
*
* @param stdClass $log log data.
* @param \stdClass $log log data.
* @return string HTML for the score column
*/
public function col_info($log) {
@@ -165,7 +165,7 @@ class model_logs extends \table_sql {
/**
* Generate the usermodified column.
*
* @param stdClass $log log data.
* @param \stdClass $log log data.
* @return string HTML for the usermodified column
*/
public function col_usermodified($log) {
@@ -42,11 +42,11 @@ class renderer extends plugin_renderer_base {
/**
* Defer to template.
*
* @param templatable $renderable
* @param templatable $templatable
* @return string HTML
*/
protected function render_models_list(templatable $renderable) {
$data = $renderable->export_for_template($this);
protected function render_models_list(templatable $templatable) {
$data = $templatable->export_for_template($this);
return parent::render_from_template('tool_models/models_list', $data);
}
@@ -140,13 +140,13 @@ class renderer extends plugin_renderer_base {
/**
* Web interface training & prediction results.
*
* @param array $trainresults
* @param \stdClass|false $trainresults
* @param string[] $trainlogs
* @param array $predictresults
* @param \stdClass|false $predictresults
* @param string[] $predictlogs
* @return string HTML
*/
public function render_getpredictions_results($trainresults = false, $trainlogs = array(), $predictresults = false, $predictlogs = array()) {
public function render_get_predictions_results($trainresults = false, $trainlogs = array(), $predictresults = false, $predictlogs = array()) {
global $OUTPUT;
$output = '';
@@ -163,7 +163,7 @@ class renderer extends plugin_renderer_base {
$output .= $OUTPUT->notification(get_string('nodatatotrain', 'tool_models'),
\core\output\notification::NOTIFY_WARNING);
} else {
$output .= $OUTPUT->notification(get_string('generalerror', 'analytics', $result->status),
$output .= $OUTPUT->notification(get_string('generalerror', 'analytics', $trainresults->status),
\core\output\notification::NOTIFY_ERROR);
}
}
@@ -187,7 +187,7 @@ class renderer extends plugin_renderer_base {
$output .= $OUTPUT->notification(get_string('nodatatopredict', 'tool_models'),
\core\output\notification::NOTIFY_WARNING);
} else {
$output .= $OUTPUT->notification(get_string('generalerror', 'analytics', $result->status),
$output .= $OUTPUT->notification(get_string('generalerror', 'analytics', $predictresults->status),
\core\output\notification::NOTIFY_ERROR);
}
}
@@ -63,7 +63,7 @@ class predict_models extends \core\task\scheduled_task {
if ($result) {
echo $OUTPUT->heading(get_string('modelresults', 'tool_models', $model->get_target()->get_name()));
$renderer = $PAGE->get_renderer('tool_models');
echo $renderer->render_getpredictions_results(false, array(), $result, $model->get_analyser()->get_logs());
echo $renderer->render_get_predictions_results(false, array(), $result, $model->get_analyser()->get_logs());
}
}
@@ -75,7 +75,7 @@ class train_models extends \core\task\scheduled_task {
echo $OUTPUT->heading(get_string('modelresults', 'tool_models', $model->get_target()->get_name()));
$renderer = $PAGE->get_renderer('tool_models');
echo $renderer->render_getpredictions_results($result, $model->get_analyser()->get_logs());
echo $renderer->render_get_predictions_results($result, $model->get_analyser()->get_logs());
}
}
}
+1 -1
View File
@@ -107,7 +107,7 @@ if (!empty($validdatasets) && !$model->is_enabled() && $options['non-interactive
// Select a dataset, train and enable the model.
$input = cli_input(get_string('clienablemodel', 'tool_models'));
while (!\core_analytics\manager::is_valid($input, '\core_analytics\local\time_splitting\base') && $input !== 'none') {
mtrace(get_string('errorunexistingtimesplitting', 'tool_models'));
mtrace(get_string('errorunexistingtimesplitting', 'analytics'));
$input = cli_input(get_string('clienablemodel', 'tool_models'));
}
@@ -199,9 +199,9 @@ function tool_models_calculate_course_dates($course, $options) {
}
echo mtrace($notification);
mtrace($notification);
}
echo mtrace(get_string('success'));
mtrace(get_string('success'));
exit(0);
+52 -4
View File
@@ -33,14 +33,62 @@ function xmldb_tool_models_install() {
// Students at risk of dropping out of courses.
$target = \core_analytics\manager::get_target('\tool_models\analytics\target\course_dropout');
// Community of inquiry indicators.
$indicators = 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',
);
array_walk($indicators, function(&$indicator) {
$indicator = \core_analytics\manager::get_indicator($indicator);
});
// We need the model to be created in order to know all its potential indicators and set them.
$model = \core_analytics\model::create($target, array());
// TODO All of them for the moment, we will define a limited set of them once in core.
$model->update(0, $model->get_potential_indicators());
// Course without teachers.
$target = \core_analytics\manager::get_target('\tool_models\analytics\target\no_teaching');
$weekbeforestart = '\core_analytics\local\time_splitting\single_range';
$timesplittingmethod = '\core_analytics\local\time_splitting\single_range';
$noteacher = \core_analytics\manager::get_indicator('\core_course\analytics\indicator\no_teacher');
\core_analytics\model::create($target, array($noteacher->get_id() => $noteacher), $weekbeforestart);
\core_analytics\model::create($target, array($noteacher->get_id() => $noteacher), $timesplittingmethod);
}
+2 -2
View File
@@ -31,7 +31,7 @@ $models = \core_analytics\manager::get_all_models();
echo $OUTPUT->header();
$renderable = new \tool_models\output\models_list($models);
echo $PAGE->get_renderer('tool_models')->render($renderable);
$templatable = new \tool_models\output\models_list($models);
echo $PAGE->get_renderer('tool_models')->render($templatable);
echo $OUTPUT->footer();
@@ -23,7 +23,6 @@
*/
$string['accuracy'] = 'Accuracy';
$string['allindicators'] = 'All indicators';
$string['allpredictions'] = 'All predictions';
$string['analysingsitedata'] = 'Analysing the site';
$string['analyticmodels'] = 'Analytic models';
@@ -60,11 +59,9 @@ $string['labelstudentdropoutno'] = 'Not at risk';
$string['labelteachingyes'] = 'Users with teaching capabilities have access to the course';
$string['labelteachingno'] = 'No teaching';
$string['loginfo'] = 'Log extra info';
$string['lowaccuracy'] = 'The model accuracy is low';
$string['modelresults'] = '{$a} results';
$string['modelslist'] = 'Models list';
$string['modeltimesplitting'] = 'Time splitting';
$string['nocompletiondetection'] = 'No method available to detect course completion (no completion nor competencies nor course grade pass)';
$string['nocourseactivity'] = 'Not enough course activity between the start and the end of the course';
$string['nocourseendtime'] = 'The course does not have an end time';
$string['nocoursesections'] = 'No course sections';
@@ -84,8 +81,6 @@ $string['sameenddate'] = 'Current end date is good';
$string['target'] = 'Target';
$string['target:coursedropout'] = 'Students at risk of dropping out';
$string['target:noteachingactivity'] = 'No teaching';
$string['target:coursedropoutinfo'] = 'Here you can find a list of students at risk of dropping out.';
$string['timemodified'] = 'Last modification';
$string['trainingprocessfinished'] = 'Training process finished';
$string['trainingresults'] = 'Training results';
$string['trainmodels'] = 'Train models';
+2 -2
View File
@@ -52,7 +52,7 @@ switch ($action) {
$title = get_string('viewlog', 'tool_models');
break;
default:
throw new moodle_exception('errorunknownaction', 'tool_models');
throw new moodle_exception('errorunknownaction', 'analytics');
}
$PAGE->set_context($context);
@@ -133,7 +133,7 @@ switch ($action) {
$predictlogs = $model->get_analyser()->get_logs();
$renderer = $PAGE->get_renderer('tool_models');
echo $renderer->render_getpredictions_results($trainresults, $trainlogs, $predictresults, $predictlogs);
echo $renderer->render_get_predictions_results($trainresults, $trainlogs, $predictresults, $predictlogs);
break;
case 'log':
@@ -43,7 +43,10 @@
"timesplitting": "Quarters",
"noinsights": "No insights available yet"
}
]
],
"warnings": {
"message": "Hey, this is a warning"
}
}
}}
+3 -2
View File
@@ -135,7 +135,7 @@ abstract class calculable {
*
* @param string $elementname
* @param int $sampleid
* @return \stdClass
* @return \stdClass|false An \stdClass object or false if it can not be found.
*/
protected function retrieve($elementname, $sampleid) {
if (empty($this->sampledata[$sampleid]) || empty($this->sampledata[$sampleid][$elementname])) {
@@ -193,9 +193,10 @@ abstract class calculable {
* - gt as 'greater than'
* - ge as 'greater or equal than'
*
* @throws \coding_exception
* @param int|float $value
* @param array $ranges e.g. [ ['lt', 20], ['ge', 20] ]
* @return void
* @return float
*/
protected function classify_value($value, $ranges) {
-1
View File
@@ -196,7 +196,6 @@ class course implements \core_analytics\analysable {
* @return int Timestamp or 0 if has not started yet.
*/
public function get_start() {
global $DB;
if ($this->starttime !== null) {
return $this->starttime;
-1
View File
@@ -195,7 +195,6 @@ class dataset_manager {
* @return bool
*/
public static function delete_previous_evaluation_file($modelid, $timesplittingid) {
$fs = get_file_storage();
if ($file = self::get_previous_evaluation_file($modelid, $timesplittingid)) {
$file->delete();
return true;
@@ -398,8 +398,6 @@ abstract class base {
return $result;
}
// TODO We may be interested in limiting $samplesdata contents to $sampleids after filtering out some sampleids.
// Only when processing data for predictions.
if ($target === false) {
// We also filter out ranges that have already been used for predictions.
@@ -573,7 +571,6 @@ abstract class base {
$trainingsamples->timesplitting = $timesplitting->get_id();
$trainingsamples->fileid = $file->get_id();
// TODO We just need the keys, we can save some space by removing the values.
$trainingsamples->sampleids = json_encode($sampleids);
$trainingsamples->timecreated = time();
@@ -73,8 +73,6 @@ abstract class by_course extends base {
*/
public function get_analysable_data($includetarget) {
$status = array();
$messages = array();
$filesbytimesplitting = array();
// This class and all children will iterate through a list of courses (\core_analytics\course).
@@ -80,7 +80,6 @@ class courses extends by_course {
* @return array
*/
protected function get_all_samples(\core_analytics\analysable $course) {
global $DB;
$context = \context_course::instance($course->get_id());
@@ -46,8 +46,6 @@ abstract class sitewide extends base {
// Here there is a single analysable and it is the system.
$analysable = new \core_analytics\site();
$return = array();
$files = $this->process_analysable($analysable, $includetarget);
// Copy to range files as there is just one analysable.
@@ -477,7 +477,7 @@ abstract class community_of_inquiry_activity extends linear {
}
/**
* cognitive_calculate_sample
* Calculates the cognitive depth of a sample.
*
* @param int $sampleid
* @param string $tablename
@@ -566,7 +566,7 @@ abstract class community_of_inquiry_activity extends linear {
}
/**
* social_calculate_sample
* Calculates the social breadth of a sample.
*
* @param int $sampleid
* @param string $tablename
@@ -595,7 +595,6 @@ abstract class community_of_inquiry_activity extends linear {
throw new \coding_exception('Activities\' potential social breadth go from 1 to 2.');
}
$scoreperlevel = $scoreperactivity / $potentiallevel;
// TODO Add support for other levels than 2.
switch ($potentiallevel) {
case 2:
// Social breadth level 2 is to view feedback. (Same as cognitive level 3).
@@ -1,99 +0,0 @@
<?php
// 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/>.
/**
* User profile set indicator.
*
* @package core_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_analytics\local\indicator;
defined('MOODLE_INTERNAL') || die();
/**
* User profile set indicator.
*
* @package core_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_profile_set extends linear {
/**
* get_name
*
* @return string
*/
public static function get_name() {
return get_string('indicator:completeduserprofile', 'analytics');
}
/**
* required_sample_data
*
* @return string[]
*/
public static function required_sample_data() {
return array('user');
}
/**
* calculate_sample
*
* @param int $sampleid
* @param string $sampleorigin
* @param int $starttime
* @param int $endtime
* @return float
*/
protected function calculate_sample($sampleid, $sampleorigin, $starttime = false, $endtime = false) {
global $CFG;
$user = $this->retrieve('user', $sampleid);
// Nothing set results in -1.
$calculatedvalue = self::MIN_VALUE;
if (!empty($CFG->sitepolicy) && !$user->policyagreed) {
return self::MIN_VALUE;
}
if (!$user->confirmed) {
return self::MIN_VALUE;
}
if ($user->description != '') {
$calculatedvalue += 1;
}
if ($user->picture != '') {
$calculatedvalue += 1;
}
// 0.2 for any of the following fields being set (some of them may even be compulsory or have a default).
$fields = array('institution', 'department', 'address', 'city', 'country', 'url');
foreach ($fields as $fieldname) {
if ($user->{$fieldname} != '') {
$calculatedvalue += 0.2;
}
}
return $this->limit_value($calculatedvalue);
}
}
@@ -1,69 +0,0 @@
<?php
// 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/>.
/**
* User tracks forums indicator.
*
* @package core_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace core_analytics\local\indicator;
defined('MOODLE_INTERNAL') || die();
/**
* User tracks forums indicator.
*
* @package core_analytics
* @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class user_track_forums extends binary {
/**
* get_name
*
* @return string
*/
public static function get_name() {
return get_string('indicator:userforumstracking', 'analytics');
}
/**
* required_sample_data
*
* @return string[]
*/
public static function required_sample_data() {
return array('user');
}
/**
* calculate_sample
*
* @param int $sampleid
* @param string $samplesorigin
* @param int $starttime
* @param int $endtime
* @return float
*/
protected function calculate_sample($sampleid, $samplesorigin, $starttime = false, $endtime = false) {
$user = $this->retrieve('user', $sampleid);
return ($user->trackforums) ? self::get_max_value() : self::get_min_value();
}
}
-7
View File
@@ -150,7 +150,6 @@ abstract class base extends \core_analytics\calculable {
* @return void
*/
public function generate_insight_notifications($modelid, $samplecontexts) {
global $CFG;
foreach ($samplecontexts as $context) {
@@ -256,12 +255,6 @@ abstract class base extends \core_analytics\calculable {
return false;
}
if (!$this->is_linear()) {
if (in_array($predictedvalue, $this->ignored_predicted_classes())) {
return false;
}
}
return true;
}
@@ -148,4 +148,26 @@ abstract class discrete extends base {
throw new \coding_exception('Overwrite ignored_predicted_classes() and return an array with the classes that triggers ' .
'the callback');
}
/**
* Should the model callback be triggered?
*
* @param mixed $predictedvalue
* @param float $predictionscore
* @return bool
*/
public function triggers_callback($predictedvalue, $predictionscore) {
if (!parent::triggers_callback($predictedvalue, $predictionscore)) {
return false;
}
if (!$this->is_linear()) {
if (in_array($predictedvalue, $this->ignored_predicted_classes())) {
return false;
}
}
return true;
}
}
@@ -62,7 +62,7 @@ abstract class base {
protected $ranges = [];
/**
* @var \core_analytics\indicator\base
* @var \core_analytics\local\indicator\base
*/
protected static $indicators = [];
@@ -147,7 +147,7 @@ abstract class base {
* @param \core_analytics\local\indicator\base[] $indicators
* @param array $ranges
* @param \core_analytics\local\target\base $target
* @return array
* @return array|bool
*/
public function calculate(&$sampleids, $samplesorigin, $indicators, $ranges, $target = false) {
@@ -324,10 +324,10 @@ abstract class base {
}
/**
* get_range_by_index
* Returns range data by its index.
*
* @param int $rangeindex
* @return array
* @return array|false Range data or false if the index is not part of the existing ranges.
*/
public function get_range_by_index($rangeindex) {
if (!isset($this->ranges[$rangeindex])) {
+7 -1
View File
@@ -177,7 +177,7 @@ class manager {
/**
* Get all available time splitting methods.
*
* @return \core_analytics\time_splitting\base[]
* @return \core_analytics\local\time_splitting\base[]
*/
public static function get_all_time_splittings() {
if (self::$alltimesplittings !== null) {
@@ -393,6 +393,12 @@ class manager {
$classes += \core_component::get_component_classes_in_namespace($frankenstyle, 'analytics\\' . $element);
}
}
foreach (\core_component::get_core_subsystems() as $subsystemname => $unusedsubsystempath) {
$componentname = 'core_' . $subsystemname;
$classes += \core_component::get_component_classes_in_namespace($componentname, 'analytics\\' . $element);
}
return $classes;
}
}
+46 -29
View File
@@ -118,9 +118,9 @@ class model {
protected $uniqueid = null;
/**
* __construct
* Constructor.
*
* @param int|stdClass $model
* @param int|\stdClass $model
* @return void
*/
public function __construct($model) {
@@ -136,7 +136,7 @@ class model {
}
/**
* get_id
* Returns the model id.
*
* @return int
*/
@@ -145,7 +145,7 @@ class model {
}
/**
* get_model_obj
* Returns a plain \stdClass with the model data.
*
* @return \stdClass
*/
@@ -154,7 +154,7 @@ class model {
}
/**
* get_target
* Returns the model target.
*
* @return \core_analytics\local\target\base
*/
@@ -169,7 +169,7 @@ class model {
}
/**
* get_indicators
* Returns the model indicators.
*
* @return \core_analytics\local\indicator\base[]
*/
@@ -221,7 +221,7 @@ class model {
}
/**
* get_analyser
* Returns the model analyser (defined by the model target).
*
* @return \core_analytics\local\analyser\base
*/
@@ -237,8 +237,9 @@ class model {
}
/**
* init_analyser
* Initialises the model analyser.
*
* @throws \coding_exception
* @param array $options
* @return void
*/
@@ -283,9 +284,9 @@ class model {
}
/**
* get_time_splitting
* Returns the model time splitting method.
*
* @return \core_analytics\local\time_splitting\base
* @return \core_analytics\local\time_splitting\base|false Returns false if no time splitting.
*/
public function get_time_splitting() {
if (empty($this->model->timesplitting)) {
@@ -338,7 +339,7 @@ class model {
}
/**
* update
* Updates the model.
*
* @param int|bool $enabled
* @param \core_analytics\local\indicator\base[] $indicators
@@ -396,9 +397,12 @@ class model {
}
/**
* Evaluates the model datasets.
* Evaluates the model.
*
* Model datasets should already be available in Moodle's filesystem.
* This method gets the site contents (through the analyser) creates a .csv dataset
* with them and evaluates the model prediction accuracy multiple times using the
* machine learning backend. It returns an object where the model score is the average
* prediction accuracy of all executed evaluations.
*
* @param array $options
* @return \stdClass[]
@@ -478,12 +482,15 @@ class model {
}
/**
* train
* Trains the model using the site contents.
*
* This method prepares a dataset from the site contents (through the analyser)
* and passes it to the machine learning backends. Static models are skipped as
* they do not require training.
*
* @return \stdClass
*/
public function train() {
global $DB;
\core_analytics\manager::check_can_manage_models();
@@ -540,7 +547,12 @@ class model {
}
/**
* predict
* Get predictions from the site contents.
*
* It analyses the site contents (through analyser classes) looking for samples
* ready to receive predictions. It generates a dataset with all samples ready to
* get predictions and it passes it to the machine learning backends or to the
* targets based on assumptions to get the predictions.
*
* @return \stdClass
*/
@@ -759,7 +771,7 @@ class model {
list($sampleids, $samplesdata) = $this->get_analyser()->get_samples($sampleids);
// Calculate the targets.
$calculations = array();
$predictions = array();
foreach ($analysables as $analysableclass => $rangedata) {
foreach ($rangedata as $rangeindex => $data) {
@@ -804,7 +816,7 @@ class model {
}
/**
* save_prediction
* Stores the prediction in the database.
*
* @param int $sampleid
* @param int $rangeindex
@@ -833,7 +845,7 @@ class model {
}
/**
* enable
* Enabled the model using the provided time splitting method.
*
* @param string $timesplittingid
* @return void
@@ -869,7 +881,10 @@ class model {
}
/**
* is_static
* Is this a static model (as defined by the target)?.
*
* Static models are based on assumptions instead of in machine learning
* backends results.
*
* @return bool
*/
@@ -878,7 +893,7 @@ class model {
}
/**
* is_enabled
* Is this model enabled?
*
* @return bool
*/
@@ -887,7 +902,7 @@ class model {
}
/**
* is_trained
* Is this model already trained?
*
* @return bool
*/
@@ -897,7 +912,7 @@ class model {
}
/**
* mark_as_trained
* Marks the model as trained
*
* @return void
*/
@@ -911,7 +926,7 @@ class model {
}
/**
* get_predictions_contexts
* Get the contexts with predictions.
*
* @return \stdClass[]
*/
@@ -1007,7 +1022,7 @@ class model {
continue;
}
// Replace stdClass object by \core_analytics\prediction objects.
// Replace \stdClass object by \core_analytics\prediction objects.
$prediction = new \core_analytics\prediction($predictiondata, $samplesdata[$sampleid]);
$predictions[$predictionid] = $prediction;
@@ -1079,7 +1094,9 @@ class model {
}
/**
* get_unique_id
* Returns a unique id for this model.
*
* This id should be unique for this site.
*
* @return string
*/
@@ -1138,7 +1155,7 @@ class model {
}
/**
* flag_file_as_used
* Flag the provided file as used for training or prediction.
*
* @param \stored_file $file
* @param string $action
@@ -1156,7 +1173,7 @@ class model {
}
/**
* log_result
* Log the evaluation results in the database.
*
* @param string $timesplittingid
* @param float $score
@@ -1229,7 +1246,7 @@ class model {
// We don't expect people to clear models regularly and the cost of filling the cache is
// 1 db read per context.
$cache = \cache::make('core', 'contextwithinsights');
$result = $cache->purge();
$cache->purge();
}
/**
-1
View File
@@ -61,7 +61,6 @@ class test_indicator_fullname extends \core_analytics\local\indicator\linear {
* @return float
*/
protected function calculate_sample($sampleid, $samplesorigin, $starttime, $endtime) {
global $DB;
$course = $this->retrieve('course', $sampleid);
@@ -60,8 +60,8 @@ class no_teacher extends \core_analytics\local\indicator\binary {
*
* @param int $sampleid
* @param string $sampleorigin
* @param int $notusedstarttime
* @param int $notusedendtime
* @param int|false $notusedstarttime
* @param int|false $notusedendtime
* @return float
*/
public function calculate_sample($sampleid, $sampleorigin, $notusedstarttime = false, $notusedendtime = false) {
-6
View File
@@ -55,18 +55,14 @@ $string['indicator:accessesafterend'] = 'Accesses after the end date';
$string['indicator:accessesbeforestart'] = 'Accesses before the start date';
$string['indicator:anywrite'] = 'Any write action';
$string['indicator:readactions'] = 'Read actions amount';
$string['indicator:completeduserprofile'] = 'User profile is completed';
$string['indicator:userforumstracking'] = 'User is tracking forums';
$string['insightmessagesubject'] = 'New insight for "{$a->contextname}": {$a->insightname}';
$string['insightinfo'] = '{$a->insightname} - {$a->contextname}';
$string['insightinfomessage'] = 'There are some insights you may find useful. Check out {$a}';
$string['invalidtimesplitting'] = 'Model with id {$a} needs a time splitting method before it can be used to train';
$string['invalidanalysablefortimesplitting'] = 'It can not be analysed using {$a} time splitting method';
$string['messageprovider:insights'] = 'Insights generated by prediction models';
$string['modeloutputdir'] = 'Models output directory';
$string['modeloutputdirinfo'] = 'Directory where prediction processors store all evaluation info. Useful for debugging and research.';
$string['nocourses'] = 'No courses to analyse';
$string['nocoursestart'] = 'No course start';
$string['nodata'] = 'No data available';
$string['noinsightsmodel'] = 'This model does not generate insights';
$string['noinsights'] = 'No insights reported';
@@ -79,7 +75,6 @@ $string['novalidsamples'] = 'No valid samples available';
$string['predictionsprocessor'] = 'Predictions processor';
$string['predictionsprocessor_help'] = 'Prediction processors are the machine learning backends that process the datasets generated by calculating models\' indicators and targets.';
$string['processingsitecontents'] = 'Processing site contents';
$string['processingsitecontents'] = 'Processing site contents';
$string['successfullyanalysed'] = 'Successfully analysed';
$string['timesplitting:deciles'] = 'Deciles';
$string['timesplitting:decilesaccum'] = 'Deciles accumulative';
@@ -87,7 +82,6 @@ $string['timesplitting:nosplitting'] = 'No time splitting';
$string['timesplitting:quarters'] = 'Quarters';
$string['timesplitting:quartersaccum'] = 'Quarters accumulative';
$string['timesplitting:singlerange'] = 'Single range';
$string['timesplitting:weekbeforecoursestart'] = 'One week before the course start';
$string['timesplitting:weekly'] = 'Weekly';
$string['timesplitting:weeklyaccum'] = 'Weekly accumulative';
$string['timesplittingmethod'] = 'Time splitting method';
+3
View File
@@ -1015,7 +1015,9 @@ $string['includeroleassignments'] = 'Include role assignments';
$string['includesitefiles'] = 'Include site files used in this course';
$string['includeuserfiles'] = 'Include user files';
$string['increasesections'] = 'Increase the number of sections';
$string['indicator:completeduserprofile'] = 'User profile is completed';
$string['indicator:noteacher'] = 'There are no teachers';
$string['indicator:userforumstracking'] = 'User is tracking forums';
$string['info'] = 'Information';
$string['institution'] = 'Institution';
$string['instudentview'] = 'in student view';
@@ -1146,6 +1148,7 @@ $string['messageprovider:errors'] = 'Important errors with the site';
$string['messageprovider:errors_help'] = 'These are important errors that an administrator should know about.';
$string['messageprovider:notices'] = 'Notices about minor problems';
$string['messageprovider:notices_help'] = 'These are notices that an administrator might be interested in seeing.';
$string['messageprovider:insights'] = 'Insights generated by prediction models';
$string['messageprovider:instantmessage'] = 'Personal messages between users';
$string['messageprovider:instantmessage_help'] = 'This section configures what happens to messages that are sent to you directly from other users on this site.';
$string['messageselect'] = 'Select this user as a message recipient';
+2
View File
@@ -144,6 +144,8 @@ $string['type_gradereport'] = 'Gradebook report';
$string['type_gradereport_plural'] = 'Gradebook reports';
$string['type_gradingform'] = 'Advanced grading method';
$string['type_gradingform_plural'] = 'Advanced grading methods';
$string['type_mlbackend'] = 'Machine learning backend';
$string['type_mlbackend_plural'] = 'Machine learning backends';
$string['type_local'] = 'Local plugin';
$string['type_local_plural'] = 'Local plugins';
$string['type_media'] = 'Media player';
+1 -1
View File
@@ -41,7 +41,7 @@ $PAGE->set_url($url);
// Check that the provided action exists.
$actions = $model->get_target()->prediction_actions($prediction, true);
if (!isset($actions[$actionname])) {
throw new \moodle_exception('errorunknownaction', 'report_insights');
throw new \moodle_exception('errorunknownaction', 'analytics');
}
$modelready = $model->is_enabled() && $model->is_trained() && $model->predictions_exist($context);
@@ -25,7 +25,6 @@
$string['disabledmodel'] = 'Sorry, this model has been disabled by the administrator';
$string['errorpredictionnotfound'] = 'Prediction not found';
$string['insight'] = 'Insight';
$string['insights'] = 'Insights';
$string['pluginname'] = 'Insights';
$string['prediction'] = 'Prediction';
-1
View File
@@ -33,7 +33,6 @@ defined('MOODLE_INTERNAL') || die;
* @return void
*/
function report_insights_extend_navigation_course($navigation, $course, $context) {
global $DB;
if (has_capability('moodle/analytics:listinsights', $context)) {