1 <?php 2 // This file is part of Moodle - http://moodle.org/ 3 // 4 // Moodle is free software: you can redistribute it and/or modify 5 // it under the terms of the GNU General Public License as published by 6 // the Free Software Foundation, either version 3 of the License, or 7 // (at your option) any later version. 8 // 9 // Moodle is distributed in the hope that it will be useful, 10 // but WITHOUT ANY WARRANTY; without even the implied warranty of 11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 // GNU General Public License for more details. 13 // 14 // You should have received a copy of the GNU General Public License 15 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 16 17 /** 18 * Upcoming activities due target. 19 * 20 * @package core 21 * @copyright 2019 David Monllao {@link http://www.davidmonllao.com} 22 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 23 */ 24 25 namespace core_user\analytics\target; 26 27 defined('MOODLE_INTERNAL') || die(); 28 29 require_once($CFG->dirroot . '/lib/enrollib.php'); 30 31 /** 32 * Upcoming activities due target. 33 * 34 * @package core 35 * @copyright 2019 David Monllao {@link http://www.davidmonllao.com} 36 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 37 */ 38 class upcoming_activities_due extends \core_analytics\local\target\binary { 39 40 /** 41 * Machine learning backends are not required to predict. 42 * 43 * @return bool 44 */ 45 public static function based_on_assumptions() { 46 return true; 47 } 48 49 /** 50 * Only update last analysis time when analysables are processed. 51 * @return bool 52 */ 53 public function always_update_analysis_time(): bool { 54 return false; 55 } 56 57 /** 58 * Only upcoming stuff. 59 * 60 * @param \core_analytics\local\time_splitting\base $timesplitting 61 * @return bool 62 */ 63 public function can_use_timesplitting(\core_analytics\local\time_splitting\base $timesplitting): bool { 64 return ($timesplitting instanceof \core_analytics\local\time_splitting\after_now); 65 } 66 67 /** 68 * Returns the name. 69 * 70 * If there is a corresponding '_help' string this will be shown as well. 71 * 72 * @return \lang_string 73 */ 74 public static function get_name() : \lang_string { 75 return new \lang_string('target:upcomingactivitiesdue', 'user'); 76 } 77 78 /** 79 * Overwritten to show a simpler language string. 80 * 81 * @param int $modelid 82 * @param \context $context 83 * @return string 84 */ 85 public function get_insight_subject(int $modelid, \context $context) { 86 return get_string('youhaveupcomingactivitiesdue'); 87 } 88 89 /** 90 * classes_description 91 * 92 * @return string[] 93 */ 94 protected static function classes_description() { 95 return array( 96 get_string('no'), 97 get_string('yes'), 98 ); 99 } 100 101 /** 102 * Returns the predicted classes that will be ignored. 103 * 104 * @return array 105 */ 106 public function ignored_predicted_classes() { 107 // No need to process users without upcoming activities due. 108 return array(0); 109 } 110 111 /** 112 * get_analyser_class 113 * 114 * @return string 115 */ 116 public function get_analyser_class() { 117 return '\core\analytics\analyser\users'; 118 } 119 120 /** 121 * All users are ok. 122 * 123 * @param \core_analytics\analysable $analysable 124 * @param mixed $fortraining 125 * @return true|string 126 */ 127 public function is_valid_analysable(\core_analytics\analysable $analysable, $fortraining = true) { 128 // The calendar API used by \core_course\analytics\indicator\activities_due is already checking 129 // if the user has any courses. 130 return true; 131 } 132 133 /** 134 * Samples are users and all of them are ok. 135 * 136 * @param int $sampleid 137 * @param \core_analytics\analysable $analysable 138 * @param bool $fortraining 139 * @return bool 140 */ 141 public function is_valid_sample($sampleid, \core_analytics\analysable $analysable, $fortraining = true) { 142 return true; 143 } 144 145 /** 146 * Calculation based on activities due indicator. 147 * 148 * @param int $sampleid 149 * @param \core_analytics\analysable $analysable 150 * @param int $starttime 151 * @param int $endtime 152 * @return float 153 */ 154 protected function calculate_sample($sampleid, \core_analytics\analysable $analysable, $starttime = false, $endtime = false) { 155 156 $activitiesdueindicator = $this->retrieve('\core_course\analytics\indicator\activities_due', $sampleid); 157 if ($activitiesdueindicator == \core_course\analytics\indicator\activities_due::get_max_value()) { 158 return 1; 159 } 160 return 0; 161 } 162 163 /** 164 * No need to link to the insights report in this case. 165 * 166 * @return bool 167 */ 168 public function link_insights_report(): bool { 169 return false; 170 } 171 172 /** 173 * Returns the body message for an insight of a single prediction. 174 * 175 * This default method is executed when the analysable used by the model generates one insight 176 * for each analysable (one_sample_per_analysable === true) 177 * 178 * @param \context $context 179 * @param \stdClass $user 180 * @param \core_analytics\prediction $prediction 181 * @param \core_analytics\action[] $actions Passed by reference to remove duplicate links to actions. 182 * @return array Plain text msg, HTML message and the main URL for this 183 * insight (you can return null if you are happy with the 184 * default insight URL calculated in prediction_info()) 185 */ 186 public function get_insight_body_for_prediction(\context $context, \stdClass $user, \core_analytics\prediction $prediction, 187 array &$actions) { 188 global $OUTPUT; 189 190 $fullmessageplaintext = get_string('youhaveupcomingactivitiesdueinfo', 'moodle', $user->firstname); 191 192 $sampledata = $prediction->get_sample_data(); 193 $activitiesdue = $sampledata['core_course\analytics\indicator\activities_due:extradata']; 194 195 if (empty($activitiesdue)) { 196 // We can throw an exception here because this is a target based on assumptions and we require the 197 // activities_due indicator. 198 throw new \coding_exception('The activities_due indicator must be part of the model indicators.'); 199 } 200 201 $activitiestext = []; 202 foreach ($activitiesdue as $key => $activitydue) { 203 204 // Human-readable version. 205 $activitiesdue[$key]->formattedtime = userdate($activitydue->time); 206 207 // We provide the URL to the activity through a script that records the user click. 208 $activityurl = new \moodle_url($activitydue->url); 209 $actionurl = \core_analytics\prediction_action::transform_to_forward_url($activityurl, 'viewupcoming', 210 $prediction->get_prediction_data()->id); 211 $activitiesdue[$key]->url = $actionurl->out(false); 212 213 if (count($activitiesdue) === 1) { 214 // We will use this activity as the main URL of this insight. 215 $insighturl = $actionurl; 216 } 217 218 $activitiestext[] = $activitydue->name . ': ' . $activitiesdue[$key]->url; 219 } 220 221 foreach ($actions as $key => $action) { 222 if ($action->get_action_name() === 'viewupcoming') { 223 224 // Use it as the main URL of the insight if there are multiple activities due. 225 if (empty($insighturl)) { 226 $insighturl = $action->get_url(); 227 } 228 229 // Remove the 'viewupcoming' action from the list of actions for this prediction as the action has 230 // been included in the link to the activity. 231 unset($actions[$key]); 232 break; 233 } 234 } 235 236 $activitieshtml = $OUTPUT->render_from_template('core_user/upcoming_activities_due_insight_body', (object) [ 237 'activitiesdue' => array_values($activitiesdue), 238 'userfirstname' => $user->firstname 239 ]); 240 241 return [ 242 FORMAT_PLAIN => $fullmessageplaintext . PHP_EOL . PHP_EOL . implode(PHP_EOL, $activitiestext) . PHP_EOL, 243 FORMAT_HTML => $activitieshtml, 244 'url' => $insighturl, 245 ]; 246 } 247 248 /** 249 * Adds a view upcoming events action. 250 * 251 * @param \core_analytics\prediction $prediction 252 * @param mixed $includedetailsaction 253 * @param bool $isinsightuser 254 * @return \core_analytics\prediction_action[] 255 */ 256 public function prediction_actions(\core_analytics\prediction $prediction, $includedetailsaction = false, 257 $isinsightuser = false) { 258 global $CFG, $USER; 259 260 $parentactions = parent::prediction_actions($prediction, $includedetailsaction, $isinsightuser); 261 262 if (!$isinsightuser && $USER->id != $prediction->get_prediction_data()->sampleid) { 263 return $parentactions; 264 } 265 266 // We force a lookahead of 30 days so we are sure that the upcoming activities due are shown. 267 $url = new \moodle_url('/calendar/view.php', ['view' => 'upcoming', 'lookahead' => '30']); 268 $pix = new \pix_icon('i/calendar', get_string('viewupcomingactivitiesdue', 'calendar')); 269 $action = new \core_analytics\prediction_action('viewupcoming', $prediction, 270 $url, $pix, get_string('viewupcomingactivitiesdue', 'calendar')); 271 272 return array_merge([$action], $parentactions); 273 } 274 }
title
Description
Body
title
Description
Body
title
Description
Body
title
Body