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 * Student enrolments analyser.
19 *
20 * @package   core
21 * @copyright 2016 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
29require_once($CFG->dirroot . '/lib/enrollib.php');
30
31/**
32 * Student enrolments analyser.
33 *
34 * It does return all student enrolments including the suspended ones.
35 *
36 * @package   core
37 * @copyright 2016 David Monllao {@link http://www.davidmonllao.com}
38 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39 */
40class student_enrolments extends \core_analytics\local\analyser\by_course {
41
42    /**
43     * @var array Cache for user_enrolment id - course id relation.
44     */
45    protected $samplecourses = array();
46
47    /**
48     * Defines the origin of the samples in the database.
49     *
50     * @return string
51     */
52    public function get_samples_origin() {
53        return 'user_enrolments';
54    }
55
56    /**
57     * Returns the student enrolment course context.
58     *
59     * @param int $sampleid
60     * @return \context
61     */
62    public function sample_access_context($sampleid) {
63        return \context_course::instance($this->get_sample_courseid($sampleid));
64    }
65
66    /**
67     * Returns the student enrolment course.
68     *
69     * @param int $sampleid
70     * @return \core_analytics\analysable
71     */
72    public function get_sample_analysable($sampleid) {
73        $course = enrol_get_course_by_user_enrolment_id($sampleid);
74        return \core_analytics\course::instance($course);
75    }
76
77    /**
78     * Data provided by get_all_samples & get_samples.
79     *
80     * @return string[]
81     */
82    protected function provided_sample_data() {
83        return array('user_enrolments', 'context', 'course', 'user');
84    }
85
86    /**
87     * We need to delete associated data if a user requests his data to be deleted.
88     *
89     * @return bool
90     */
91    public function processes_user_data() {
92        return true;
93    }
94
95    /**
96     * Join the samples origin table with the user id table.
97     *
98     * @param string $sampletablealias
99     * @return string
100     */
101    public function join_sample_user($sampletablealias) {
102        return "JOIN {user_enrolments} ue ON {$sampletablealias}.sampleid = ue.id " .
103               "JOIN {user} u ON u.id = ue.userid";
104    }
105
106    /**
107     * All course student enrolments.
108     *
109     * It does return all student enrolments including the suspended ones.
110     *
111     * @param \core_analytics\analysable $course
112     * @return array
113     */
114    public function get_all_samples(\core_analytics\analysable $course) {
115
116        $enrolments = enrol_get_course_users($course->get_id());
117
118        // We fetch all enrolments, but we are only interested in students.
119        $studentids = $course->get_students();
120
121        $samplesdata = array();
122        foreach ($enrolments as $userenrolmentid => $user) {
123
124            if (empty($studentids[$user->id])) {
125                // Not a student or an analysed one.
126                continue;
127            }
128
129            $sampleid = $userenrolmentid;
130            $samplesdata[$sampleid]['user_enrolments'] = (object)array(
131                'id' => $user->ueid,
132                'status' => $user->uestatus,
133                'enrolid' => $user->ueenrolid,
134                'userid' => $user->id,
135                'timestart' => $user->uetimestart,
136                'timeend' => $user->uetimeend,
137                'modifierid' => $user->uemodifierid,
138                'timecreated' => $user->uetimecreated,
139                'timemodified' => $user->uetimemodified
140            );
141            unset($user->ueid);
142            unset($user->uestatus);
143            unset($user->ueenrolid);
144            unset($user->uetimestart);
145            unset($user->uetimeend);
146            unset($user->uemodifierid);
147            unset($user->uetimecreated);
148            unset($user->uetimemodified);
149
150            // This student has been already analysed. We analyse each student once.
151            unset($studentids[$user->id]);
152
153            $samplesdata[$sampleid]['course'] = $course->get_course_data();
154            $samplesdata[$sampleid]['context'] = $course->get_context();
155            $samplesdata[$sampleid]['user'] = $user;
156
157            // Fill the cache.
158            $this->samplecourses[$sampleid] = $course->get_id();
159        }
160
161        $enrolids = array_keys($samplesdata);
162        return array(array_combine($enrolids, $enrolids), $samplesdata);
163    }
164
165    /**
166     * Returns all samples from the samples ids.
167     *
168     * @param int[] $sampleids
169     * @return array
170     */
171    public function get_samples($sampleids) {
172        global $DB;
173
174        $enrolments = enrol_get_course_users(false, false, array(), $sampleids);
175
176        // Some course enrolments.
177        list($enrolsql, $params) = $DB->get_in_or_equal($sampleids, SQL_PARAMS_NAMED);
178
179        $samplesdata = array();
180        foreach ($enrolments as $userenrolmentid => $user) {
181
182            $sampleid = $userenrolmentid;
183            $samplesdata[$sampleid]['user_enrolments'] = (object)array(
184                'id' => $user->ueid,
185                'status' => $user->uestatus,
186                'enrolid' => $user->ueenrolid,
187                'userid' => $user->id,
188                'timestart' => $user->uetimestart,
189                'timeend' => $user->uetimeend,
190                'modifierid' => $user->uemodifierid,
191                'timecreated' => $user->uetimecreated,
192                'timemodified' => $user->uetimemodified
193            );
194            unset($user->ueid);
195            unset($user->uestatus);
196            unset($user->ueenrolid);
197            unset($user->uetimestart);
198            unset($user->uetimeend);
199            unset($user->uemodifierid);
200            unset($user->uetimecreated);
201            unset($user->uetimemodified);
202
203            // Enrolment samples are grouped by the course they belong to, so all $sampleids belong to the same
204            // course, $courseid and $coursemodinfo will only query the DB once and cache the course data in memory.
205            $courseid = $this->get_sample_courseid($sampleid);
206            $coursemodinfo = get_fast_modinfo($courseid);
207            $coursecontext = \context_course::instance($courseid);
208
209            $samplesdata[$sampleid]['course'] = $coursemodinfo->get_course();
210            $samplesdata[$sampleid]['context'] = $coursecontext;
211            $samplesdata[$sampleid]['user'] = $user;
212
213            // Fill the cache.
214            $this->samplecourses[$sampleid] = $coursemodinfo->get_course()->id;
215        }
216
217        $enrolids = array_keys($samplesdata);
218        return array(array_combine($enrolids, $enrolids), $samplesdata);
219    }
220
221    /**
222     * Returns the student enrolment course id.
223     *
224     * @param int $sampleid
225     * @return int
226     */
227    protected function get_sample_courseid($sampleid) {
228        global $DB;
229
230        if (empty($this->samplecourses[$sampleid])) {
231            $course = enrol_get_course_by_user_enrolment_id($sampleid);
232            $this->samplecourses[$sampleid] = $course->id;
233        }
234
235        return $this->samplecourses[$sampleid];
236    }
237
238    /**
239     * Returns the visible name of a sample + a renderable to display as sample picture.
240     *
241     * @param int $sampleid
242     * @param int $contextid
243     * @param array $sampledata
244     * @return array array(string, \renderable)
245     */
246    public function sample_description($sampleid, $contextid, $sampledata) {
247        $description = fullname($sampledata['user'], true, array('context' => $contextid));
248        return array($description, new \user_picture($sampledata['user']));
249    }
250
251}
252