MDL-64783 analytics: Activities due insight forwards to calendar
authorDavid Monllaó <davidm@moodle.com>
Fri, 5 Apr 2019 14:40:50 +0000 (16:40 +0200)
committerEloy Lafuente (stronk7) <stronk7@moodle.org>
Mon, 8 Apr 2019 22:29:57 +0000 (00:29 +0200)
The patch includes changes applied after the peer review.

24 files changed:
analytics/classes/analysis.php
analytics/classes/course.php
analytics/classes/insights_generator.php
analytics/classes/local/analyser/base.php
analytics/classes/local/analyser/by_course.php
analytics/classes/local/analyser/sitewide.php
analytics/classes/local/analysis/result.php
analytics/classes/local/analysis/result_array.php
analytics/classes/local/analysis/result_file.php
analytics/classes/local/target/base.php
analytics/classes/local/time_splitting/periodic.php
analytics/classes/model.php
analytics/classes/user.php
analytics/tests/fixtures/test_site_users_analyser.php
calendar/lib.php
calendar/view.php
lang/en/moodle.php
lib/classes/analytics/analyser/users.php
lib/classes/analytics/target/course_enrolments.php
lib/classes/analytics/target/no_teaching.php
lib/tests/fixtures/deprecated_analyser.php
report/insights/done.php
report/insights/insights.php
user/classes/analytics/target/upcoming_activities_due.php

index 764b4ee..bcb621c 100644 (file)
@@ -164,12 +164,6 @@ class analysis {
      */
     public function process_analysable(\core_analytics\analysable $analysable): array {
 
-        $options = $this->analyser->get_options();
-
-        // Default returns.
-        $files = array();
-        $message = null;
-
         // Target instances scope is per-analysable (it can't be lower as calculations run once per
         // analysable, not time splitting method nor time range).
         $target = call_user_func(array($this->analyser->get_target(), 'instance'));
@@ -192,7 +186,7 @@ class analysis {
             $cachedresult = $this->result->retrieve_cached_result($timesplitting, $analysable);
             if ($cachedresult) {
                 $result = new \stdClass();
-                $result->result = $previousanalysis;
+                $result->result = $cachedresult;
                 $results[$timesplitting->get_id()] = $result;
                 continue;
             }
@@ -351,8 +345,7 @@ class analysis {
             }
 
             // We need to pass all the analysis data.
-            $formattedresult = $this->result->format_result($data, $target, $timesplitting, $analysable,
-                $this->analyser->get_modelid(), $this->includetarget, $options);
+            $formattedresult = $this->result->format_result($data, $target, $timesplitting, $analysable);
 
         } catch (\Throwable $e) {
             $this->finish_analysable_analysis();
@@ -381,7 +374,7 @@ class analysis {
      * @param array $sampleids
      * @param array $ranges
      * @param \core_analytics\local\target\base $target
-     * @return array|bool
+     * @return array|null
      */
     public function calculate(\core_analytics\local\time_splitting\base $timesplitting, array &$sampleids,
             array $ranges, \core_analytics\local\target\base $target): ?array {
@@ -389,7 +382,7 @@ class analysis {
         $calculatedtarget = null;
         if ($this->includetarget) {
             // We first calculate the target because analysable data may still be invalid or none
-            // of the analysable samples may be valid ($sampleids is also passed by reference).
+            // of the analysable samples may be valid.
             $calculatedtarget = $target->calculate($sampleids, $timesplitting->get_analysable());
 
             // We remove samples we can not calculate their target.
@@ -403,13 +396,13 @@ class analysis {
 
         // No need to continue calculating if the target couldn't be calculated for any sample.
         if (empty($sampleids)) {
-            return false;
+            return null;
         }
 
         $dataset = $this->calculate_indicators($timesplitting, $sampleids, $ranges);
 
         if (empty($dataset)) {
-            return false;
+            return null;
         }
 
         // Now that we have the indicators in place we can add the time range indicators (and target if provided) to each of them.
@@ -552,7 +545,7 @@ class analysis {
      *
      * @param \core_analytics\local\time_splitting\base $timesplitting
      * @param array $dataset
-     * @param ?array $calculatedtarget
+     * @param array|null $calculatedtarget
      * @return null
      */
     protected function fill_dataset(\core_analytics\local\time_splitting\base $timesplitting,
@@ -731,7 +724,7 @@ class analysis {
 
         if (!$predictedrange) {
             // Nothing to filter out.
-            return;
+            return null;
         }
 
         $predictedrange->sampleids = json_decode($predictedrange->sampleids, true);
@@ -739,7 +732,7 @@ class analysis {
         if (count($missingsamples) === 0) {
             // All samples already calculated.
             unset($ranges[$rangeindex]);
-            return;
+            return null;
         }
 
         // Replace the list of samples by the one excluding samples that already got predictions at this range.
@@ -748,6 +741,13 @@ class analysis {
         return $predictedrange;
     }
 
+    /**
+     * Returns a predict samples record.
+     *
+     * @param  \core_analytics\local\time_splitting\base $timesplitting
+     * @param  int                                       $rangeindex
+     * @return \stdClass|false
+     */
     private function get_predict_samples_record(\core_analytics\local\time_splitting\base $timesplitting, int $rangeindex) {
         global $DB;
 
@@ -785,11 +785,11 @@ class analysis {
      * @param int[] $sampleids
      * @param array $ranges
      * @param \core_analytics\local\time_splitting\base $timesplitting
-     * @param ?\stdClass $predictsamplesrecord The existing record or null if there is no record yet.
+     * @param \stdClass|null $predictsamplesrecord The existing record or null if there is no record yet.
      * @return null
      */
     protected function save_prediction_samples(array $sampleids, array $ranges,
-            \core_analytics\local\time_splitting\base $timesplitting, ?\stdClass $predictsamplesrecord) {
+            \core_analytics\local\time_splitting\base $timesplitting, ?\stdClass $predictsamplesrecord = null) {
         global $DB;
 
         if (count($ranges) > 1) {
@@ -804,8 +804,11 @@ class analysis {
             $predictsamplesrecord->timemodified = time();
             $DB->update_record('analytics_predict_samples', $predictsamplesrecord);
         } else {
-            $predictsamplesrecord = (object)['modelid' => $this->analyser->get_modelid(), 'analysableid' => $timesplitting->get_analysable()->get_id(),
-                'timesplitting' => $timesplitting->get_id(), 'rangeindex' => $rangeindex];
+            $predictsamplesrecord = (object)[
+                'modelid' => $this->analyser->get_modelid(),
+                'analysableid' => $timesplitting->get_analysable()->get_id(),
+                'timesplitting' => $timesplitting->get_id(), 'rangeindex' => $rangeindex
+            ];
             $predictsamplesrecord->sampleids = json_encode($sampleids);
             $predictsamplesrecord->timecreated = time();
             $predictsamplesrecord->timemodified = $predictsamplesrecord->timecreated;
@@ -870,12 +873,14 @@ class analysis {
     private static function get_insert_batch_size(): int {
         global $DB;
 
+        $dbconfig = $DB->export_dbconfig();
+
         // 500 is pgsql default so using 1000 is fine, no other db driver uses a hardcoded value.
-        if (empty($DB->dboptions['bulkinsertsize'])) {
+        if (empty($dbconfig) || empty($dbconfig->dboptions) || empty($dbconfig->dboptions['bulkinsertsize'])) {
             return 1000;
         }
 
-        $bulkinsert = $DB->dboptions['bulkinsertsize'];
+        $bulkinsert = $dbconfig->dboptions['bulkinsertsize'];
         if ($bulkinsert < 1000) {
             return $bulkinsert;
         }
index d20154c..6e372cf 100644 (file)
@@ -133,7 +133,7 @@ class course implements \core_analytics\analysable {
      * through this constructor will not be cached.
      *
      * @param int|\stdClass $course Course id or mdl_course record
-     * @param ?\context $context
+     * @param \context|null $context
      * @return void
      */
     public function __construct($course, ?\context $context = null) {
@@ -156,7 +156,7 @@ class course implements \core_analytics\analysable {
      * Lazy load of course data, students and teachers.
      *
      * @param int|\stdClass $course Course object or course id
-     * @param ?\context $context
+     * @param \context|null $context
      * @return \core_analytics\course
      */
     public static function instance($course, ?\context $context = null) {
index ea1785f..f7029bb 100644 (file)
@@ -66,8 +66,8 @@ class insights_generator {
     /**
      * Generates insight notifications.
      *
-     * @param array                      $samplecontexts    The contexts these predictions belong to
-     * @param \core_analytics\prediction $predictions       The prediction records
+     * @param array                         $samplecontexts    The contexts these predictions belong to
+     * @param \core_analytics\prediction[]  $predictions       The prediction records
      * @return  null
      */
     public function generate($samplecontexts, $predictions) {
@@ -89,7 +89,8 @@ class insights_generator {
                 $insighturl = $this->target->get_insight_context_url($this->modelid, $context);
                 $fullmessage = get_string('insightinfomessage', 'analytics', $insighturl->out(false));
 
-                $fullmessagehtml = $OUTPUT->render_from_template('core_analytics/insight_info_message', ['url' => $insighturl->out(false)]);
+                $fullmessagehtml = $OUTPUT->render_from_template('core_analytics/insight_info_message',
+                    ['url' => $insighturl->out(false)]);
                 $this->notifications($context, $insighturl, $fullmessage, $fullmessagehtml);
             }
         }
@@ -189,7 +190,8 @@ class insights_generator {
             $messageactions[] = $actiondata;
         }
 
-        $fullmessagehtml = $OUTPUT->render_from_template('core_analytics/insight_info_message_prediction', ['actions' => $messageactions]);
+        $fullmessagehtml = $OUTPUT->render_from_template('core_analytics/insight_info_message_prediction',
+            ['actions' => $messageactions]);
         return [$insighturl, $fullmessageplaintext, $fullmessagehtml];
     }
 }
index ce20aa7..1c65e2c 100644 (file)
@@ -110,11 +110,15 @@ abstract class base {
      *
      * \core_analytics\local\analyser\by_course and \core_analytics\local\analyser\sitewide are implementing
      * this method returning site courses (by_course) and the whole system (sitewide) as analysables.
+     *
+     * @todo MDL-65284 This will be removed in Moodle 4.1
+     * @deprecated
+     * @see get_analysables_iterator
      * @throws  \coding_exception
      * @return \core_analytics\analysable[] Array of analysable elements using the analysable id as array key.
      */
     public function get_analysables() {
-        // This function should only be called from get_analysables_iterator and we keep it here until php 4.1
+        // This function should only be called from get_analysables_iterator and we keep it here until Moodle 4.1
         // for backwards compatibility.
         throw new \coding_exception('This method is deprecated in favour of get_analysables_iterator.');
     }
@@ -126,7 +130,7 @@ abstract class base {
      * have already been processed and the order in which they have been processed. Helper methods are available
      * to ease to implementation of get_analysables_iterator: get_iterator_sql and order_sql.
      *
-     * @param ?string $action 'prediction', 'training' or null if no specific action needed.
+     * @param string|null $action 'prediction', 'training' or null if no specific action needed.
      * @return \Iterator
      */
     public function get_analysables_iterator(?string $action = null) {
@@ -402,7 +406,7 @@ abstract class base {
     }
 
     /**
-     * Get the sql of a default implementaion of the iterator.
+     * Get the sql of a default implementation of the iterator.
      *
      * This method only works for analysers that return analysable elements which ids map to a context instance ids.
      *
index 7794cbe..99e70c1 100644 (file)
@@ -38,7 +38,7 @@ abstract class by_course extends base {
     /**
      * Return the list of courses to analyse.
      *
-     * @param ?string $action 'prediction', 'training' or null if no specific action needed.
+     * @param string|null $action 'prediction', 'training' or null if no specific action needed.
      * @return \Iterator
      */
     public function get_analysables_iterator(?string $action = null) {
@@ -65,7 +65,7 @@ abstract class by_course extends base {
 
         if (!$recordset->valid()) {
             $this->add_log(get_string('nocourses', 'analytics'));
-            return [];
+            return new \ArrayIterator([]);
         }
 
         return new \core\dml\recordset_walk($recordset, function($record) {
index da7c475..da6ea71 100644 (file)
@@ -36,9 +36,9 @@ defined('MOODLE_INTERNAL') || die();
 abstract class sitewide extends base {
 
     /**
-     * Return the list of courses to analyse.
+     * Return the list of analysables to analyse.
      *
-     * @param ?string $action 'prediction', 'training' or null if no specific action needed.
+     * @param string|null $action 'prediction', 'training' or null if no specific action needed.
      * @return \Iterator
      */
     public function get_analysables_iterator(?string $action = null) {
index 94917a8..d829261 100644 (file)
@@ -33,7 +33,7 @@ defined('MOODLE_INTERNAL') || die();
  * @copyright 2019 David Monllao {@link http://www.davidmonllao.com}
  * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
-class result {
+abstract class result {
 
     /**
      * @var int
@@ -73,4 +73,30 @@ class result {
         \core_analytics\analysable $analysable) {
         return false;
     }
+
+    /**
+     * Stores the analysis results.
+     *
+     * @param  array $results
+     * @return bool            True if anything was successfully analysed
+     */
+    abstract public function add_analysable_results(array $results): bool;
+
+    /**
+     * Formats the result.
+     *
+     * @param  array                                     $data
+     * @param  \core_analytics\local\target\base         $target
+     * @param  \core_analytics\local\time_splitting\base $timesplitting
+     * @param  \core_analytics\analysable                $analysable
+     * @return mixed It can be in whatever format the result uses
+     */
+    abstract public function format_result(array $data, \core_analytics\local\target\base $target,
+            \core_analytics\local\time_splitting\base $timesplitting, \core_analytics\analysable $analysable);
+
+    /**
+     * Returns the results of the analysis.
+     * @return array
+     */
+    abstract public function get(): array;
 }
\ No newline at end of file
index 59c25df..0a8e21e 100644 (file)
@@ -73,14 +73,10 @@ class result_array extends result {
      * @param  \core_analytics\local\target\base         $target
      * @param  \core_analytics\local\time_splitting\base $timesplitting
      * @param  \core_analytics\analysable                $analysable
-     * @param  int                                       $modelid
-     * @param  bool                                      $includetarget
-     * @param  array                                     $options
-     * @return mixed A \stored_file in this case
+     * @return mixed The data as it comes
      */
     public function format_result(array $data, \core_analytics\local\target\base $target,
-            \core_analytics\local\time_splitting\base $timesplitting, \core_analytics\analysable $analysable,
-            int $modelid, bool $includetarget, array $options) {
+            \core_analytics\local\time_splitting\base $timesplitting, \core_analytics\analysable $analysable) {
         return $data;
     }
 
index 05e47ff..7a6b61e 100644 (file)
@@ -100,22 +100,18 @@ class result_file extends result {
      * @param  \core_analytics\local\target\base         $target
      * @param  \core_analytics\local\time_splitting\base $timesplitting
      * @param  \core_analytics\analysable                $analysable
-     * @param  int                                       $modelid
-     * @param  bool                                      $includetarget
-     * @param  array                                     $options
      * @return mixed A \stored_file in this case
      */
     public function format_result(array $data, \core_analytics\local\target\base $target,
-            \core_analytics\local\time_splitting\base $timesplitting, \core_analytics\analysable $analysable,
-            int $modelid, bool $includetarget, array $options) {
+            \core_analytics\local\time_splitting\base $timesplitting, \core_analytics\analysable $analysable) {
 
-        if (!empty($includetarget)) {
+        if (!empty($this->includetarget)) {
             $filearea = \core_analytics\dataset_manager::LABELLED_FILEAREA;
         } else {
             $filearea = \core_analytics\dataset_manager::UNLABELLED_FILEAREA;
         }
-        $dataset = new \core_analytics\dataset_manager($modelid, $analysable->get_id(),
-            $timesplitting->get_id(), $filearea, $options['evaluation']);
+        $dataset = new \core_analytics\dataset_manager($this->modelid, $analysable->get_id(),
+            $timesplitting->get_id(), $filearea, $this->options['evaluation']);
 
         // Add extra metadata.
         $this->add_model_metadata($data, $timesplitting, $target);
index 9ae2290..0ea5e8b 100644 (file)
@@ -122,9 +122,11 @@ abstract class base extends \core_analytics\calculable {
      *
      * @param \core_analytics\prediction $prediction
      * @param bool $includedetailsaction
+     * @param bool $isinsightuser
      * @return \core_analytics\prediction_action[]
      */
-    public function prediction_actions(\core_analytics\prediction $prediction, $includedetailsaction = false, $isinsightuser = false) {
+    public function prediction_actions(\core_analytics\prediction $prediction, $includedetailsaction = false,
+            $isinsightuser = false) {
         global $PAGE;
 
         $predictionid = $prediction->get_prediction_data()->id;
index 3fe1304..4a52370 100644 (file)
@@ -43,7 +43,7 @@ abstract class periodic extends base {
     abstract protected function periodicity();
 
     /**
-     * Returns whether the course can be processed by this time splitting method or not.
+     * Returns whether the analysable can be processed by this time splitting method or not.
      *
      * @param \core_analytics\analysable $analysable
      * @return bool
@@ -83,7 +83,7 @@ abstract class periodic extends base {
 
         $nextrange = $this->get_next_range($next);
         if ($this->ready_to_predict($nextrange) && (empty($end) || $next < $end)) {
-            // Add the next one if we we have not reached the analysable end yet.
+            // Add the next one if we have not reached the analysable end yet.
             // It will be used to get predictions.
             $ranges[] = $nextrange;
         }
index cce9f9f..5113a85 100644 (file)
@@ -954,7 +954,6 @@ class model {
      * Get predictions from a static model.
      *
      * @param array $indicatorcalculations
-     * @param string[] $headers
      * @return \stdClass[]
      */
     protected function get_static_predictions(&$indicatorcalculations) {
index 8751e02..6dd83c7 100644 (file)
@@ -71,7 +71,7 @@ class user implements \core_analytics\analysable {
      * through this constructor will not be cached.
      *
      * @param int|\stdClass $user User id
-     * @param ?\context $context
+     * @param \context|null $context
      * @return void
      */
     public function __construct($user, ?\context $context = null) {
@@ -94,7 +94,7 @@ class user implements \core_analytics\analysable {
      * Lazy load of analysable data.
      *
      * @param int|\stdClass $user User object or user id
-     * @param ?\context $context
+     * @param \context|null $context
      * @return \core_analytics\user
      */
     public static function instance($user, ?\context $context = null) {
index a3c8d27..ac0e1c0 100644 (file)
@@ -121,7 +121,7 @@ class test_site_users_analyser extends \core_analytics\local\analyser\sitewide {
      * @return array array(string, \renderable)
      */
     public function sample_description($sampleid, $contextid, $sampledata) {
-        $description = fullname($samplesdata['user']);
+        $description = fullname($sampledata['user']);
         $userimage = new \pix_icon('i/user', get_string('user'));
         return array($description, $userimage);
     }
index fd6078a..84b5ab3 100644 (file)
@@ -3302,9 +3302,11 @@ function calendar_get_legacy_events($tstart, $tend, $users, $groups, $courses,
  * @param   string  $view The type of calendar to have displayed
  * @param   bool    $includenavigation Whether to include navigation
  * @param   bool    $skipevents Whether to load the events or not
+ * @param   int     $lookahead Overwrites site and users's lookahead setting.
  * @return  array[array, string]
  */
-function calendar_get_view(\calendar_information $calendar, $view, $includenavigation = true, bool $skipevents = false) {
+function calendar_get_view(\calendar_information $calendar, $view, $includenavigation = true, bool $skipevents = false,
+        ?int $lookahead = null) {
     global $PAGE, $CFG;
 
     $renderer = $PAGE->get_renderer('core_calendar');
@@ -3322,12 +3324,14 @@ function calendar_get_view(\calendar_information $calendar, $view, $includenavig
         $date->modify('+1 day');
     } else if ($view === 'upcoming' || $view === 'upcoming_mini') {
         // Number of days in the future that will be used to fetch events.
-        if (isset($CFG->calendar_lookahead)) {
-            $defaultlookahead = intval($CFG->calendar_lookahead);
-        } else {
-            $defaultlookahead = CALENDAR_DEFAULT_UPCOMING_LOOKAHEAD;
+        if (!$lookahead) {
+            if (isset($CFG->calendar_lookahead)) {
+                $defaultlookahead = intval($CFG->calendar_lookahead);
+            } else {
+                $defaultlookahead = CALENDAR_DEFAULT_UPCOMING_LOOKAHEAD;
+            }
+            $lookahead = get_user_preferences('calendar_lookahead', $defaultlookahead);
         }
-        $lookahead = get_user_preferences('calendar_lookahead', $defaultlookahead);
 
         // Maximum number of events to be displayed on upcoming view.
         $defaultmaxevents = CALENDAR_DEFAULT_UPCOMING_MAXEVENTS;
index df616e1..3fb1509 100644 (file)
@@ -53,6 +53,7 @@ $categoryid = optional_param('category', null, PARAM_INT);
 $courseid = optional_param('course', SITEID, PARAM_INT);
 $view = optional_param('view', 'upcoming', PARAM_ALPHA);
 $time = optional_param('time', 0, PARAM_INT);
+$lookahead = optional_param('lookahead', null, PARAM_INT);
 
 $url = new moodle_url('/calendar/view.php');
 
@@ -124,7 +125,7 @@ echo html_writer::start_tag('div', array('class'=>'heightcontainer'));
 echo $OUTPUT->heading(get_string('calendar', 'calendar'));
 
 
-list($data, $template) = calendar_get_view($calendar, $view);
+list($data, $template) = calendar_get_view($calendar, $view, true, false, $lookahead);
 echo $renderer->render_from_template($template, $data);
 
 echo html_writer::end_tag('div');
index fb01f78..fdb5e2e 100644 (file)
@@ -886,7 +886,6 @@ $string['general'] = 'General';
 $string['geolocation'] = 'latitude - longitude';
 $string['gettheselogs'] = 'Get these logs';
 $string['go'] = 'Go';
-$string['gotodashboard'] = 'Go to Dashboard';
 $string['gpl'] = 'Copyright (C) 1999 onwards  Martin Dougiamas  (http://moodle.com)
 
 This program is free software; you can redistribute it and/or modify
index 422951d..40ba586 100644 (file)
@@ -38,7 +38,7 @@ class users extends \core_analytics\local\analyser\base {
     /**
      * The site users are the analysable elements returned by this analyser.
      *
-     * @param ?string $action 'prediction', 'training' or null if no specific action needed.
+     * @param string|null $action 'prediction', 'training' or null if no specific action needed.
      * @return \Iterator
      */
     public function get_analysables_iterator(?string $action = null) {
@@ -46,7 +46,6 @@ class users extends \core_analytics\local\analyser\base {
 
         $siteadmins = explode(',', $CFG->siteadmins);
 
-
         list($sql, $params) = $this->get_iterator_sql('user', CONTEXT_USER, $action, 'u');
 
         $sql .= " AND u.deleted = :deleted AND u.confirmed = :confirmed AND u.suspended = :suspended";
@@ -57,6 +56,7 @@ class users extends \core_analytics\local\analyser\base {
         $recordset = $DB->get_recordset_sql($sql, $params);
         if (!$recordset->valid()) {
             $this->add_log(get_string('nousersfound'));
+            return new \ArrayIterator([]);
         }
 
         return new \core\dml\recordset_walk($recordset, function($record) use ($siteadmins) {
index 54ac6b4..8193626 100644 (file)
@@ -153,9 +153,11 @@ abstract class course_enrolments extends \core_analytics\local\target\binary {
      *
      * @param \core_analytics\prediction $prediction
      * @param bool $includedetailsaction
+     * @param bool $isinsightuser
      * @return \core_analytics\prediction_action[]
      */
-    public function prediction_actions(\core_analytics\prediction $prediction, $includedetailsaction = false, $isinsightuser = false) {
+    public function prediction_actions(\core_analytics\prediction $prediction, $includedetailsaction = false,
+            $isinsightuser = false) {
         global $USER;
 
         $actions = array();
index 4ec1176..a7769b8 100644 (file)
@@ -71,9 +71,11 @@ class no_teaching extends \core_analytics\local\target\binary {
      *
      * @param \core_analytics\prediction $prediction
      * @param mixed $includedetailsaction
+     * @param bool $isinsightuser
      * @return \core_analytics\prediction_action[]
      */
-    public function prediction_actions(\core_analytics\prediction $prediction, $includedetailsaction = false, $isinsightuser = false) {
+    public function prediction_actions(\core_analytics\prediction $prediction, $includedetailsaction = false,
+            $isinsightuser = false) {
         global $CFG;
 
         require_once($CFG->dirroot . '/course/lib.php');
index 33e7594..a4a48ef 100644 (file)
@@ -132,7 +132,7 @@ class deprecated_analyser extends \core_analytics\local\analyser\base {
      * @return array array(string, \renderable)
      */
     public function sample_description($sampleid, $contextid, $sampledata) {
-        $description = fullname($samplesdata['user']);
+        $description = fullname($sampledata['user']);
         $userimage = new \pix_icon('i/user', get_string('user'));
         return array($description, $userimage);
     }
index f21dfe3..d64d151 100644 (file)
@@ -18,7 +18,7 @@
  * Forwards the user to the action they selected.
  *
  * @package    report_insights
- * @copyright  2017 David Monllao {@link http://www.davidmonllao.com}
+ * @copyright  2019 David Monllao {@link http://www.davidmonllao.com}
  * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
  */
 
index 4990412..c45aaf9 100644 (file)
@@ -110,7 +110,7 @@ $PAGE->set_heading($insightinfo->contextname);
 if ($model->get_analyser()::one_sample_per_analysable()) {
 
     // Param $perpage to 2 so we can detect if this model's analyser is using one_sample_per_analysable incorrectly.
-    $predictionsdata = $model->get_predictions($context, true, false, 2);
+    $predictionsdata = $model->get_predictions($context, true, 0, 2);
     if ($predictionsdata) {
         list($total, $predictions) = $predictionsdata;
         if ($total > 1) {
index 911d301..096005c 100644 (file)
@@ -121,7 +121,7 @@ class upcoming_activities_due extends \core_analytics\local\target\binary {
     }
 
     /**
-     * Only process samples which start date is getting close.
+     * Samples are users and all of them are ok.
      *
      * @param int $sampleid
      * @param \core_analytics\analysable $analysable
@@ -151,13 +151,15 @@ class upcoming_activities_due extends \core_analytics\local\target\binary {
     }
 
     /**
-     * Adds a view dashboard action.
+     * Adds a view upcoming events action.
      *
      * @param \core_analytics\prediction $prediction
      * @param mixed $includedetailsaction
+     * @param bool $isinsightuser
      * @return \core_analytics\prediction_action[]
      */
-    public function prediction_actions(\core_analytics\prediction $prediction, $includedetailsaction = false, $isinsightuser = false) {
+    public function prediction_actions(\core_analytics\prediction $prediction, $includedetailsaction = false,
+            $isinsightuser = false) {
         global $CFG, $USER;
 
         $parentactions = parent::prediction_actions($prediction, $includedetailsaction);
@@ -166,10 +168,11 @@ class upcoming_activities_due extends \core_analytics\local\target\binary {
             return $parentactions;
         }
 
-        $url = new \moodle_url('/my/index.php');
-        $pix = new \pix_icon('i/dashboard', get_string('gotodashboard'));
+        // We force a lookahead of 30 days so we are sure that the upcoming activities due are shown.
+        $url = new \moodle_url('/calendar/view.php', ['view' => 'upcoming', 'lookahead' => '30']);
+        $pix = new \pix_icon('i/calendar', get_string('upcomingevents', 'calendar'));
         $action = new \core_analytics\prediction_action('viewupcoming', $prediction,
-            $url, $pix, get_string('gotodashboard'));
+            $url, $pix, get_string('upcomingevents', 'calendar'));
 
         return array_merge([$action], $parentactions);
     }