Issues (2037)

main/gradebook/lib/user_data_generator.class.php (1 issue)

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
/**
5
 * Class UserDataGenerator
6
 * Class to select, sort and transform object data into array data,
7
 * used for a student's general view.
8
 *
9
 * @author Bert Steppé
10
 */
11
class UserDataGenerator
12
{
13
    // Sorting types constants
14
    public const UDG_SORT_TYPE = 1;
15
    public const UDG_SORT_NAME = 2;
16
    public const UDG_SORT_COURSE = 4;
17
    public const UDG_SORT_CATEGORY = 8;
18
    public const UDG_SORT_AVERAGE = 16;
19
    public const UDG_SORT_SCORE = 32;
20
    public const UDG_SORT_MASK = 64;
21
22
    public const UDG_SORT_ASC = 128;
23
    public const UDG_SORT_DESC = 256;
24
25
    private $items;
26
    private $userid;
27
28
    private $coursecodecache;
29
    private $categorycache;
30
    private $scorecache;
31
    private $avgcache;
32
33
    /**
34
     * UserDataGenerator constructor.
35
     *
36
     * @param int   $userid
37
     * @param array $evals
38
     * @param array $links
39
     */
40
    public function __construct($userid, $evals = [], $links = [])
41
    {
42
        $this->userid = $userid;
43
        $result = [];
44
        foreach ($evals as $eval) {
45
            $toadd = true;
46
            $coursecode = $eval->get_course_code();
47
            if (isset($coursecode)) {
48
                $result = Result::load(null, $userid, $eval->get_id());
49
                if (0 == count($result)) {
50
                    $toadd = false;
51
                }
52
            }
53
            if ($toadd) {
54
                $evals_filtered_copy = $evals;
55
            }
56
        }
57
        if (0 == count($result)) {
58
            $evals_filtered = $evals;
59
        } else {
60
            $evals_filtered = $evals_filtered_copy;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $evals_filtered_copy does not seem to be defined for all execution paths leading up to this point.
Loading history...
61
        }
62
        $this->items = array_merge($evals_filtered, $links);
63
64
        $this->coursecodecache = [];
65
        $this->categorycache = [];
66
        $this->scorecache = null;
67
        $this->avgcache = null;
68
    }
69
70
    /**
71
     * Get total number of items (rows).
72
     */
73
    public function get_total_items_count()
74
    {
75
        return count($this->items);
76
    }
77
78
    /**
79
     * Get actual array data.
80
     *
81
     * @return array 2-dimensional array - each array contains the elements:
82
     *               0: eval/link object
83
     *               1: item name
84
     *               2: course name
85
     *               3: category name
86
     *               4: average score
87
     *               5: student's score
88
     *               6: student's score as custom display (only if custom scoring enabled)
89
     */
90
    public function get_data(
91
        $sorting = 0,
92
        $start = 0,
93
        $count = null,
94
        $ignore_score_color = false
95
    ) {
96
        // do some checks on count, redefine if invalid value
97
        if (!isset($count)) {
98
            $count = count($this->items) - $start;
99
        }
100
        if ($count < 0) {
101
            $count = 0;
102
        }
103
        $allitems = $this->items;
104
105
        // sort users array
106
        if ($sorting & self::UDG_SORT_TYPE) {
107
            usort($allitems, ['UserDataGenerator', 'sort_by_type']);
108
        } elseif ($sorting & self::UDG_SORT_NAME) {
109
            usort($allitems, ['UserDataGenerator', 'sort_by_name']);
110
        } elseif ($sorting & self::UDG_SORT_COURSE) {
111
            usort($allitems, ['UserDataGenerator', 'sort_by_course']);
112
        } elseif ($sorting & self::UDG_SORT_CATEGORY) {
113
            usort($allitems, ['UserDataGenerator', 'sort_by_category']);
114
        } elseif ($sorting & self::UDG_SORT_AVERAGE) {
115
            // if user sorts on average scores, first calculate them and cache them
116
            foreach ($allitems as $item) {
117
                $this->avgcache[$item->get_item_type().$item->get_id()] = $item->calc_score();
118
            }
119
            usort($allitems, ['UserDataGenerator', 'sort_by_average']);
120
        } elseif ($sorting & self::UDG_SORT_SCORE) {
121
            // if user sorts on student's scores, first calculate them and cache them
122
            foreach ($allitems as $item) {
123
                $this->scorecache[$item->get_item_type().$item->get_id()] = $item->calc_score($this->userid);
124
            }
125
            usort($allitems, ['UserDataGenerator', 'sort_by_score']);
126
        } elseif ($sorting & self::UDG_SORT_MASK) {
127
            // if user sorts on student's masks, first calculate scores and cache them
128
            foreach ($allitems as $item) {
129
                $this->scorecache[$item->get_item_type().$item->get_id()] = $item->calc_score($this->userid);
130
            }
131
            usort($allitems, ['UserDataGenerator', 'sort_by_mask']);
132
        }
133
134
        if ($sorting & self::UDG_SORT_DESC) {
135
            $allitems = array_reverse($allitems);
136
        }
137
        // select the items we have to display
138
        $visibleitems = array_slice($allitems, $start, $count);
139
140
        // fill score cache if not done yet
141
        if (!isset($this->scorecache)) {
142
            foreach ($visibleitems as $item) {
143
                $this->scorecache[$item->get_item_type().$item->get_id()] = $item->calc_score($this->userid);
144
            }
145
        }
146
        // generate the data to display
147
        $scoredisplay = ScoreDisplay::instance();
148
        $data = [];
149
        $model = ExerciseLib::getCourseScoreModel();
150
        foreach ($visibleitems as $item) {
151
            $row = [];
152
            $row[] = $item;
153
            $row[] = $item->get_name();
154
            $row[] = $this->build_course_name($item);
155
            $row[] = $this->build_category_name($item);
156
157
            if (!empty($model)) {
158
                if (isset($this->avgcache)) {
159
                    $avgscore = $this->avgcache[$item->get_item_type().$item->get_id()];
160
                } else {
161
                    $avgscore = $item->calc_score();
162
                }
163
                $row[] = ExerciseLib::show_score($avgscore[0], $avgscore[1]);
164
                $score = $this->scorecache[$item->get_item_type().$item->get_id()];
165
                $displayScore = ExerciseLib::show_score($score[0], $score[1]);
166
                $row[] = $displayScore;
167
                if ($scoredisplay->is_custom()) {
168
                    $row[] = $displayScore;
169
                }
170
            } else {
171
                $row[] = $this->build_average_column($item, $ignore_score_color);
172
                $row[] = $this->build_result_column($item, $ignore_score_color);
173
                if ($scoredisplay->is_custom()) {
174
                    $row[] = $this->build_mask_column($item, $ignore_score_color);
175
                }
176
            }
177
178
            $data[] = $row;
179
        }
180
181
        return $data;
182
    }
183
184
    /**
185
     * @param $item1
186
     * @param $item2
187
     *
188
     * @return int
189
     */
190
    public function sort_by_type($item1, $item2)
191
    {
192
        if ($item1->get_item_type() == $item2->get_item_type()) {
193
            return $this->sort_by_name($item1, $item2);
194
        } else {
195
            return $item1->get_item_type() < $item2->get_item_type() ? -1 : 1;
196
        }
197
    }
198
199
    /**
200
     * @param $item1
201
     * @param $item2
202
     *
203
     * @return int
204
     */
205
    public function sort_by_course($item1, $item2)
206
    {
207
        $name1 = api_strtolower(
208
            $this->get_course_name_from_code_cached($item1->get_course_code())
209
        );
210
        $name2 = api_strtolower(
211
            $this->get_course_name_from_code_cached($item2->get_course_code())
212
        );
213
214
        return api_strnatcmp($name1, $name2);
215
    }
216
217
    /**
218
     * @param $item1
219
     * @param $item2
220
     *
221
     * @return int
222
     */
223
    public function sort_by_category($item1, $item2)
224
    {
225
        $cat1 = $this->get_category_cached($item1->get_category_id());
226
        $cat2 = $this->get_category_cached($item2->get_category_id());
227
        $name1 = api_strtolower($this->get_category_name_to_display($cat1));
228
        $name2 = api_strtolower($this->get_category_name_to_display($cat2));
229
230
        return api_strnatcmp($name1, $name2);
231
    }
232
233
    /**
234
     * @param $item1
235
     * @param $item2
236
     *
237
     * @return int
238
     */
239
    public function sort_by_name($item1, $item2)
240
    {
241
        return api_strnatcmp($item1->get_name(), $item2->get_name());
242
    }
243
244
    /**
245
     * @param $item1
246
     * @param $item2
247
     *
248
     * @return int
249
     */
250
    public function sort_by_average($item1, $item2)
251
    {
252
        $score1 = $this->avgcache[$item1->get_item_type().$item1->get_id()];
253
        $score2 = $this->avgcache[$item2->get_item_type().$item2->get_id()];
254
255
        return $this->compare_scores($score1, $score2);
256
    }
257
258
    /**
259
     * @param $item1
260
     * @param $item2
261
     *
262
     * @return int
263
     */
264
    public function sort_by_score($item1, $item2)
265
    {
266
        $score1 = $this->scorecache[$item1->get_item_type().$item1->get_id()];
267
        $score2 = $this->scorecache[$item2->get_item_type().$item2->get_id()];
268
269
        return $this->compare_scores($score1, $score2);
270
    }
271
272
    /**
273
     * @param $item1
274
     * @param $item2
275
     *
276
     * @return int
277
     */
278
    public function sort_by_mask($item1, $item2)
279
    {
280
        $score1 = $this->scorecache[$item1->get_item_type().$item1->get_id()];
281
        $score2 = $this->scorecache[$item2->get_item_type().$item2->get_id()];
282
283
        return ScoreDisplay::compare_scores_by_custom_display($score1, $score2);
284
    }
285
286
    /**
287
     * @param $score1
288
     * @param $score2
289
     *
290
     * @return int
291
     */
292
    public function compare_scores($score1, $score2)
293
    {
294
        if (!isset($score1)) {
295
            return isset($score2) ? 1 : 0;
296
        } elseif (!isset($score2)) {
297
            return -1;
298
        } elseif (($score1[0] / $score1[1]) == ($score2[0] / $score2[1])) {
299
            return 0;
300
        } else {
301
            return ($score1[0] / $score1[1]) < ($score2[0] / $score2[1]) ? -1 : 1;
302
        }
303
    }
304
305
    /**
306
     * @param $item
307
     *
308
     * @return mixed
309
     */
310
    private function build_course_name($item)
311
    {
312
        return $this->get_course_name_from_code_cached($item->get_course_code());
313
    }
314
315
    /**
316
     * @param $item
317
     *
318
     * @return string
319
     */
320
    private function build_category_name($item)
321
    {
322
        $cat = $this->get_category_cached($item->get_category_id());
323
324
        return $this->get_category_name_to_display($cat);
325
    }
326
327
    /**
328
     * @param $item
329
     * @param $ignore_score_color
330
     *
331
     * @return string
332
     */
333
    private function build_average_column($item, $ignore_score_color)
334
    {
335
        if (isset($this->avgcache)) {
336
            $avgscore = $this->avgcache[$item->get_item_type().$item->get_id()];
337
        } else {
338
            $avgscore = $item->calc_score('', 'average');
339
        }
340
        $scoredisplay = ScoreDisplay::instance();
341
        $displaytype = SCORE_AVERAGE;
342
343
        return $scoredisplay->display_score($avgscore, $displaytype);
344
    }
345
346
    /**
347
     * @param $item
348
     * @param $ignore_score_color
349
     *
350
     * @return string
351
     */
352
    private function build_result_column($item, $ignore_score_color)
353
    {
354
        $studscore = $this->scorecache[$item->get_item_type().$item->get_id()];
355
        $scoredisplay = ScoreDisplay::instance();
356
        $displaytype = SCORE_DIV_PERCENT;
357
        if ($ignore_score_color) {
358
            $displaytype |= SCORE_IGNORE_SPLIT;
359
        }
360
361
        return $scoredisplay->display_score(
362
            $studscore,
363
            $displaytype,
364
            SCORE_ONLY_DEFAULT
365
        );
366
    }
367
368
    /**
369
     * @param $item
370
     * @param $ignore_score_color
371
     *
372
     * @return string
373
     */
374
    private function build_mask_column($item, $ignore_score_color)
375
    {
376
        $studscore = $this->scorecache[$item->get_item_type().$item->get_id()];
377
        $scoredisplay = ScoreDisplay::instance();
378
        $displaytype = SCORE_DIV_PERCENT;
379
        if ($ignore_score_color) {
380
            $displaytype |= SCORE_IGNORE_SPLIT;
381
        }
382
383
        return $scoredisplay->display_score(
384
            $studscore,
385
            $displaytype,
386
            SCORE_ONLY_CUSTOM
387
        );
388
    }
389
390
    /**
391
     * @param string $coursecode
392
     *
393
     * @return mixed
394
     */
395
    private function get_course_name_from_code_cached($coursecode)
396
    {
397
        if (isset($this->coursecodecache) &&
398
            isset($this->coursecodecache[$coursecode])
399
        ) {
400
            return $this->coursecodecache[$coursecode];
401
        } else {
402
            $name = CourseManager::getCourseNameFromCode($coursecode);
403
            $this->coursecodecache[$coursecode] = $name;
404
405
            return $name;
406
        }
407
    }
408
409
    /**
410
     * @param int $category_id
411
     */
412
    private function get_category_cached($category_id)
413
    {
414
        if (isset($this->categorycache) &&
415
            isset($this->categorycache[$category_id])
416
        ) {
417
            return $this->categorycache[$category_id];
418
        } else {
419
            $cat = Category::load($category_id);
420
            if (isset($cat)) {
421
                $this->categorycache[$category_id] = $cat[0];
422
423
                return $cat[0];
424
            } else {
425
                return null;
426
            }
427
        }
428
    }
429
430
    /**
431
     * @param $cat
432
     *
433
     * @return string
434
     */
435
    private function get_category_name_to_display($cat)
436
    {
437
        if (isset($cat)) {
438
            if ('0' == $cat->get_parent_id() || null == $cat->get_parent_id()) {
439
                return '';
440
            } else {
441
                return $cat->get_name();
442
            }
443
        }
444
445
        return '';
446
    }
447
}
448