MDL-57791 analytics: Second review round
authorDavid Monllao <davidm@moodle.com>
Mon, 19 Jun 2017 14:03:51 +0000 (16:03 +0200)
committerDavid Monllao <davidm@moodle.com>
Mon, 24 Jul 2017 06:36:50 +0000 (08:36 +0200)
38 files changed:
admin/settings/analytics.php
admin/tool/models/amd/src/log_info.js
admin/tool/models/classes/analytics/target/course_dropout.php
admin/tool/models/classes/analytics/target/no_teaching.php
admin/tool/models/classes/output/model_logs.php
admin/tool/models/classes/output/renderer.php
admin/tool/models/classes/task/predict_models.php
admin/tool/models/classes/task/train_models.php
admin/tool/models/cli/evaluate_model.php
admin/tool/models/cli/guess_course_start_and_end.php
admin/tool/models/db/install.php
admin/tool/models/index.php
admin/tool/models/lang/en/tool_models.php
admin/tool/models/model.php
admin/tool/models/templates/models_list.mustache
analytics/classes/calculable.php
analytics/classes/course.php
analytics/classes/dataset_manager.php
analytics/classes/local/analyser/base.php
analytics/classes/local/analyser/by_course.php
analytics/classes/local/analyser/courses.php
analytics/classes/local/analyser/sitewide.php
analytics/classes/local/indicator/community_of_inquiry_activity.php
analytics/classes/local/indicator/user_profile_set.php [deleted file]
analytics/classes/local/indicator/user_track_forums.php [deleted file]
analytics/classes/local/target/base.php
analytics/classes/local/target/discrete.php
analytics/classes/local/time_splitting/base.php
analytics/classes/manager.php
analytics/classes/model.php
analytics/tests/fixtures/test_indicator_fullname.php
course/classes/analytics/indicator/no_teacher.php
lang/en/analytics.php
lang/en/moodle.php
lang/en/plugin.php
report/insights/action.php
report/insights/lang/en/report_insights.php
report/insights/lib.php

index f3dfa20..f8aa88d 100644 (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));
     }
index 6f9a114..2b89be9 100644 (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
          */
index 6ce111b..6af0884 100644 (file)
@@ -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');
index e35bb90..5d59f58 100644 (file)
@@ -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();
index e86eca5..8d7e211 100644 (file)
@@ -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) {
index 6110aee..e966dd7 100644 (file)
@@ -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);
             }
         }
index 9f4d3db..dc5d2d4 100644 (file)
@@ -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());
             }
         }
 
index bd2af8e..8501cd0 100644 (file)
@@ -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());
             }
         }
     }
index 0036105..fc08368 100644 (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'));
     }
 
index bf01bfd..63de9cd 100644 (file)
@@ -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);
index ec86a90..5d83a70 100644 (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);
 }
index 415a488..47fe6d0 100644 (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();
index 0015d4f..56ffd0c 100644 (file)
@@ -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';
index 26c3fb2..cbcb090 100644 (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':
index 0cb1965..5726843 100644 (file)
                 "timesplitting": "Quarters",
                 "noinsights": "No insights available yet"
             }
-        ]
+        ],
+        "warnings": {
+            "message": "Hey, this is a warning"
+        }
     }
 }}
 
index 7d4597b..ed81701 100644 (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) {
 
index 6dee6cf..d99d3af 100644 (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;
index fe34cbb..e791020 100644 (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;
index f2dc76b..d5eea16 100644 (file)
@@ -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();
 
index e4feb14..424270e 100644 (file)
@@ -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).
index fa33bd5..c572088 100644 (file)
@@ -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());
 
index 1cffcc0..1f21aca 100644 (file)
@@ -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.
index 367ae2e..318fd52 100644 (file)
@@ -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).
diff --git a/analytics/classes/local/indicator/user_profile_set.php b/analytics/classes/local/indicator/user_profile_set.php
deleted file mode 100644 (file)
index 3a2ad7c..0000000
+++ /dev/null
@@ -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);
-    }
-}
diff --git a/analytics/classes/local/indicator/user_track_forums.php b/analytics/classes/local/indicator/user_track_forums.php
deleted file mode 100644 (file)
index 4d1daa8..0000000
+++ /dev/null
@@ -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();
-    }
-}
index 8144220..688e230 100644 (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;
     }
 
index 5d3dcb4..0799b4d 100644 (file)
@@ -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;
+    }
 }
index 4992799..60b7f8f 100644 (file)
@@ -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])) {
index 02325a6..132beeb 100644 (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;
     }
 }
index f1e25dd..4e54856 100644 (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();
     }
 
     /**
index 6c47bb5..2487407 100644 (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);
 
index 54687cd..d240a96 100644 (file)
@@ -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) {
index e2bc33d..45681ca 100644 (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';
index 6c9c660..4e7c857 100644 (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';
index 460de37..78c661b 100644 (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';
index f0b07c7..718e2ba 100644 (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);
index 3efc313..a5e1d75 100644 (file)
@@ -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';
index aee031f..6ebc7a1 100644 (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)) {