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 * Users analyser (insights for users). 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 25namespace core\analytics\analyser; 26 27defined('MOODLE_INTERNAL') || die(); 28 29/** 30 * Users analyser (insights for users). 31 * 32 * @package core 33 * @copyright 2019 David Monllao {@link http://www.davidmonllao.com} 34 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later 35 */ 36class users extends \core_analytics\local\analyser\base { 37 38 /** 39 * The site users are the analysable elements returned by this analyser. 40 * 41 * @param string|null $action 'prediction', 'training' or null if no specific action needed. 42 * @param \context[] $contexts Only analysables that depend on the provided contexts. All analysables in the system if empty. 43 * @return \Iterator 44 */ 45 public function get_analysables_iterator(?string $action = null, array $contexts = []) { 46 global $DB, $CFG; 47 48 $siteadmins = explode(',', $CFG->siteadmins); 49 50 list($sql, $params) = $this->get_iterator_sql('user', CONTEXT_USER, $action, 'u', $contexts); 51 52 $sql .= " AND u.deleted = :deleted AND u.confirmed = :confirmed AND u.suspended = :suspended"; 53 $params = $params + ['deleted' => 0, 'confirmed' => 1, 'suspended' => 0]; 54 55 $ordersql = $this->order_sql('timecreated', 'ASC', 'u'); 56 57 $recordset = $DB->get_recordset_sql($sql, $params); 58 if (!$recordset->valid()) { 59 $this->add_log(get_string('nousersfound')); 60 return new \ArrayIterator([]); 61 } 62 63 return new \core\dml\recordset_walk($recordset, function($record) use ($siteadmins) { 64 65 if (in_array($record->id, $siteadmins) || isguestuser($record->id)) { 66 // Skip admins and the guest user. 67 return false; 68 } 69 $context = \context_helper::preload_from_record($record); 70 return \core_analytics\user::instance($record, $context); 71 }); 72 } 73 74 /** 75 * Just one sample per analysable. 76 * 77 * @return bool 78 */ 79 public static function one_sample_per_analysable() { 80 return true; 81 } 82 83 /** 84 * Samples origin is user table. 85 * 86 * @return string 87 */ 88 public function get_samples_origin() { 89 return 'user'; 90 } 91 92 /** 93 * Returns the analysable of a sample 94 * 95 * @param int $sampleid 96 * @return \core_analytics\analysable 97 */ 98 public function get_sample_analysable($sampleid) { 99 return \core_analytics\user::instance($sampleid); 100 } 101 102 /** 103 * This provides samples' user and context. 104 * 105 * @return string[] 106 */ 107 protected function provided_sample_data() { 108 return ['user', 'context']; 109 } 110 111 /** 112 * Returns the context of a sample. 113 * 114 * @param int $sampleid 115 * @return \context 116 */ 117 public function sample_access_context($sampleid) { 118 return \context_user::instance($sampleid); 119 } 120 121 /** 122 * This will return just one user as we analyse users separately. 123 * 124 * @param \core_analytics\analysable $user 125 * @return array 126 */ 127 public function get_all_samples(\core_analytics\analysable $user) { 128 129 $context = \context_user::instance($user->get_id()); 130 131 // Just 1 sample per analysable. 132 return [ 133 [$user->get_id() => $user->get_id()], 134 [$user->get_id() => ['user' => $user->get_user_data(), 'context' => $context]] 135 ]; 136 } 137 138 /** 139 * Returns samples data from sample ids. 140 * 141 * @param int[] $sampleids 142 * @return array 143 */ 144 public function get_samples($sampleids) { 145 global $DB; 146 147 list($sql, $params) = $DB->get_in_or_equal($sampleids, SQL_PARAMS_NAMED); 148 $users = $DB->get_records_select('user', "id $sql", $params); 149 150 $userids = array_keys($users); 151 $sampleids = array_combine($userids, $userids); 152 153 $users = array_map(function($user) { 154 return ['user' => $user, 'context' => \context_user::instance($user->id)]; 155 }, $users); 156 157 // No related data attached. 158 return [$sampleids, $users]; 159 } 160 161 /** 162 * Returns the description of a sample. 163 * 164 * @param int $sampleid 165 * @param int $contextid 166 * @param array $sampledata 167 * @return array array(string, \renderable) 168 */ 169 public function sample_description($sampleid, $contextid, $sampledata) { 170 $description = fullname($sampledata['user']); 171 return [$description, new \user_picture($sampledata['user'])]; 172 } 173 174 /** 175 * We need to delete associated data if a user requests his data to be deleted. 176 * 177 * @return bool 178 */ 179 public function processes_user_data() { 180 return true; 181 } 182 183 /** 184 * Join the samples origin table with the user id table. 185 * 186 * @param string $sampletablealias 187 * @return string 188 */ 189 public function join_sample_user($sampletablealias) { 190 return "JOIN {user} u ON u.id = {$sampletablealias}.sampleid"; 191 } 192} 193