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