Completed
Push — master ( 9b8b24...6e1754 )
by Julito
58:58
created

GradebookTable   F

Complexity

Total Complexity 165

Size/Duplication

Total Lines 1101
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 1101
rs 0.6314
c 0
b 0
f 0
wmc 165

13 Methods

Rating   Name   Duplication   Size   Complexity  
A build_id_column() 0 12 4
F __construct() 0 98 17
A getDataForGraph() 0 3 1
F get_table_data() 0 659 106
B getGraph() 0 102 6
A build_course_code() 0 3 1
A get_data() 0 3 1
A get_total_number_of_items() 0 3 1
A build_weight() 0 3 1
D build_name_link() 0 89 21
A build_certificate_min_score() 0 3 1
A build_type_column() 0 3 1
A build_edit_column() 0 12 4

How to fix   Complexity   

Complex Class

Complex classes like GradebookTable often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use GradebookTable, and based on these observations, apply Extract Interface, too.

1
<?php
2
/* For licensing terms, see license.txt */
3
4
use ChamiloSession as Session;
5
use CpChart\Cache as pCache;
6
use CpChart\Data as pData;
7
use CpChart\Image as pImage;
8
9
/**
10
 * GradebookTable Class
11
 * Table to display categories, evaluations and links
12
 * @author Stijn Konings
13
 * @author Bert Steppé (refactored, optimised)
14
 * @package chamilo.gradebook
15
 */
16
class GradebookTable extends SortableTable
17
{
18
    private $currentcat;
19
    private $datagen;
20
    private $evals_links;
21
    private $dataForGraph;
22
    public $cats;
23
    public $exportToPdf;
24
    public $teacherView;
25
    public $userId;
26
    public $studentList;
27
28
    /**
29
     * GradebookTable constructor.
30
     * @param Category $currentcat
31
     * @param array $cats
32
     * @param array $evals
33
     * @param array $links
34
     * @param null $addparams
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $showTeacherView is correct as it would always require null to be passed?
Loading history...
Documentation Bug introduced by
Are you sure the doc-type for parameter $addparams is correct as it would always require null to be passed?
Loading history...
35
     * @param bool $exportToPdf
36
     * @param null $showTeacherView
37
     * @param int $userId
38
     * @param array $studentList
39
     */
40
    public function __construct(
41
        $currentcat,
42
        $cats = array(),
43
        $evals = array(),
44
        $links = array(),
45
        $addparams = null,
46
        $exportToPdf = false,
47
        $showTeacherView = null,
48
        $userId = null,
49
        $studentList = array()
0 ignored issues
show
Unused Code introduced by
The parameter $studentList is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

49
        /** @scrutinizer ignore-unused */ $studentList = array()

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
50
    ) {
51
        $this->teacherView = is_null($showTeacherView) ? api_is_allowed_to_edit(null, true) : $showTeacherView;
52
        $this->userId = is_null($userId) ? api_get_user_id() : $userId;
53
        $this->exportToPdf = $exportToPdf;
54
55
        parent::__construct(
56
            'gradebooklist',
57
            null,
58
            null,
59
            api_is_allowed_to_edit() ? 1 : 0,
60
            20,
61
            'ASC',
62
            'gradebook_list'
63
        );
64
65
        $this->evals_links = array_merge($evals, $links);
66
        $this->currentcat = $currentcat;
67
        $this->cats = $cats;
68
        $this->datagen = new GradebookDataGenerator($cats, $evals, $links);
69
70
        if (!empty($userId)) {
71
            $this->datagen->userId = $userId;
72
        }
73
74
        if (isset($addparams)) {
75
            $this->set_additional_parameters($addparams);
76
        }
77
78
        $column = 0;
79
        if ($this->teacherView) {
80
            if ($this->exportToPdf == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
81
                $this->set_header($column++, '', '', 'width="25px"');
82
            }
83
        }
84
85
        $this->set_header($column++, get_lang('Type'), '', 'width="35px"');
86
        $this->set_header($column++, get_lang('Name'), false);
87
88
        if ($this->exportToPdf == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
89
            $this->set_header($column++, get_lang('Description'), false);
90
        }
91
92
        $model = ExerciseLib::getCourseScoreModel();
93
94
        if ($this->teacherView) {
95
            $this->set_header(
96
                $column++,
97
                get_lang('Weight'),
98
                '',
99
                'width="100px"'
100
            );
101
        } else {
102
            $this->set_header($column++, get_lang('Weight'), false);
103
            $this->set_header($column++, get_lang('Result'), false);
104
            if (empty($model)) {
105
                $this->set_header($column++, get_lang('Ranking'), false);
106
                $this->set_header($column++, get_lang('BestScore'), false);
107
                $this->set_header($column++, get_lang('Average'), false);
108
            }
109
110
            if (!empty($cats)) {
111
                if ($this->exportToPdf == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
112
                    $this->set_header($column++, get_lang('Actions'), false);
113
                }
114
            }
115
        }
116
117
        // Deactivates the odd/even alt rows in order that the +/- buttons work see #4047
118
        $this->odd_even_rows_enabled = false;
119
120
        // Admins get an edit column.
121
        if ($this->teacherView) {
122
            $this->set_header($column++, get_lang('Modify'), false, 'width="195px"');
123
            // Actions on multiple selected documents.
124
            $this->set_form_actions(
125
                array(
126
                    'setvisible' => get_lang('SetVisible'),
127
                    'setinvisible' => get_lang('SetInvisible'),
128
                    'deleted' => get_lang('DeleteSelected')
129
                )
130
            );
131
        } else {
132
            if (empty($_GET['selectcat']) && !$this->teacherView) {
133
                if ($this->exportToPdf == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
134
                    $this->set_header(
135
                        $column++,
136
                        get_lang('Certificates'),
137
                        false
138
                    );
139
                }
140
            }
141
        }
142
    }
143
144
    /**
145
     * @return GradebookDataGenerator
146
     */
147
    public function get_data()
148
    {
149
        return $this->datagen;
150
    }
151
152
    /**
153
     * Function used by SortableTable to get total number of items in the table
154
     * @return int
155
     */
156
    public function get_total_number_of_items()
157
    {
158
        return $this->datagen->get_total_items_count();
159
    }
160
161
    /**
162
     * Function used by SortableTable to generate the data to display
163
     * @param int $from
164
     * @param int $per_page
165
     * @param int $column
166
     * @param string $direction
167
     * @param int $sort
168
     * @return array|mixed
169
     */
170
    public function get_table_data($from = 1, $per_page = null, $column = null, $direction = null, $sort = null)
171
    {
172
        //variables load in index.php
173
        global $certificate_min_score;
174
        // determine sorting type
175
        $col_adjust = api_is_allowed_to_edit() ? 1 : 0;
176
        // By id
177
        $this->column = 5;
178
179
        switch ($this->column) {
180
            // Type
181
            case (0 + $col_adjust):
182
                $sorting = GradebookDataGenerator::GDG_SORT_TYPE;
183
                break;
184
            case (1 + $col_adjust):
185
                $sorting = GradebookDataGenerator::GDG_SORT_NAME;
186
                break;
187
            case (2 + $col_adjust):
188
                $sorting = GradebookDataGenerator::GDG_SORT_DESCRIPTION;
189
                break;
190
            case (3 + $col_adjust):
191
                $sorting = GradebookDataGenerator::GDG_SORT_WEIGHT;
192
                break;
193
            case (4 + $col_adjust):
194
                $sorting = GradebookDataGenerator::GDG_SORT_DATE;
195
                break;
196
            case (5 + $col_adjust):
197
                $sorting = GradebookDataGenerator::GDG_SORT_ID;
198
                break;
199
        }
200
201
        if ($this->direction == 'DESC') {
202
            $sorting |= GradebookDataGenerator::GDG_SORT_DESC;
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $sorting does not seem to be defined for all execution paths leading up to this point.
Loading history...
203
        } else {
204
            $sorting |= GradebookDataGenerator::GDG_SORT_ASC;
205
        }
206
207
        // Status of user in course.
208
        $user_id = $this->userId;
209
        $course_code = api_get_course_id();
210
        $session_id = api_get_session_id();
211
212
        if (empty($session_id)) {
213
            $statusToFilter = STUDENT;
214
        } else {
215
            $statusToFilter = 0;
216
        }
217
218
        if (empty($this->studentList)) {
219
            $studentList = CourseManager::get_user_list_from_course_code(
220
                $course_code,
221
                $session_id,
222
                null,
223
                null,
224
                $statusToFilter
225
            );
226
            $this->studentList = $studentList;
227
        }
228
229
        $this->datagen->userId = $this->userId;
230
        $data_array = $this->datagen->get_data(
231
            $sorting,
232
            $from,
233
            $this->per_page,
234
            false,
235
            $this->studentList
236
        );
237
238
        // generate the data to display
239
        $sortable_data = array();
240
        $weight_total_links = 0;
241
        $main_cat = Category::load(
242
            null,
243
            null,
244
            $course_code,
245
            null,
246
            null,
247
            $session_id,
248
            'ORDER BY id'
249
        );
250
251
        $total_categories_weight = 0;
252
        $scoredisplay = ScoreDisplay::instance();
253
254
        $totalUserResult = [0, 0];
255
        $totalBest = [0, 0];
256
        $totalAverage = [0, 0];
257
258
        $type = 'detail';
259
        if ($this->exportToPdf) {
260
            $type = 'simple';
261
        }
262
263
        $model = ExerciseLib::getCourseScoreModel();
264
265
        // Categories.
266
        if (!empty($data_array)) {
267
            foreach ($data_array as $data) {
268
                // list of items inside the gradebook (exercises, lps, forums, etc)
269
                $row = array();
270
271
                /** @var AbstractLink $item */
272
                $item = $mainCategory = $data[0];
273
274
                //if the item is invisible, wrap it in a span with class invisible
275
                $invisibility_span_open = api_is_allowed_to_edit() && $item->is_visible() == '0' ? '<span class="text-muted">' : '';
276
                $invisibility_span_close = api_is_allowed_to_edit() && $item->is_visible() == '0' ? '</span>' : '';
277
278
                // Id
279
                if ($this->teacherView) {
280
                    if ($this->exportToPdf == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
281
                        $row[] = $this->build_id_column($item);
282
                    }
283
                }
284
285
                // Type.
286
                $row[] = $this->build_type_column($item);
287
288
                // Name.
289
                if (get_class($item) == 'Category') {
290
                    $row[] = $invisibility_span_open.'<strong>'.$item->get_name().'</strong>'.$invisibility_span_close;
291
                    $main_categories[$item->get_id()]['name'] = $item->get_name();
292
                } else {
293
                    $name = $this->build_name_link($item, $type);
294
                    $row[] = $invisibility_span_open.$name.$invisibility_span_close;
295
                    $main_categories[$item->get_id()]['name'] = $name;
296
                }
297
298
                $this->dataForGraph['categories'][] = $item->get_name();
299
300
                $main_categories[$item->get_id()]['weight'] = $item->get_weight();
301
                $total_categories_weight += $item->get_weight();
302
303
                // Description.
304
                if ($this->exportToPdf == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
305
                    $row[] = $invisibility_span_open.$data[2].$invisibility_span_close;
306
                }
307
308
                // Weight.
309
                $weight = $scoredisplay->display_score(
310
                    array(
311
                        $data['3'],
312
                        $this->currentcat->get_weight()
313
                    ),
314
                    SCORE_SIMPLE,
315
                    SCORE_BOTH,
316
                    true
317
                );
318
319
                if ($this->teacherView) {
320
                    $row[] = $invisibility_span_open.
321
                        Display::tag('p', $weight, array('class' => 'score')).
322
                        $invisibility_span_close;
323
                } else {
324
                    $row[] = $invisibility_span_open.$weight.$invisibility_span_close;
325
                }
326
327
                $category_weight = $item->get_weight();
328
                $mainCategoryWeight = $main_cat[0]->get_weight();
329
330
                if ($this->teacherView) {
331
                    $weight_total_links += $data[3];
332
                } else {
333
                    $cattotal = Category::load($_GET['selectcat']);
334
                    $scoretotal = $cattotal[0]->calc_score($this->userId);
335
                }
336
337
                // Edit (for admins).
338
                if ($this->teacherView) {
339
                    $cat = new Category();
340
                    $show_message = $cat->show_message_resource_delete($item->get_course_code());
341
                    if ($show_message === false) {
342
                        $row[] = $this->build_edit_column($item);
343
                    }
344
                } else {
345
                    $score = $item->calc_score($this->userId);
346
347
                    if (!empty($score[1])) {
348
                        $completeScore = $scoredisplay->display_score($score, SCORE_DIV_PERCENT);
349
                        $score = $score[0] / $score[1] * $item->get_weight();
350
                        $score = $scoredisplay->display_score(array($score, null), SCORE_SIMPLE);
351
                        $scoreToDisplay = Display::tip($score, $completeScore);
352
                    } else {
353
                        $scoreToDisplay = '-';
354
                        $categoryScore = null;
355
                    }
356
357
                    // Students get the results and certificates columns
358
                    if (1) {
359
                        $value_data = isset($data[4]) ? $data[4] : null;
360
                        $best = isset($data['best']) ? $data['best'] : null;
361
                        $average = isset($data['average']) ? $data['average'] : null;
362
                        $ranking = isset($data['ranking']) ? $data['ranking'] : null;
363
364
                        $totalResult = [
365
                            $data['result_score'][0],
366
                            $data['result_score'][1],
367
                        ];
368
369
                        $totalUserResult[0] += $totalResult[0] / ($totalResult[1] ?: 1) * $data[3];
370
                        $totalUserResult[1] += $data[3];
371
372
                        $totalBest = [
373
                            $scoredisplay->format_score($totalBest[0] + $data['best_score'][0]),
374
                            $scoredisplay->format_score($totalBest[1] + $data['best_score'][1]),
375
                        ];
376
377
                        $totalAverage = [
378
                            $data['average_score'][0],
379
                            $data['average_score'][1],
380
                        ];
381
382
                        // Student result
383
                        if (empty($model)) {
384
                            $row[] = $value_data;
385
                        } else {
386
                            $row[] = ExerciseLib::show_score(
387
                                $data['result_score'][0],
388
                                $data['result_score'][1]
389
                            );
390
                        }
391
                        $totalResultAverageValue = strip_tags(
392
                            $scoredisplay->display_score(
393
                                $totalResult,
394
                                SCORE_AVERAGE
395
                            )
396
                        );
397
398
                        $this->dataForGraph['my_result'][] = floatval($totalResultAverageValue);
399
                        $this->dataForGraph['my_result_no_float'][] = $data['result_score'][0];
400
                        $totalAverageValue = strip_tags($scoredisplay->display_score($totalAverage, SCORE_AVERAGE));
401
                        $this->dataForGraph['average'][] = floatval($totalAverageValue);
402
403
                        if (empty($model)) {
404
                            // Ranking
405
                            $row[] = $ranking;
406
                            // Best
407
                            $row[] = $best;
408
                            // Average
409
                            $row[] = $average;
410
                        }
411
412
                        if (get_class($item) == 'Category') {
413
                            if ($this->exportToPdf == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
414
                                $row[] = $this->build_edit_column($item);
415
                            }
416
                        }
417
                    } else {
418
                        $row[] = $scoreToDisplay;
419
420
                        if (!empty($this->cats)) {
421
                            if ($this->exportToPdf == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
422
                                $row[] = $this->build_edit_column($item);
423
                            }
424
                        }
425
                    }
426
                }
427
428
                // Category added.
429
                $sortable_data[] = $row;
430
431
                // Loading children
432
                if (get_class($item) == 'Category') {
433
                    $course_code = api_get_course_id();
434
                    $session_id = api_get_session_id();
435
                    $parent_id = $item->get_id();
436
                    $cats = Category::load(
437
                        $parent_id,
438
                        null,
439
                        null,
440
                        null,
441
                        null,
442
                        null
443
                    );
444
445
                    if (isset($cats[0])) {
446
                        $allcat = $cats[0]->get_subcategories($this->userId, $course_code, $session_id);
447
                        $alleval = $cats[0]->get_evaluations($this->userId);
448
                        $alllink = $cats[0]->get_links($this->userId);
449
450
                        $sub_cat_info = new GradebookDataGenerator($allcat, $alleval, $alllink);
451
                        $sub_cat_info->userId = $user_id;
452
453
                        $data_array2 = $sub_cat_info->get_data(
454
                            $sorting,
455
                            $from,
456
                            $this->per_page,
457
                            false,
458
                            $this->studentList
459
                        );
460
                        $total_weight = 0;
461
462
                        // Links.
463
                        foreach ($data_array2 as $data) {
464
                            $row = array();
465
                            $item = $data[0];
466
467
                            //if the item is invisible, wrap it in a span with class invisible
468
                            $invisibility_span_open = api_is_allowed_to_edit() && $item->is_visible() == '0' ? '<span class="text-muted">' : '';
469
                            $invisibility_span_close = api_is_allowed_to_edit() && $item->is_visible() == '0' ? '</span>' : '';
470
471
                            if (isset($item)) {
472
                                $main_categories[$parent_id]['children'][$item->get_id()]['name'] = $item->get_name();
473
                                $main_categories[$parent_id]['children'][$item->get_id()]['weight'] = $item->get_weight();
474
                            }
475
476
                            if ($this->teacherView) {
477
                                if ($this->exportToPdf == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
478
                                    $row[] = $this->build_id_column($item);
479
                                }
480
                            }
481
482
                            // Type
483
                            $row[] = $this->build_type_column($item, array('style' => 'padding-left:5px'));
484
485
                            // Name.
486
                            $row[] = $invisibility_span_open."&nbsp;&nbsp;&nbsp;  ".
487
                                $this->build_name_link($item, $type).$invisibility_span_close;
488
489
                            // Description.
490
                            if ($this->exportToPdf == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
491
                                $row[] = $invisibility_span_open.$data[2].$invisibility_span_close;
492
                            }
493
494
                            $weight = $data[3];
495
                            $total_weight += $weight;
496
497
                            // Weight
498
                            $row[] = $invisibility_span_open.$weight.$invisibility_span_close;
499
500
                            if ($this->teacherView) {
0 ignored issues
show
Unused Code introduced by
This if statement is empty and can be removed.

This check looks for the bodies of if statements that have no statements or where all statements have been commented out. This may be the result of changes for debugging or the code may simply be obsolete.

These if bodies can be removed. If you have an empty if but statements in the else branch, consider inverting the condition.

if (rand(1, 6) > 3) {
//print "Check failed";
} else {
    print "Check succeeded";
}

could be turned into

if (rand(1, 6) <= 3) {
    print "Check succeeded";
}

This is much more concise to read.

Loading history...
501
                                //$weight_total_links += intval($data[3]);
502
                            } else {
503
                                $cattotal = Category::load($_GET['selectcat']);
504
                                $scoretotal = $cattotal[0]->calc_score($this->userId);
505
                            }
506
507
                            // Admins get an edit column.
508
                            if (api_is_allowed_to_edit(null, true) &&
509
                                isset($_GET['user_id']) == false &&
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
510
                                (isset($_GET['action']) && $_GET['action'] != 'export_all' || !isset($_GET['action']))
511
                            ) {
512
                                $cat = new Category();
513
                                $show_message = $cat->show_message_resource_delete($item->get_course_code());
514
                                if ($show_message === false) {
515
                                    if ($this->exportToPdf == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
516
                                        $row[] = $this->build_edit_column($item);
517
                                    }
518
                                }
519
                            } else {
520
                                // Students get the results and certificates columns
521
                                $eval_n_links = array_merge($alleval, $alllink);
522
523
                                if (count($eval_n_links) > 0) {
524
                                    $value_data = isset($data[4]) ? $data[4] : null;
525
                                    if (!is_null($value_data)) {
526
                                        //$score = $item->calc_score(api_get_user_id());
527
                                        //$new_score = $data[3] * $score[0] / $score[1];
528
                                        //$new_score = floatval(number_format($new_score, api_get_setting('gradebook_number_decimals')));
529
530
                                        // Result
531
                                        $row[] = $value_data;
532
                                        $best = isset($data['best']) ? $data['best'] : null;
533
                                        $average = isset($data['average']) ? $data['average'] : null;
534
                                        $ranking = isset($data['ranking']) ? $data['ranking'] : null;
535
536
                                        // Ranking
537
                                        $row[] = $ranking;
538
                                        // Best
539
                                        $row[] = $best;
540
                                        // Average
541
                                        $row[] = $average;
542
                                    }
543
                                }
544
545
                                if (!empty($cats)) {
546
                                    if ($this->exportToPdf == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
547
                                        $row[] = null;
548
                                    }
549
                                }
550
                            }
551
552
                            if ($this->exportToPdf == false) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

Loading history...
553
                                $row['child_of'] = $parent_id;
554
                            }
555
                            $sortable_data[] = $row;
556
                        }
557
558
                        // "Warning row"
559
                        if (!empty($data_array)) {
560
                            if ($this->teacherView) {
561
                                // Compare the category weight to the sum of all weights inside the category
562
                                if (intval($total_weight) == $category_weight) {
563
                                    $label = null;
564
                                    $total = GradebookUtils::score_badges(
565
                                        array(
566
                                            $total_weight.' / '.$category_weight,
567
                                            '100'
568
                                        )
569
                                    );
570
                                } else {
571
                                    $label = Display::return_icon(
572
                                        'warning.png',
573
                                        sprintf(get_lang('TotalWeightMustBeX'), $category_weight)
574
                                    );
575
                                    $total = Display::badge($total_weight.' / '.$category_weight, 'warning');
576
                                }
577
                                $row = array(
578
                                    null,
579
                                    null,
580
                                    "&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;<h5>".get_lang('SubTotal').'</h5>',
581
                                    null,
582
                                    $total.' '.$label,
583
                                    'child_of' => $parent_id
584
                                );
585
                                $sortable_data[] = $row;
586
                            }
587
                        }
588
                    }
589
                }
590
            }
591
        } //end looping categories
592
593
        $main_weight = 0;
594
        if (count($main_cat) > 1) {
595
            /** @var Category $myCat */
596
            foreach ($main_cat as $myCat) {
597
                $myParentId = $myCat->get_parent_id();
598
                if ($myParentId == 0) {
599
                    $main_weight = intval($myCat->get_weight());
600
                }
601
            }
602
        }
603
604
        if ($this->teacherView) {
605
            // Total for teacher.
606
            if (count($main_cat) > 1) {
607
                if (intval($total_categories_weight) == $main_weight) {
608
                    $total = GradebookUtils::score_badges(
609
                        array(
610
                            $total_categories_weight.' / '.$main_weight,
611
                            '100'
612
                        )
613
                    );
614
                } else {
615
                    $total = Display::badge($total_categories_weight.' / '.$main_weight, 'warning');
616
                }
617
                $row = array(
618
                    null,
619
                    null,
620
                    '<strong>'.get_lang('Total').'</strong>',
621
                    null,
622
                    $total
623
                );
624
                $sortable_data[] = $row;
625
            }
626
        } else {
627
            // Total for student.
628
            if (count($main_cat) > 1) {
629
                $weights = [];
630
                foreach ($main_categories as $cat) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $main_categories does not seem to be defined for all execution paths leading up to this point.
Loading history...
631
                    $weights[] = $cat['weight'];
632
                }
633
                $main_weight = intval($main_cat[0]->get_weight());
634
                $global = null;
635
                $average = null;
636
                $myTotal = 0;
637
638
                foreach ($this->dataForGraph['my_result_no_float'] as $result) {
639
                    $myTotal += $scoredisplay->format_score($result);
640
                }
641
642
                $totalResult[0] = $myTotal;
643
                // Overwrite main weight
644
                $totalResult[1] = $main_weight;
645
                $totalResult = $scoredisplay->display_score(
646
                    $totalResult,
647
                    SCORE_DIV
648
                );
649
650
                $totalRanking = array();
651
                $invalidateRanking = true;
652
                $average = 0;
653
                foreach ($this->studentList as $student) {
654
                    $score = $main_cat[0]->calc_score($student['user_id']);
655
                    if (!empty($score[0])) {
656
                        $invalidateRanking = false;
657
                    }
658
                    $totalRanking[$student['user_id']] = $score[0];
659
                    $average += $score[0];
660
                }
661
662
                $totalRanking = AbstractLink::getCurrentUserRanking($user_id, $totalRanking);
663
664
                $totalRanking = $scoredisplay->display_score(
665
                    $totalRanking,
666
                    SCORE_DIV,
667
                    SCORE_BOTH,
668
                    true
669
                );
670
671
                if ($invalidateRanking) {
672
                    $totalRanking = null;
673
                }
674
675
                // Overwrite main weight
676
                $totalBest[1] = $main_weight;
677
678
                $totalBest = $scoredisplay->display_score(
679
                    $totalBest,
680
                    SCORE_DIV,
681
                    SCORE_BOTH,
682
                    true
683
                );
684
685
                // Overwrite main weight
686
                $totalAverage[0] = $average / count($this->studentList);
0 ignored issues
show
Bug introduced by
It seems like $this->studentList can also be of type integer; however, parameter $var of count() does only seem to accept Countable|array, maybe add an additional type check? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

686
                $totalAverage[0] = $average / count(/** @scrutinizer ignore-type */ $this->studentList);
Loading history...
687
                $totalAverage[1] = $main_weight;
688
689
                $totalAverage = $scoredisplay->display_score(
690
                    $totalAverage,
691
                    SCORE_DIV,
692
                    SCORE_BOTH,
693
                    true
694
                );
695
696
                if ($this->exportToPdf) {
697
                    $row = array(
698
                        null,
699
                        '<h3>'.get_lang('Total').'</h3>',
700
                        $main_weight,
701
                        $totalResult,
702
                        $totalRanking,
703
                        $totalBest,
704
                        $totalAverage
705
                    );
706
                } else {
707
                    $row = array(
708
                        null,
709
                        '<h3>'.get_lang('Total').'</h3>',
710
                        null,
711
                        $main_weight,
712
                        $totalResult,
713
                        $totalRanking,
714
                        $totalBest,
715
                        $totalAverage
716
                    );
717
                }
718
719
                $sortable_data[] = $row;
720
            }
721
        }
722
723
        // Warning messages
724
        $view = isset($_GET['view']) ? $_GET['view'] : null;
725
726
        if ($this->teacherView) {
727
            if (isset($_GET['selectcat']) &&
728
                $_GET['selectcat'] > 0 &&
729
                $view <> 'presence'
730
            ) {
731
                $id_cat = intval($_GET['selectcat']);
732
                $category = Category::load($id_cat);
733
                $weight_category = intval($this->build_weight($category[0]));
734
                $course_code = $this->build_course_code($category[0]);
735
                $weight_total_links = round($weight_total_links);
736
737
                if ($weight_total_links > $weight_category ||
738
                    $weight_total_links < $weight_category ||
739
                    $weight_total_links > $weight_category
740
                ) {
741
                    $warning_message = sprintf(get_lang('TotalWeightMustBeX'), $weight_category);
742
                    $modify_icons = '<a href="gradebook_edit_cat.php?editcat='.$id_cat.'&cidReq='.$course_code.'&id_session='.api_get_session_id().'">'.
743
                        Display::return_icon('edit.png', $warning_message, array(), ICON_SIZE_SMALL).'</a>';
744
                    $warning_message .= $modify_icons;
745
                    echo Display::return_message($warning_message, 'warning', false);
746
                }
747
748
                $content_html = DocumentManager::replace_user_info_into_html(
749
                    api_get_user_id(),
750
                    $course_code,
751
                    api_get_session_id()
752
                );
753
754
                if (!empty($content_html)) {
755
                    $new_content = explode('</head>', $content_html['content']);
756
                }
757
758
                if (empty($new_content[0])) {
759
                    // Set default certificate
760
                    $courseData = api_get_course_info($course_code);
761
                    DocumentManager::generateDefaultCertificate($courseData);
762
                }
763
            }
764
765
            if (empty($_GET['selectcat'])) {
766
                $categories = Category::load();
767
                $weight_categories = $certificate_min_scores = $course_codes = array();
768
                foreach ($categories as $category) {
769
                    $course_code_category = $this->build_course_code($category);
770
                    if (!empty($course_code)) {
771
                        if ($course_code_category == $course_code) {
772
                            $weight_categories[] = intval($this->build_weight($category));
773
                            $certificate_min_scores[] = intval($this->build_certificate_min_score($category));
774
                            $course_codes[] = $course_code;
775
                            break;
776
                        }
777
                    } else {
778
                        $weight_categories[] = intval($this->build_weight($category));
779
                        $certificate_min_scores[] = intval($this->build_certificate_min_score($category));
780
                        $course_codes[] = $course_code_category;
781
                    }
782
                }
783
784
                if (is_array($weight_categories) &&
785
                    is_array($certificate_min_scores) &&
786
                    is_array($course_codes)
787
                ) {
788
                    $warning_message = '';
789
                    for ($x = 0; $x < count($weight_categories); $x++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
790
                        $weight_category = intval($weight_categories[$x]);
791
                        $certificate_min_score = intval($certificate_min_scores[$x]);
792
                        $course_code = $course_codes[$x];
793
794
                        if (empty($certificate_min_score) ||
795
                            ($certificate_min_score > $weight_category)
796
                        ) {
797
                            $warning_message .= $course_code.'&nbsp;-&nbsp;'.get_lang('CertificateMinimunScoreIsRequiredAndMustNotBeMoreThan').'&nbsp;'.$weight_category.'<br />';
798
                        }
799
                    }
800
801
                    if (!empty($warning_message)) {
802
                        echo Display::return_message($warning_message, 'warning', false);
803
                    }
804
                }
805
            }
806
        }
807
808
        if (!$this->teacherView) {
809
            $rowTotal = [];
810
            $rowTotal[] = ' ';
811
            $rowTotal[] = get_lang('FinalScore');
812
813
            if (!$this->exportToPdf) {
814
                $rowTotal[] = ' ';
815
            }
816
            $rowTotal[] = ' ';
817
            $rowTotal[] = $scoredisplay->display_score(
818
                $totalUserResult,
819
                SCORE_DIV_PERCENT_WITH_CUSTOM
820
            );
821
            $rowTotal[] = ' ';
822
            $rowTotal[] = ' ';
823
            $rowTotal[] = ' ';
824
825
            $sortable_data[] = $rowTotal;
826
        }
827
828
        return $sortable_data;
829
    }
830
831
832
    /**
833
     * @return array
834
     */
835
    private function getDataForGraph()
836
    {
837
        return $this->dataForGraph;
838
    }
839
840
    /**
841
     * @return string
842
     */
843
    public function getGraph()
844
    {
845
        $data = $this->getDataForGraph();
846
        if (!empty($data) &&
847
            isset($data['categories']) &&
848
            isset($data['my_result']) &&
849
            isset($data['average'])
850
        ) {
851
            $dataSet = new pData();
852
            $dataSet->addPoints($data['my_result'], get_lang('Me'));
853
            // In order to generate random values
854
            // $data['average'] = array(rand(0,50), rand(0,50));
855
            $dataSet->addPoints($data['average'], get_lang('Average'));
856
            $dataSet->addPoints($data['categories'], 'categories');
857
858
            $dataSet->setAbscissa("categories");
859
            $xSize = 600;
860
            $ySize = 400;
861
            $pChart = new pImage($xSize, $ySize, $dataSet);
862
            /* Turn of Antialiasing */
863
            $pChart->Antialias = false;
864
865
            /* Add a border to the picture */
866
            $pChart->drawRectangle(
867
                0,
868
                0,
869
                $xSize - 1,
870
                $ySize - 1,
871
                array("R" => 0, "G" => 0, "B" => 0)
872
            );
873
            $pChart->drawText(
874
                10,
875
                16,
876
                get_lang('Results'),
877
                array("FontSize" => 11, "Align" => TEXT_ALIGN_BOTTOMMIDDLE)
878
            );
879
            $pChart->setGraphArea(50, 30, $xSize - 50, $ySize - 70);
880
            $pChart->setFontProperties(
881
                array(
882
                    'FontName' => api_get_path(SYS_FONTS_PATH).'opensans/OpenSans-Regular.ttf',
883
                    'FontSize' => 10,
884
                )
885
            );
886
887
            /* Draw the scale */
888
            $scaleSettings = array(
889
                "XMargin" => AUTO,
890
                "YMargin" => 10,
891
                "Floating" => true,
892
                "GridR" => 200,
893
                "GridG" => 200,
894
                "GridB" => 200,
895
                "DrawSubTicks" => true,
896
                "CycleBackground" => true,
897
                'LabelRotation' => 10
898
            );
899
            $pChart->drawScale($scaleSettings);
900
901
            /* Draw the line chart */
902
            $pChart->drawLineChart();
903
            $pChart->drawPlotChart(
904
                array(
905
                    "DisplayValues" => true,
906
                    "PlotBorder" => true,
907
                    "BorderSize" => 2,
908
                    "Surrounding" => -60,
909
                    "BorderAlpha" => 80,
910
                )
911
            );
912
913
            /* Write the chart legend */
914
            $pChart->drawLegend(
915
                $xSize - 180,
916
                9,
917
                array(
918
                    "Style" => LEGEND_NOBORDER,
919
                    "Mode" => LEGEND_HORIZONTAL,
920
                    "FontR" => 0,
921
                    "FontG" => 0,
922
                    "FontB" => 0,
923
                )
924
            );
925
926
            $cachePath = api_get_path(SYS_ARCHIVE_PATH);
927
            $myCache = new pCache(array('CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)));
928
            $chartHash = $myCache->getHash($dataSet);
929
930
            $myCache->writeToCache($chartHash, $pChart);
931
            $imgSysPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
932
            $myCache->saveFromCache($chartHash, $imgSysPath);
933
            $imgWebPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
934
935
            if (file_exists($imgSysPath)) {
936
                $result = '<div id="contentArea" style="text-align: center;" >';
937
                $result .= '<img src="'.$imgWebPath.'" >';
938
                $result .= '</div>';
939
940
                return $result;
941
            }
942
        }
943
944
        return '';
945
    }
946
947
    /**
948
     * @param $item
949
     * @return mixed
950
     */
951
    private function build_certificate_min_score($item)
952
    {
953
        return $item->getCertificateMinScore();
954
    }
955
956
    /**
957
     * @param $item
958
     * @return mixed
959
     */
960
    private function build_weight($item)
961
    {
962
        return $item->get_weight();
963
    }
964
965
    /**
966
     * @param $item
967
     * @return mixed
968
     */
969
    private function build_course_code($item)
970
    {
971
        return $item->get_course_code();
972
    }
973
974
    /**
975
     * @param $item
976
     * @return string
977
     */
978
    private function build_id_column($item)
979
    {
980
        switch ($item->get_item_type()) {
981
            // category
982
            case 'C':
983
                return 'CATE'.$item->get_id();
984
            // evaluation
985
            case 'E':
986
                return 'EVAL'.$item->get_id();
987
            // link
988
            case 'L':
989
                return 'LINK'.$item->get_id();
990
        }
991
    }
992
993
    /**
994
     * @param $item
995
     * @param array $attributes
996
     * @return string
997
     */
998
    private function build_type_column($item, $attributes = array())
999
    {
1000
        return GradebookUtils::build_type_icon_tag($item->get_icon_name(), $attributes);
1001
    }
1002
1003
    /**
1004
     * Generate name column
1005
     * @param GradebookItem $item
1006
     * @param string $type simple|detail
1007
     * @return string
1008
     */
1009
    private function build_name_link($item, $type = 'detail')
1010
    {
1011
        $view = isset($_GET['view']) ? Security::remove_XSS($_GET['view']) : null;
1012
        $categoryId = $item->getCategory()->get_id();
0 ignored issues
show
Bug introduced by
The method getCategory() does not exist on GradebookItem. It seems like you code against a sub-type of said class. However, the method does not exist in Category. Are you sure you never get one of those? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1012
        $categoryId = $item->/** @scrutinizer ignore-call */ getCategory()->get_id();
Loading history...
1013
1014
        switch ($item->get_item_type()) {
1015
            // category
1016
            case 'C':
1017
                $prms_uri = '?selectcat='.$item->get_id().'&view='.$view;
1018
                $isStudentView = api_is_student_view_active();
1019
                if (isset($is_student) || $isStudentView) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $is_student seems to never exist and therefore isset should always be false.
Loading history...
1020
                    $prms_uri = $prms_uri.'&amp;isStudentView=studentview';
1021
                }
1022
                $cat = new Category();
1023
                $show_message = $cat->show_message_resource_delete($item->get_course_code());
1024
1025
                return '&nbsp;<a href="'.Category::getUrl().$prms_uri.'">'
1026
                    .$item->get_name()
1027
                    .'</a>'
1028
                    .($item->is_course() ? ' &nbsp;['.$item->get_course_code().']'.$show_message : '');
0 ignored issues
show
Bug introduced by
Are you sure $show_message of type false|string can be used in concatenation? ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-type  annotation

1028
                    .($item->is_course() ? ' &nbsp;['.$item->get_course_code().']'./** @scrutinizer ignore-type */ $show_message : '');
Loading history...
Bug introduced by
The method is_course() does not exist on GradebookItem. It seems like you code against a sub-type of GradebookItem such as Category. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1028
                    .($item->/** @scrutinizer ignore-call */ is_course() ? ' &nbsp;['.$item->get_course_code().']'.$show_message : '');
Loading history...
1029
                // evaluation
1030
                //no break because of return
1031
            case 'E':
1032
                $cat = new Category();
1033
                $course_id = CourseManager::get_course_by_category($categoryId);
1034
                $show_message = $cat->show_message_resource_delete($course_id);
1035
1036
                // course/platform admin can go to the view_results page
1037
                if (api_is_allowed_to_edit() && $show_message === false) {
1038
                    if ($item->get_type() == 'presence') {
0 ignored issues
show
Bug introduced by
The method get_type() does not exist on GradebookItem. Since it exists in all sub-types, consider adding an abstract or default implementation to GradebookItem. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1038
                    if ($item->/** @scrutinizer ignore-call */ get_type() == 'presence') {
Loading history...
1039
                        return '&nbsp;'
1040
                            .'<a href="gradebook_view_result.php?cidReq='.$course_id.'&amp;selecteval='.$item->get_id().'">'
1041
                            .$item->get_name()
1042
                            .'</a>';
1043
                    } else {
1044
                        $extra = Display::label(get_lang('Evaluation'));
1045
                        if ($type == 'simple') {
1046
                            $extra = '';
1047
                        }
1048
1049
                        return '&nbsp;'
1050
                            .'<a href="gradebook_view_result.php?'.api_get_cidreq().'&selecteval='.$item->get_id().'">'
1051
                            .$item->get_name()
1052
                            .'</a>&nbsp;'.$extra;
1053
                    }
1054
                } elseif (ScoreDisplay::instance()->is_custom() && $show_message === false) {
1055
                    // students can go to the statistics page (if custom display enabled)
1056
                    return '&nbsp;'
1057
                        .'<a href="gradebook_statistics.php?'.api_get_cidreq().'&selecteval='.$item->get_id().'">'
1058
                        .$item->get_name()
1059
                        .'</a>';
1060
1061
                } elseif ($show_message === false && !api_is_allowed_to_edit() && !ScoreDisplay::instance()->is_custom()) {
1062
                    return '&nbsp;'
1063
                        .'<a href="gradebook_statistics.php?'.api_get_cidreq().'&selecteval='.$item->get_id().'">'
1064
                        .$item->get_name()
1065
                        .'</a>';
1066
                } else {
1067
                    return '['.get_lang('Evaluation').']&nbsp;&nbsp;'.$item->get_name().$show_message;
1068
                }
1069
                // no break because of return
1070
            case 'L':
1071
                // link
1072
                $cat = new Category();
1073
                $course_id = CourseManager::get_course_by_category($categoryId);
1074
                $show_message = $cat->show_message_resource_delete($course_id);
1075
1076
                $url = $item->get_link();
0 ignored issues
show
Bug introduced by
The method get_link() does not exist on GradebookItem. It seems like you code against a sub-type of GradebookItem such as AbstractLink. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1076
                /** @scrutinizer ignore-call */ 
1077
                $url = $item->get_link();
Loading history...
1077
1078
                if (isset($url) && $show_message === false) {
1079
                    $text = '&nbsp;<a href="'.$item->get_link().'">'
1080
                        .$item->get_name()
1081
                        .'</a>';
1082
                } else {
1083
                    $text = $item->get_name();
1084
                }
1085
1086
                $extra = Display::label($item->get_type_name(), 'info');
0 ignored issues
show
Bug introduced by
The method get_type_name() does not exist on GradebookItem. It seems like you code against a sub-type of GradebookItem such as AbstractLink. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

1086
                $extra = Display::label($item->/** @scrutinizer ignore-call */ get_type_name(), 'info');
Loading history...
1087
                if ($type == 'simple') {
1088
                    $extra = '';
1089
                }
1090
1091
                $text .= "&nbsp;".$extra.$show_message;
1092
                $cc = $this->currentcat->get_course_code();
1093
                if (empty($cc)) {
1094
                    $text .= '&nbsp;[<a href="'.api_get_path(REL_COURSE_PATH).$item->get_course_code().'/">'.$item->get_course_code().'</a>]';
1095
                }
1096
1097
                return $text;
1098
        }
1099
    }
1100
1101
    /**
1102
     * @param AbstractLink $item
1103
     * @return null|string
1104
     */
1105
    private function build_edit_column($item)
1106
    {
1107
        switch ($item->get_item_type()) {
1108
            // category
1109
            case 'C':
1110
                return GradebookUtils::build_edit_icons_cat($item, $this->currentcat);
1111
            // evaluation
1112
            case 'E':
1113
                return GradebookUtils::build_edit_icons_eval($item, $this->currentcat->get_id());
1114
            // link
1115
            case 'L':
1116
                return GradebookUtils::build_edit_icons_link($item, $this->currentcat->get_id());
1117
        }
1118
    }
1119
}
1120