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 * Unit tests for core analysers.
19 *
20 * @package   core
21 * @category  test
22 * @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
23 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
24 */
25
26defined('MOODLE_INTERNAL') || die();
27
28require_once(__DIR__ . '/../../analytics/tests/fixtures/test_target_course_level_shortname.php');
29require_once(__DIR__ . '/../../analytics/tests/fixtures/test_target_shortname.php');
30require_once(__DIR__ . '/../../lib/enrollib.php');
31
32/**
33 * Unit tests for core analysers.
34 *
35 * @package   core
36 * @category  test
37 * @copyright 2017 David Monllaó {@link http://www.davidmonllao.com}
38 * @license   http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
39 */
40class core_analytics_analysers_testcase extends advanced_testcase {
41
42    /**
43     * test_courses_analyser
44     *
45     * @return void
46     */
47    public function test_courses_analyser() {
48        $this->resetAfterTest(true);
49
50        $course1 = $this->getDataGenerator()->create_course();
51        $coursecontext = \context_course::instance($course1->id);
52
53        $target = new test_target_shortname();
54        $analyser = new \core\analytics\analyser\courses(1, $target, [], [], []);
55        $analysable = new \core_analytics\course($course1);
56
57        $this->assertInstanceOf('\core_analytics\course', $analyser->get_sample_analysable($course1->id));
58
59        $this->assertInstanceOf('\context_course', $analyser->sample_access_context($course1->id));
60
61        // Just 1 sample per course.
62        $class = new ReflectionClass('\core\analytics\analyser\courses');
63        $method = $class->getMethod('get_all_samples');
64        $method->setAccessible(true);
65        list($sampleids, $samplesdata) = $method->invoke($analyser, $analysable);
66        $this->assertCount(1, $sampleids);
67        $sampleid = reset($sampleids);
68        $this->assertEquals($course1->id, $sampleid);
69        $this->assertEquals($course1->fullname, $samplesdata[$sampleid]['course']->fullname);
70        $this->assertEquals($coursecontext, $samplesdata[$sampleid]['context']);
71
72        // To compare it later.
73        $prevsampledata = $samplesdata[$sampleid];
74        list($sampleids, $samplesdata) = $analyser->get_samples(array($sampleid));
75        $this->assertEquals($prevsampledata['context'], $samplesdata[$sampleid]['context']);
76        $this->assertEquals($prevsampledata['course']->shortname, $samplesdata[$sampleid]['course']->shortname);
77
78        // Context restriction.
79        $category1 = $this->getDataGenerator()->create_category();
80        $category1context = \context_coursecat::instance($category1->id);
81        $category2 = $this->getDataGenerator()->create_category();
82        $category2context = \context_coursecat::instance($category2->id);
83        $course2 = $this->getDataGenerator()->create_course(['category' => $category1->id]);
84        $course3 = $this->getDataGenerator()->create_course(['category' => $category2->id]);
85        $this->assertCount(2, $analyser->get_analysables_iterator(false, [$category1context, $category2context]));
86
87    }
88
89    /**
90     * test_site_courses_analyser
91     *
92     * @return void
93     */
94    public function test_site_courses_analyser() {
95        $this->resetAfterTest(true);
96
97        $course1 = $this->getDataGenerator()->create_course();
98        $course2 = $this->getDataGenerator()->create_course();
99        $course3 = $this->getDataGenerator()->create_course();
100        $course1context = \context_course::instance($course1->id);
101
102        $target = new test_target_shortname();
103        $analyser = new \core\analytics\analyser\site_courses(1, $target, [], [], []);
104        $analysable = new \core_analytics\site();
105
106        $this->assertInstanceOf('\core_analytics\site', $analyser->get_sample_analysable($course1->id));
107        $this->assertInstanceOf('\core_analytics\site', $analyser->get_sample_analysable($course2->id));
108
109        $this->assertInstanceOf('\context_system', $analyser->sample_access_context($course1->id));
110        $this->assertInstanceOf('\context_system', $analyser->sample_access_context($course3->id));
111
112        $class = new ReflectionClass('\core\analytics\analyser\site_courses');
113        $method = $class->getMethod('get_all_samples');
114        $method->setAccessible(true);
115        list($sampleids, $samplesdata) = $method->invoke($analyser, $analysable);
116        $this->assertCount(3, $sampleids);
117
118        // Use course1 it does not really matter.
119        $this->assertArrayHasKey($course1->id, $sampleids);
120        $sampleid = $course1->id;
121        $this->assertEquals($course1->id, $sampleid);
122        $this->assertEquals($course1->fullname, $samplesdata[$sampleid]['course']->fullname);
123        $this->assertEquals($course1context, $samplesdata[$sampleid]['context']);
124
125        // To compare it later.
126        $prevsampledata = $samplesdata[$sampleid];
127        list($sampleids, $samplesdata) = $analyser->get_samples(array($sampleid));
128        $this->assertEquals($prevsampledata['context'], $samplesdata[$sampleid]['context']);
129        $this->assertEquals($prevsampledata['course']->shortname, $samplesdata[$sampleid]['course']->shortname);
130    }
131
132    /**
133     * test_student_enrolments_analyser
134     *
135     * @return void
136     */
137    public function test_student_enrolments_analyser() {
138        global $DB;
139
140        $this->resetAfterTest(true);
141
142        $course1 = $this->getDataGenerator()->create_course();
143        $course1context = \context_course::instance($course1->id);
144
145        $user1 = $this->getDataGenerator()->create_user();
146        $user2 = $this->getDataGenerator()->create_user();
147        $user3 = $this->getDataGenerator()->create_user();
148
149        // Checking that suspended users are also included.
150        $this->getDataGenerator()->enrol_user($user1->id, $course1->id, 'student');
151        $this->getDataGenerator()->enrol_user($user2->id, $course1->id, 'student', 'manual', 0, 0, ENROL_USER_SUSPENDED);
152        $this->getDataGenerator()->enrol_user($user3->id, $course1->id, 'editingteacher');
153        $enrol = $DB->get_record('enrol', array('courseid' => $course1->id, 'enrol' => 'manual'));
154        $ue1 = $DB->get_record('user_enrolments', array('userid' => $user1->id, 'enrolid' => $enrol->id));
155        $ue2 = $DB->get_record('user_enrolments', array('userid' => $user2->id, 'enrolid' => $enrol->id));
156
157        $target = new test_target_shortname();
158        $analyser = new \core\analytics\analyser\student_enrolments(1, $target, [], [], []);
159        $analysable = new \core_analytics\course($course1);
160
161        $this->assertInstanceOf('\core_analytics\course', $analyser->get_sample_analysable($ue1->id));
162        $this->assertInstanceOf('\context_course', $analyser->sample_access_context($ue1->id));
163
164        $class = new ReflectionClass('\core\analytics\analyser\student_enrolments');
165        $method = $class->getMethod('get_all_samples');
166        $method->setAccessible(true);
167        list($sampleids, $samplesdata) = $method->invoke($analyser, $analysable);
168        // Only students.
169        $this->assertCount(2, $sampleids);
170
171        $this->assertArrayHasKey($ue1->id, $sampleids);
172        $this->assertArrayHasKey($ue2->id, $sampleids);
173
174        // Shouldn't matter which one we select.
175        $sampleid = $ue1->id;
176        $this->assertEquals($ue1, $samplesdata[$sampleid]['user_enrolments']);
177        $this->assertEquals($course1->fullname, $samplesdata[$sampleid]['course']->fullname);
178        $this->assertEquals($course1context, $samplesdata[$sampleid]['context']);
179        $this->assertEquals($user1->firstname, $samplesdata[$sampleid]['user']->firstname);
180
181        // To compare it later.
182        $prevsampledata = $samplesdata[$sampleid];
183        list($sampleids, $samplesdata) = $analyser->get_samples(array($sampleid));
184        $this->assertEquals($prevsampledata['user_enrolments'], $samplesdata[$sampleid]['user_enrolments']);
185        $this->assertEquals($prevsampledata['context'], $samplesdata[$sampleid]['context']);
186        $this->assertEquals($prevsampledata['course']->shortname, $samplesdata[$sampleid]['course']->shortname);
187        $this->assertEquals($prevsampledata['user']->firstname, $samplesdata[$sampleid]['user']->firstname);
188
189        // Context restriction.
190        $category1 = $this->getDataGenerator()->create_category();
191        $category1context = \context_coursecat::instance($category1->id);
192        $category2 = $this->getDataGenerator()->create_category();
193        $category2context = \context_coursecat::instance($category2->id);
194        $course2 = $this->getDataGenerator()->create_course(['category' => $category1->id]);
195        $course3 = $this->getDataGenerator()->create_course(['category' => $category2->id]);
196        $this->assertCount(2, $analyser->get_analysables_iterator(false, [$category1context, $category2context]));
197    }
198
199    /**
200     * test_get_analysables_iterator description
201     *
202     * @return null
203     */
204    public function test_get_analysables_iterator() {
205        global $DB;
206
207        $this->resetAfterTest(true);
208
209        $courses = array();
210        for ($i = 0; $i < 2; $i++) {
211            $course = $this->getDataGenerator()->create_course();
212            $analysable = new \core_analytics\course($course);
213            $courses[$analysable->get_id()] = $course;
214        }
215
216        // Check that the analysis performs as expected.
217        $modelid = 1;
218        $includetarget = false;
219
220        $target = new test_target_course_level_shortname();
221        $analyser = new \core\analytics\analyser\courses($modelid, $target, [], [], []);
222
223        $result = new \core_analytics\local\analysis\result_array($modelid, $includetarget, []);
224        $analysis = new \core_analytics\analysis($analyser, $includetarget, $result);
225        $analysis->run();
226        $params = array('modelid' => $modelid, 'action' => 'prediction');
227        $this->assertEquals(2, $DB->count_records('analytics_used_analysables', $params));
228
229        // Check that the previous records do not conflict with the includetarget == false ones.
230        $includetarget = true;
231
232        $target = new test_target_course_level_shortname();
233        $analyser = new \core\analytics\analyser\courses($modelid, $target, [], [], []);
234
235        $result = new \core_analytics\local\analysis\result_array($modelid, $includetarget, []);
236        $analysis = new \core_analytics\analysis($analyser, $includetarget, $result);
237        $analysis->run();
238        $params = array('modelid' => $modelid, 'action' => 'prediction');
239        $this->assertEquals(2, $DB->count_records('analytics_used_analysables', $params));
240        $params = array('modelid' => $modelid, 'action' => 'training');
241        $this->assertEquals(2, $DB->count_records('analytics_used_analysables', $params));
242        $params = array('modelid' => $modelid);
243        $this->assertEquals(4, $DB->count_records('analytics_used_analysables', $params));
244
245        // Check that other models' records do not conflict with previous records.
246        $prevmodelid = 1;
247        $modelid = 2;
248        $includetarget = false;
249
250        $target = new test_target_course_level_shortname();
251        $analyser = new \core\analytics\analyser\courses($modelid, $target, [], [], []);
252
253        $result = new \core_analytics\local\analysis\result_array($modelid, $includetarget, []);
254        $analysis = new \core_analytics\analysis($analyser, $includetarget, $result);
255        $analysis->run();
256        $params = array('modelid' => $prevmodelid);
257        $this->assertEquals(4, $DB->count_records('analytics_used_analysables', $params));
258        $params = array('modelid' => $modelid, 'action' => 'prediction');
259        $this->assertEquals(2, $DB->count_records('analytics_used_analysables', $params));
260        $this->assertEquals(6, $DB->count_records('analytics_used_analysables'));
261
262        $includetarget = true;
263
264        $target = new test_target_course_level_shortname();
265        $analyser = new \core\analytics\analyser\courses($modelid, $target, [], [], []);
266
267        $result = new \core_analytics\local\analysis\result_array($modelid, $includetarget, []);
268        $analysis = new \core_analytics\analysis($analyser, $includetarget, $result);
269        $analysis->run();
270        $params = array('modelid' => $prevmodelid);
271        $this->assertEquals(4, $DB->count_records('analytics_used_analysables', $params));
272        $params = array('modelid' => $modelid, 'action' => 'training');
273        $this->assertEquals(2, $DB->count_records('analytics_used_analysables', $params));
274        $this->assertEquals(8, $DB->count_records('analytics_used_analysables'));
275    }
276}
277