sort_user()   A
last analyzed

Complexity

Conditions 4
Paths 3

Size

Total Lines 11
Code Lines 5

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 4
eloc 5
nc 3
nop 2
dl 0
loc 11
rs 10
c 0
b 0
f 0
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Enums\ActionIcon;
6
use Chamilo\CoreBundle\Enums\ObjectIcon;
7
use Chamilo\CoreBundle\Enums\ToolIcon;
8
use Chamilo\CoreBundle\Framework\Container;
9
use Chamilo\CourseBundle\Entity\CQuiz;
10
11
require_once __DIR__.'/../inc/global.inc.php';
12
13
$toolTable = Database::get_course_table(TABLE_TOOL_LIST);
14
$quizTable = Database::get_course_table(TABLE_QUIZ_TEST);
15
16
$this_section = SECTION_TRACKING;
17
18
$is_allowedToTrack =
19
    api_is_course_admin() ||
20
    api_is_platform_admin(true) ||
21
    api_is_session_general_coach();
22
23
if (!$is_allowedToTrack) {
24
    api_not_allowed(true);
25
}
26
27
$exportToXLS = isset($_GET['export']);
28
$courseInfo = api_get_course_info();
29
30
$global = false;
31
if (api_is_platform_admin() && empty($_GET['cidReq'])) {
32
    // Global reporting: all courses.
33
    $global = true;
34
}
35
36
$courseList = [];
37
if ($global) {
38
    $temp = CourseManager::get_courses_list();
39
    foreach ($temp as $tempCourse) {
40
        $courseList[] = api_get_course_entity($tempCourse['real_id']);
41
    }
42
} else {
43
    // Course context: current course only.
44
    $courseList = [api_get_course_entity()];
45
}
46
47
$sessionId = api_get_session_id();
48
49
if (empty($sessionId)) {
50
    $sessionCondition = ' AND session_id = 0';
51
} else {
52
    $sessionCondition = api_get_session_condition($sessionId, true, true);
53
}
54
55
// -----------------------------------------------------------------------------
56
// Filter form
57
// -----------------------------------------------------------------------------
58
$form = new FormValidator(
59
    'search_simple',
60
    'POST',
61
    api_get_self().'?'.api_get_cidreq(),
62
    '',
63
    null,
64
    false
65
);
66
67
// Percentage filter – use a narrow input, since the value is between 0 and 100.
68
$form->addElement(
69
    'number',
70
    'score',
71
    get_lang('Percentage'),
72
    [
73
        'min' => 0,
74
        'max' => 100,
75
        'step' => 1,
76
        'style' => 'max-width: 6rem;',
77
    ]
78
);
79
80
if ($global) {
81
    $form->addElement('hidden', 'view', 'admin');
82
} else {
83
    // Course context – add optional test selector.
84
    $courseId = api_get_course_int_id();
85
86
    $sql = "SELECT quiz.title, iid
87
            FROM $quizTable AS quiz
88
            WHERE
89
                c_id = $courseId AND
90
                active = 1
91
                $sessionCondition
92
            ORDER BY quiz.title ASC";
93
    $result = Database::query($sql);
94
95
    if (Database::num_rows($result) > 0) {
96
        $exerciseList = [get_lang('All')];
97
        while ($row = Database::fetch_array($result)) {
98
            $exerciseList[$row['iid']] = $row['title'];
99
        }
100
101
        $form->addSelect('exercise_id', get_lang('Test'), $exerciseList);
102
    }
103
}
104
105
$form->addButton(
106
    'filter',
107
    get_lang('Filter'),
108
    'filter',
109
    'primary',
110
    null,
111
    null,
112
    [
113
        'style' => 'margin-top: 15px;',
114
    ]
115
);
116
117
$filter_score = isset($_REQUEST['score']) ? (int) $_REQUEST['score'] : 70;
118
$exerciseId = isset($_REQUEST['exercise_id']) ? (int) $_REQUEST['exercise_id'] : 0;
119
120
$form->setDefaults(['score' => $filter_score]);
121
122
// -----------------------------------------------------------------------------
123
// Build main HTML table and export array
124
// -----------------------------------------------------------------------------
125
$html = '<div class="table-responsive">';
126
$html .= '<table class="table table-hover table-striped data_table">';
127
128
if ($global) {
129
    $html .= '<tr>';
130
    $html .= '<th>'.get_lang('Courses').'</th>';
131
    $html .= '<th>'.get_lang('Tests').'</th>';
132
    $html .= '<th>'.get_lang('Taken').'</th>';
133
    $html .= '<th>'.get_lang('Not taken').'</th>';
134
    $html .= '<th>'.sprintf(get_lang('Pass minimum %s'), $filter_score).'%</th>';
135
    $html .= '<th>'.get_lang('Fail').'</th>';
136
    $html .= '<th>'.get_lang('Total learners').'</th>';
137
    $html .= '</tr>';
138
} else {
139
    $html .= '<tr>';
140
    $html .= '<th>'.get_lang('Tests').'</th>';
141
    $html .= '<th>'.get_lang('User').'</th>';
142
    $html .= '<th>'.get_lang('Username').'</th>';
143
    $html .= '<th>'.get_lang('Percentage').' %</th>';
144
    $html .= '<th>'.get_lang('Status').'</th>';
145
    $html .= '<th>'.get_lang('Attempts').'</th>';
146
    $html .= '</tr>';
147
}
148
149
$export_array_global = [];
150
151
if (!empty($courseList)) {
152
    $quizRepo = Container::getQuizRepository();
153
154
    /** @var Course $course */
155
    foreach ($courseList as $course) {
156
        $courseId = $course->getId();
157
        $courseInfo = api_get_course_info_by_id($courseId);
158
159
        if (empty($courseInfo)) {
160
            continue;
161
        }
162
163
        $sessionList = SessionManager::get_session_by_course($courseId);
164
        $newSessionList = [];
165
166
        if (!empty($sessionList)) {
167
            foreach ($sessionList as $session) {
168
                $newSessionList[$session['id']] = $session['title'];
169
            }
170
        }
171
172
        // Check quiz tool visibility in this course.
173
        $sql = "SELECT visibility FROM $toolTable
174
                WHERE c_id = $courseId AND title = 'quiz'";
175
        $result = Database::query($sql);
176
        $toolVisible = (Database::num_rows($result) > 0)
177
            ? (int) Database::result($result, 0, 'visibility')
178
            : 0;
179
180
        if (1 === $toolVisible) {
181
            // Fetch exams depending on context.
182
            if ($global) {
183
                $qb = $quizRepo->getResourcesByCourse($course);
184
                $exercises = $qb->getQuery()->getResult();
185
            } else {
186
                if (!empty($exerciseId)) {
187
                    $exercises = [];
188
                    $found = $quizRepo->find($exerciseId);
189
                    if ($found instanceof CQuiz) {
190
                        $exercises[] = $found;
191
                    }
192
                } else {
193
                    $qb = $quizRepo->getResourcesByCourse($course, api_get_session_entity());
194
                    $exercises = $qb->getQuery()->getResult();
195
                }
196
            }
197
198
            if (!empty($exercises)) {
199
                /** @var CQuiz $exercise */
200
                foreach ($exercises as $exercise) {
201
                    $links = $exercise->getResourceNode()->getResourceLinks();
202
203
                    $exerciseSessionId = null;
204
                    foreach ($links as $link) {
205
                        if ($link->hasSession()) {
206
                            $exerciseSessionId = $link->getSession()->getId();
207
                            break;
208
                        }
209
                    }
210
211
                    // Base-course exam replicated across sessions.
212
                    if (empty($exerciseSessionId)) {
213
                        if ($global) {
214
                            // Per session.
215
                            foreach ($newSessionList as $currentSessionId => $sessionName) {
216
                                $resultRow = processStudentList(
217
                                    $filter_score,
218
                                    $global,
219
                                    $exercise,
220
                                    $courseInfo,
221
                                    $currentSessionId,
222
                                    $newSessionList
223
                                );
224
225
                                $html .= $resultRow['html'];
226
                                $export_array_global = array_merge(
227
                                    $export_array_global,
228
                                    $resultRow['export_array_global']
229
                                );
230
                            }
231
232
                            // Base course (no session).
233
                            $resultRow = processStudentList(
234
                                $filter_score,
235
                                $global,
236
                                $exercise,
237
                                $courseInfo,
238
                                0,
239
                                $newSessionList
240
                            );
241
242
                            $html .= $resultRow['html'];
243
                            $export_array_global = array_merge(
244
                                $export_array_global,
245
                                $resultRow['export_array_global']
246
                            );
247
                        } else {
248
                            // Course context (teacher view).
249
                            if (empty($sessionId)) {
250
                                $resultRow = processStudentList(
251
                                    $filter_score,
252
                                    $global,
253
                                    $exercise,
254
                                    $courseInfo,
255
                                    0,
256
                                    $newSessionList
257
                                );
258
                            } else {
259
                                $resultRow = processStudentList(
260
                                    $filter_score,
261
                                    $global,
262
                                    $exercise,
263
                                    $courseInfo,
264
                                    $sessionId,
265
                                    $newSessionList
266
                                );
267
                            }
268
269
                            $html .= $resultRow['html'];
270
271
                            if (is_array($resultRow['export_array_global'])) {
272
                                $export_array_global = array_merge(
273
                                    $export_array_global,
274
                                    $resultRow['export_array_global']
275
                                );
276
                            }
277
                        }
278
                    } else {
279
                        // Exam exists only inside a specific session.
280
                        $resultRow = processStudentList(
281
                            $filter_score,
282
                            $global,
283
                            $exercise,
284
                            $courseInfo,
285
                            $exerciseSessionId,
286
                            $newSessionList
287
                        );
288
289
                        $html .= $resultRow['html'];
290
                        $export_array_global = array_merge(
291
                            $export_array_global,
292
                            $resultRow['export_array_global']
293
                        );
294
                    }
295
                }
296
            } else {
297
                // No exams in this course.
298
                if ($global) {
299
                    $html .= '<tr>';
300
                    $html .= '<td>'.Security::remove_XSS($course->getTitle()).'</td>';
301
                    $html .= '<td colspan="6">'.get_lang('There is no test for the moment').'</td>';
302
                    $html .= '</tr>';
303
                } else {
304
                    $html .= '<tr>
305
                                <td colspan="6">'.
306
                        get_lang('There is no test for the moment').
307
                        '</td>
308
                              </tr>';
309
                }
310
            }
311
        } else {
312
            // Quiz tool is not visible in this course.
313
            if ($global) {
314
                $html .= '<tr>';
315
                $html .= '<td>'.Security::remove_XSS($course->getTitle()).'</td>';
316
                $html .= '<td colspan="6">'.get_lang('There is no test for the moment').'</td>';
317
                $html .= '</tr>';
318
            } else {
319
                $html .= '<tr>
320
                            <td colspan="6">'.
321
                    get_lang('There is no test for the moment').
322
                    '</td>
323
                          </tr>';
324
            }
325
        }
326
    }
327
}
328
329
$html .= '</table>';
330
$html .= '</div>';
331
332
// -----------------------------------------------------------------------------
333
// Export handling
334
// -----------------------------------------------------------------------------
335
$filename = 'exam-reporting-'.api_get_local_time().'.xlsx';
336
337
if ($exportToXLS) {
338
    export_complete_report_xls($filename, $export_array_global);
339
340
    exit;
341
}
342
343
// -----------------------------------------------------------------------------
344
// Page rendering (normal HTML view)
345
// -----------------------------------------------------------------------------
346
Display::display_header(get_lang('Reporting'));
347
348
// Top-level My Space menu (main navigation for reporting).
349
$primaryMenu = Display::mySpaceMenu('exams');
350
351
// Secondary navigation: exam-specific menu (icons row).
352
$sessionForToolbar = $global ? 0 : api_get_session_id();
353
$examMenu = TrackingCourseLog::actionsLeft('exams', $sessionForToolbar, $global);
354
355
// Right side: export + print.
356
$queryBase = api_get_self().'?';
357
if (!$global) {
358
    $queryBase .= api_get_cidreq().'&';
359
}
360
361
$exportUrl = $queryBase.'export=1&score='.$filter_score.'&exercise_id='.$exerciseId;
362
363
$actionsRight = Display::url(
364
    Display::getMdiIcon(
365
        ActionIcon::EXPORT_SPREADSHEET,
366
        'ch-tool-icon',
367
        null,
368
        ICON_SIZE_MEDIUM,
369
        get_lang('Excel export')
370
    ),
371
    $exportUrl
372
);
373
$actionsRight .= Display::url(
374
    Display::getMdiIcon(
375
        ActionIcon::PRINT,
376
        'ch-tool-icon',
377
        null,
378
        ICON_SIZE_MEDIUM,
379
        get_lang('Print')
380
    ),
381
    'javascript: void(0);',
382
    ['onclick' => 'javascript: window.print();']
383
);
384
385
// Toolbar with exam local menu on the left and actions on the right.
386
$toolbar = Display::toolbarAction('toolbar-exams', [$examMenu, $actionsRight]);
387
388
// Main layout wrapper.
389
echo '<div class="w-full px-4 md:px-8 pb-8 space-y-4">';
390
391
// Row 1: primary My Space menu.
392
echo '  <div class="flex flex-wrap gap-2">';
393
echo        $primaryMenu;
394
echo '  </div>';
395
396
// Row 2: exam toolbar (local icons + export/print).
397
echo '  <div class="flex flex-wrap gap-2">';
398
echo        $toolbar;
399
echo '  </div>';
400
401
// Filter card.
402
echo '  <section class="bg-white rounded-xl shadow-sm border border-gray-50">';
403
echo '      <div class="p-4 md:p-5">';
404
$form->display();
405
echo '          <p class="mt-3 font-semibold">'.
406
    sprintf(get_lang('Filtering with score %s'), $filter_score).'%</p>';
407
echo '      </div>';
408
echo '  </section>';
409
410
// Results table card.
411
echo '  <section class="bg-white rounded-xl shadow-sm border border-gray-50 overflow-x-auto">';
412
echo        $html;
413
echo '  </section>';
414
415
echo '</div>';
416
417
Display::display_footer();
418
419
/**
420
 * Compare two users by score (used when sorting student results).
421
 *
422
 * @param array $a
423
 * @param array $b
424
 *
425
 * @return int
426
 */
427
function sort_user($a, $b)
428
{
429
    if (is_numeric($a['score']) && is_numeric($b['score'])) {
430
        if ($a['score'] < $b['score']) {
431
            return 1;
432
        }
433
434
        return 0;
435
    }
436
437
    return 1;
438
}
439
440
/**
441
 * Export complete report to XLS.
442
 *
443
 * @param string $filename
444
 * @param array  $array
445
 */
446
function export_complete_report_xls($filename, $array)
447
{
448
    global $global, $filter_score;
449
450
    $list = [];
451
452
    if ($global) {
453
        $headers = [];
454
        $headers[] = get_lang('Courses');
455
        $headers[] = get_lang('Tests');
456
        $headers[] = get_lang('Taken');
457
        $headers[] = get_lang('Not taken');
458
        $headers[] = sprintf(get_lang('Pass minimum %s'), $filter_score).'%';
459
        $headers[] = get_lang('Fail');
460
        $headers[] = get_lang('Total learners');
461
462
        $list[] = $headers;
463
464
        foreach ($array as $row) {
465
            $listItem = [];
466
            foreach ($row as $item) {
467
                $listItem[] = html_entity_decode(strip_tags($item));
468
            }
469
            $list[] = $listItem;
470
        }
471
    } else {
472
        $headers = [];
473
        $headers[] = get_lang('Tests');
474
        $headers[] = get_lang('User');
475
        $headers[] = get_lang('Username');
476
        $headers[] = get_lang('Percentage');
477
        $headers[] = get_lang('Status');
478
        $headers[] = get_lang('Attempts');
479
480
        $list[] = $headers;
481
482
        foreach ($array as $row) {
483
            $listItem = [];
484
            $listItem[] = html_entity_decode(strip_tags($row['exercise']));
485
486
            foreach ($row['users'] as $key => $user) {
487
                $listItem[] = html_entity_decode(strip_tags($user));
488
                $listItem[] = $row['usernames'][$key];
489
                foreach ($row['results'][$key] as $resultItem) {
490
                    $listItem[] = html_entity_decode(strip_tags($resultItem));
491
                }
492
            }
493
494
            $list[] = $listItem;
495
        }
496
    }
497
498
    Export::arrayToXls($list, $filename);
499
}
500
501
/**
502
 * Process exam statistics for a given exam and session.
503
 *
504
 * @param int   $filter_score
505
 * @param bool  $global
506
 * @param CQuiz $exercise
507
 * @param array $courseInfo
508
 * @param int   $sessionId
509
 * @param array $newSessionList
510
 *
511
 * @return array{html:string, export_array_global:array, total_students:int}
512
 */
513
function processStudentList(
514
    $filter_score,
515
    $global,
516
    Cquiz $exercise,
517
    $courseInfo,
518
    $sessionId,
519
    $newSessionList
520
) {
521
    $exerciseStatsTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
522
    $courseId = $courseInfo['real_id'];
523
524
    if (empty($sessionId)) {
525
        $students = CourseManager::get_student_list_from_course_code(
526
            $courseInfo['code'],
527
            false,
528
            0,
529
            null,
530
            null,
531
            false
532
        );
533
    } else {
534
        $students = CourseManager::get_student_list_from_course_code(
535
            $courseInfo['code'],
536
            true,
537
            $sessionId,
538
            null,
539
            null,
540
            false
541
        );
542
    }
543
544
    $html = '';
545
    $totalStudents = count($students);
546
547
    if ($global) {
548
        // One row per course + exam (+ session).
549
        $html .= '<tr>';
550
        $html .= '<td>'.Security::remove_XSS($courseInfo['title']).'</td>';
551
        $html .= '<td>';
552
553
        $html .= Security::remove_XSS($exercise->getTitle());
554
555
        if (!empty($sessionId)) {
556
            $sessionName = $newSessionList[$sessionId] ?? null;
557
            if (!empty($sessionName)) {
558
                $html .= ' '.Display::getMdiIcon(
559
                        ObjectIcon::STAR,
560
                        'ch-tool-icon',
561
                        null,
562
                        ICON_SIZE_SMALL,
563
                        get_lang('Session')
564
                    ).' ('.$sessionName.')';
565
            }
566
        }
567
568
        $html .= '</td>';
569
    } else {
570
        // Course context: first column is the exam title with rowspan.
571
        $html .= '<tr>';
572
        $html .= '<td rowspan="'.$totalStudents.'">';
573
        $html .= Security::remove_XSS($exercise->getTitle());
574
        $html .= '</td>';
575
    }
576
577
    $globalRow = [
578
        $courseInfo['title'],
579
        $exercise->getTitle(),
580
    ];
581
582
    $totalWithParameterScore = 0;
583
    $taken = 0;
584
    $export_array_global = [];
585
    $studentResult = [];
586
    $export_array = [];
587
588
    $exerciseId = $exercise->getIid();
589
    $sessionCondition = api_get_session_condition($sessionId);
590
591
    foreach ($students as $student) {
592
        $studentId = isset($student['user_id']) ? (int) $student['user_id'] : (int) $student['id_user'];
593
594
        $sql = "SELECT COUNT(exe_id) AS count
595
                FROM $exerciseStatsTable
596
                WHERE
597
                    c_id = $courseId AND
598
                    exe_exo_id = $exerciseId AND
599
                    exe_user_id = $studentId
600
                    $sessionCondition";
601
        $result = Database::query($sql);
602
        $attempts = Database::fetch_array($result);
603
604
        $sql = "SELECT exe_id, score, max_score
605
                FROM $exerciseStatsTable
606
                WHERE
607
                    exe_user_id = $studentId AND
608
                    c_id = $courseId AND
609
                    exe_exo_id = $exerciseId AND
610
                    session_id = $sessionId
611
                ORDER BY score DESC
612
                LIMIT 1";
613
        $result = Database::query($sql);
614
615
        $score = 0;
616
        $weighting = 0;
617
618
        while ($scoreInfo = Database::fetch_array($result)) {
619
            $score += $scoreInfo['score'];
620
            $weighting += $scoreInfo['max_score'];
621
        }
622
623
        $percentageScore = 0;
624
625
        // Protect against zero or "0" weighting values.
626
        if (!empty($weighting) && (float) $weighting !== 0.0) {
627
            $percentageScore = round(($score * 100) / (float) $weighting);
628
        }
629
630
        if ($attempts['count'] > 0) {
631
            $taken++;
632
        }
633
634
        if ($percentageScore >= $filter_score) {
635
            $totalWithParameterScore++;
636
        }
637
638
        $tempArray = [];
639
640
        if (!$global) {
641
            $userInfo = api_get_user_info($studentId);
642
643
            $userRow = '<td>'.$userInfo['complete_name'].'</td>';
644
            $userRow .= '<td>'.$userInfo['username'].'</td>';
645
646
            if (!empty($attempts['count'])) {
647
                $userRow .= '<td>'.$percentageScore.'</td>';
648
                $tempArray[] = $percentageScore;
649
650
                if ($percentageScore >= $filter_score) {
651
                    $userRow .= '<td style="background-color:#DFFFA8">';
652
                    $userRow .= get_lang('Pass').'</td>';
653
                    $tempArray[] = get_lang('Pass');
654
                } else {
655
                    $userRow .= '<td style="background-color:#FC9A9E">';
656
                    $userRow .= get_lang('Fail').'</td>';
657
                    $tempArray[] = get_lang('Fail');
658
                }
659
660
                $userRow .= '<td>'.$attempts['count'].'</td>';
661
                $tempArray[] = $attempts['count'];
662
            } else {
663
                $userRow .= '<td>-</td>';
664
                $tempArray[] = '-';
665
666
                $userRow .= '<td style="background-color:#FCE89A">';
667
                $userRow .= get_lang('No attempts').'</td>';
668
                $tempArray[] = get_lang('No attempts');
669
670
                $userRow .= '<td>0</td>';
671
                $tempArray[] = 0;
672
            }
673
674
            $userRow .= '</tr>';
675
676
            $studentResult[$studentId] = [
677
                'html' => $userRow,
678
                'score' => $score,
679
                'array' => $tempArray,
680
                'user' => $userInfo['complete_name'],
681
                'username' => $userInfo['username'],
682
            ];
683
        }
684
    }
685
686
    $row_not_global['exercise'] = $exercise->getTitle();
0 ignored issues
show
Comprehensibility Best Practice introduced by
$row_not_global was never initialized. Although not strictly required by PHP, it is generally a good practice to add $row_not_global = array(); before regardless.
Loading history...
687
688
    if (!$global) {
689
        if (!empty($studentResult)) {
690
            $studentResultEmpty = [];
691
            $studentResultContent = [];
692
693
            foreach ($studentResult as $row) {
694
                if ('-' === $row['score']) {
695
                    $studentResultEmpty[] = $row;
696
                } else {
697
                    $studentResultContent[] = $row;
698
                }
699
            }
700
701
            // Sort only users with an actual score.
702
            usort($studentResultContent, 'sort_user');
703
            $studentResult = array_merge($studentResultContent, $studentResultEmpty);
704
705
            foreach ($studentResult as $row) {
706
                $html .= $row['html'];
707
                $row_not_global['results'][] = $row['array'];
708
                $row_not_global['users'][] = $row['user'];
709
                $row_not_global['usernames'][] = $row['username'];
710
            }
711
712
            $export_array[] = $row_not_global;
713
        }
714
    }
715
716
    if ($global) {
717
        // Exam taken.
718
        $html .= '<td>'.$taken.'</td>';
719
        $globalRow[] = $taken;
720
721
        // Exam not taken.
722
        $notTaken = $totalStudents - $taken;
723
        $html .= '<td>'.$notTaken.'</td>';
724
        $globalRow[] = $notTaken;
725
726
        // Exam passed (>= filter score).
727
        if (!empty($totalWithParameterScore)) {
728
            $html .= '<td style="background-color:#DFFFA8">'.$totalWithParameterScore.'</td>';
729
        } else {
730
            $html .= '<td style="background-color:#FCE89A">'.$totalWithParameterScore.'</td>';
731
        }
732
        $globalRow[] = $totalWithParameterScore;
733
734
        // Exam failed.
735
        $fail = $taken - $totalWithParameterScore;
736
        $html .= '<td>'.$fail.'</td>';
737
        $globalRow[] = $fail;
738
739
        // Total learners.
740
        $html .= '<td>'.$totalStudents.'</td>';
741
        $globalRow[] = $totalStudents;
742
743
        $html .= '</tr>';
744
        $export_array_global[] = $globalRow;
745
    }
746
747
    return [
748
        'html' => $html,
749
        'export_array_global' => $global ? $export_array_global : $export_array,
750
        'total_students' => $totalStudents,
751
    ];
752
}
753