Passed
Push — master ( 5d8439...1c5d20 )
by Yannick
16:06 queued 08:16
created

Tracking   F

Complexity

Total Complexity 953

Size/Duplication

Total Lines 8143
Duplicated Lines 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 4600
dl 0
loc 8143
rs 0.8
c 1
b 0
f 0
wmc 953

67 Methods

Rating   Name   Duplication   Size   Complexity  
A count_student_visited_links() 0 19 1
A chat_last_connection() 0 31 2
A chat_connections_during_last_x_days_by_course() 0 33 3
A count_course_per_student() 0 21 2
F getLpStats() 0 1254 178
F generate_exercise_result_thumbnail_graph() 0 181 17
C get_time_spent_on_the_platform() 0 86 14
A countStudentMessages() 0 12 1
F getCalculateTime() 0 159 40
C compareUserData() 0 47 13
F get_avg_student_exercise_score() 0 135 22
A getTotalTimeSpentOnThePlatform() 0 32 5
B minimumTimeAvailable() 0 24 10
A count_number_of_forums_by_course() 0 15 1
A generate_session_exercise_graph() 0 51 1
F generate_exercise_result_graph() 0 177 17
A get_first_connection_date_on_the_course() 0 38 5
A get_course_list_in_session_from_student() 0 20 2
F get_avg_student_score() 0 354 55
A get_exercise_student_progress() 0 40 5
F show_course_detail() 0 408 39
C get_time_spent_in_lp() 0 139 14
B get_ip_from_user_event() 0 32 7
C getCourseLpProgress() 0 67 12
B get_last_connection_time_in_lp() 0 76 10
A get_tools_most_used_by_course() 0 31 4
B get_time_spent_on_the_course() 0 47 8
A get_links_most_visited_by_course() 0 33 4
A getLastConnectionDateByCourse() 0 23 3
B get_last_connection_date() 0 38 6
A count_number_of_posts_by_course() 0 12 1
F get_courses_followed_by_coach() 0 97 12
B get_group_reporting() 0 82 9
A get_courses_list_from_session() 0 22 2
A getFirstConnectionTimeInLp() 0 45 4
B getTotalTimeSpentInCourses() 0 29 6
A getCourseLpFinalizationDate() 0 37 3
A getCourseQuizLastFinalizationDate() 0 29 3
F get_sessions_coached_by_user() 0 141 19
B get_exercise_student_average_best_attempt() 0 30 8
F get_exercise_progress() 0 224 26
A getNumberOfCourseAccessDates() 0 14 1
D get_last_connection_date_on_the_course() 0 108 19
B getInactiveStudentsInCourse() 0 89 11
A isAllowToTrack() 0 8 6
F getStats() 0 306 29
A count_student_uploaded_documents() 0 19 1
C generateQuizzesTable() 0 173 13
A is_allowed_to_coach_student() 0 36 3
A get_first_connection_date() 0 20 3
A setUserSearchForm() 0 27 1
B get_course_connections_count() 0 61 10
A get_documents_most_downloaded_by_course() 0 41 4
A getLastStudentPublication() 0 32 1
A count_number_of_threads_by_course() 0 15 1
B getToolInformation() 0 82 8
F generateLearningPathsTable() 0 197 24
A getLastConnectionInAnyCourse() 0 22 3
A displayUserSkills() 0 8 3
F show_user_progress() 0 831 97
A countStudentDownloadedDocuments() 0 21 2
F get_avg_student_progress() 0 153 18
A count_student_exercise_attempts() 0 41 3
B get_student_followed_by_coach() 0 87 7
F processUserDataMove() 0 591 82
B getAverageStudentScore() 0 66 8
A countStudentPublications() 0 12 1

How to fix   Complexity   

Complex Class

Complex classes like Tracking 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 Tracking, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\TrackEAttemptQualify;
6
use Chamilo\CoreBundle\Entity\Course;
7
use Chamilo\CoreBundle\Entity\ExtraField as EntityExtraField;
8
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
9
use Chamilo\CoreBundle\Entity\TrackEDownloads;
10
use Chamilo\CoreBundle\Entity\User;
11
use Chamilo\CoreBundle\Entity\Usergroup;
12
use Chamilo\CoreBundle\Framework\Container;
13
use Chamilo\CourseBundle\Entity\CLp;
14
use Chamilo\CourseBundle\Entity\CQuiz;
15
use Chamilo\CourseBundle\Entity\CStudentPublication;
16
use ChamiloSession as Session;
17
use CpChart\Cache as pCache;
18
use CpChart\Data as pData;
19
use CpChart\Image as pImage;
20
use ExtraField as ExtraFieldModel;
21
use Chamilo\CoreBundle\Component\Utils\ActionIcon;
22
use Chamilo\CoreBundle\Component\Utils\StateIcon;
23
use Chamilo\CoreBundle\ServiceHelper\AccessUrlHelper;
24
25
/**
26
 *  Class Tracking.
27
 *
28
 *  @author  Julio Montoya <[email protected]>
29
 */
30
class Tracking
31
{
32
    /**
33
     * Get group reporting.
34
     *
35
     * @param int    $course_id
36
     * @param int    $sessionId
37
     * @param int    $group_id
38
     * @param string $type
39
     * @param int    $start
40
     * @param int    $limit
41
     * @param int    $sidx
42
     * @param string $sord
43
     * @param array  $where_condition
44
     *
45
     * @return array|null
46
     */
47
    public static function get_group_reporting(
48
        $courseId,
49
        $sessionId = 0,
50
        $group_id = 0,
51
        $type = 'all',
52
        $start = 0,
53
        $limit = 1000,
54
        $sidx = 1,
55
        $sord = 'desc',
56
        $where_condition = []
57
    ) {
58
        $courseId = (int) $courseId;
59
        $sessionId = (int) $sessionId;
60
61
        if (empty($courseId)) {
62
            return null;
63
        }
64
        $course = api_get_course_entity($courseId);
65
66
        $session = api_get_session_entity($sessionId);
67
        if ('count' === $type) {
68
            return GroupManager::get_group_list(null, $course, null, $sessionId, true);
69
        }
70
71
        $groupList = GroupManager::get_group_list(null, $course, null, $sessionId, false, null, true);
72
        $parsedResult = [];
73
        if (!empty($groupList)) {
74
            foreach ($groupList as $group) {
75
                $users = GroupManager::get_users($group->getIid(), true, null, null, false, $courseId);
76
                $time = 0;
77
                $avg_student_score = 0;
78
                $avg_student_progress = 0;
79
                $work = 0;
80
                $messages = 0;
81
                foreach ($users as $user_data) {
82
                    $user = api_get_user_entity($user_data['user_id']);
83
                    $time += self::get_time_spent_on_the_course(
84
                        $user_data['user_id'],
85
                        $courseId,
86
                        $sessionId
87
                    );
88
                    $average = self::get_avg_student_score(
89
                        $user_data['user_id'],
90
                        $course,
91
                        [],
92
                        $session
93
                    );
94
                    if (is_numeric($average)) {
95
                        $avg_student_score += $average;
96
                    }
97
                    $avg_student_progress += self::get_avg_student_progress(
98
                        $user_data['user_id'],
99
                        $course,
100
                        [],
101
                        $session
102
                    );
103
                    $work += Container::getStudentPublicationRepository()->countUserPublications(
104
                        $user,
105
                        $course,
106
                        $session
107
                    );
108
                    $messages += Container::getForumPostRepository()->countUserForumPosts($user, $course, $session);
109
                }
110
111
                $countUsers = count($users);
112
                $averageProgress = empty($countUsers) ? 0 : round($avg_student_progress / $countUsers, 2);
113
                $averageScore = empty($countUsers) ? 0 : round($avg_student_score / $countUsers, 2);
114
115
                $groupItem = [
116
                    'id' => $group->getIid(),
117
                    'title' => $group->getTitle(),
118
                    'time' => api_time_to_hms($time),
119
                    'progress' => $averageProgress,
120
                    'score' => $averageScore,
121
                    'works' => $work,
122
                    'messages' => $messages,
123
                ];
124
                $parsedResult[] = $groupItem;
125
            }
126
        }
127
128
        return $parsedResult;
129
    }
130
131
    /**
132
     * @param int    $session_id
133
     * @param string $origin
134
     * @param bool   $export_csv
135
     * @param int    $lp_id
136
     * @param int    $lp_item_id
137
     * @param int    $extendId
138
     * @param int    $extendAttemptId
139
     * @param string $extendedAttempt
140
     * @param string $extendedAll
141
     * @param string $type            classic or simple
142
     * @param bool   $allowExtend     Optional. Allow or not extend te results
143
     *
144
     * @return string
145
     */
146
    public static function getLpStats(
147
        int $user_id,
148
        Course $course,
149
        ?SessionEntity $session,
150
        $origin,
151
        $export_csv,
152
        $lp_id,
153
        $lp_item_id = null,
154
        $extendId = null,
155
        $extendAttemptId = null,
156
        $extendedAttempt = null,
157
        $extendedAll = null,
158
        $type = 'classic',
159
        $allowExtend = true
160
    ) {
161
        if (empty($lp_id)) {
162
            return '';
163
        }
164
165
        $hideTime = ('true' === api_get_setting('lp.hide_lp_time'));
166
        $lp_id = (int) $lp_id;
167
168
        $lp_item_id = (int) $lp_item_id;
169
        $user_id = (int) $user_id;
170
        $sessionId = $session ? $session->getId() : 0;
171
        $origin = Security::remove_XSS($origin);
172
        $lp = Container::getLpRepository()->find($lp_id);
173
        $list = learnpath::get_flat_ordered_items_list($lp);
174
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
175
        $courseId = $course->getId();
176
        $session_condition = api_get_session_condition($sessionId);
177
178
        // Extend all button
179
        $output = '';
180
        $extra = '<script>
181
        $(function() {
182
            $( "#dialog:ui-dialog" ).dialog( "destroy" );
183
            $( "#dialog-confirm" ).dialog({
184
                autoOpen: false,
185
                show: "blind",
186
                resizable: false,
187
                height:300,
188
                modal: true
189
            });
190
191
            $(".export").click(function() {
192
                var targetUrl = $(this).attr("href");
193
                $( "#dialog-confirm" ).dialog({
194
                    width:400,
195
                    height:300,
196
                    buttons: {
197
                        "'.addslashes(get_lang('Download')).'": function() {
198
                            var option = $("input[name=add_logo]:checked").val();
199
                            location.href = targetUrl+"&add_logo="+option;
200
                            $(this).dialog("close");
201
                        }
202
                    }
203
                });
204
                $("#dialog-confirm").dialog("open");
205
206
                return false;
207
            });
208
        });
209
        </script>';
210
211
        $extra .= '<div id="dialog-confirm" title="'.get_lang('Please confirm your choice').'">';
212
        $form = new FormValidator('report', 'post', null, null, ['class' => 'form-vertical']);
213
        $form->addCheckBox('add_logo', '', get_lang('AddRightLogo'), ['id' => 'export_format_csv_label']);
214
        $extra .= $form->returnForm();
215
        $extra .= '</div>';
216
        $output .= $extra;
217
218
        $url_suffix = '&lp_id='.$lp_id;
219
        if ('tracking' === $origin) {
220
            $url_suffix = '&sid='.$sessionId.'&cid='.$courseId.'&student_id='.$user_id.'&lp_id='.$lp_id.'&origin='.$origin;
221
        }
222
223
        $extend_all = 0;
224
        if (!empty($extendedAll)) {
225
            $extend_all_link = Display::url(
226
                Display::getMdiIcon(
227
                    ActionIcon::VIEW_LESS,
228
                    'ch-tool-icon',
229
                    null,
230
                    ICON_SIZE_SMALL,
231
                    get_lang('Hide all attempts')
232
                ),
233
                api_get_self().'?action=stats'.$url_suffix
234
            );
235
            $extend_all = 1;
236
        } else {
237
            $extend_all_link = Display::url(
238
                Display::getMdiIcon(
239
                    ActionIcon::VIEW_MORE,
240
                    'ch-tool-icon',
241
                    null,
242
                    ICON_SIZE_SMALL,
243
                    get_lang('Show all attempts')
244
                ),
245
                api_get_self().'?action=stats&extend_all=1'.$url_suffix
246
            );
247
        }
248
249
        if ('tracking' !== $origin) {
250
            $output .= '<div class="section-status">';
251
            $output .= Display::page_header(get_lang('My progress'));
252
            $output .= '</div>';
253
        }
254
255
        $actionColumn = null;
256
        if ('classic' === $type) {
257
            $actionColumn = ' <th>'.get_lang('Detail').'</th>';
258
        }
259
260
        $timeHeader = '<th class="lp_time" colspan="2">'.get_lang('Time').'</th>';
261
        if ($hideTime) {
262
            $timeHeader = '';
263
        }
264
        $output .= '<div class="table-responsive">';
265
        $output .= '<table id="lp_tracking" class="table tracking">
266
            <thead>
267
            <tr class="table-header">
268
                <th width="16">'.(true === $allowExtend ? $extend_all_link : '&nbsp;').'</th>
269
                <th colspan="4">
270
                    '.get_lang('Learning object name').'
271
                </th>
272
                <th colspan="2">
273
                    '.get_lang('Status').'
274
                </th>
275
                <th colspan="2">
276
                    '.get_lang('Score').'
277
                </th>
278
                '.$timeHeader.'
279
                '.$actionColumn.'
280
                </tr>
281
            </thead>
282
            <tbody>
283
        ';
284
285
        // Going through the items using the $items[] array instead of the database order ensures
286
        // we get them in the same order as in the imsmanifest file, which is rather random when using
287
        // the database table.
288
        $TBL_LP_ITEM = Database::get_course_table(TABLE_LP_ITEM);
289
        $TBL_LP_ITEM_VIEW = Database::get_course_table(TABLE_LP_ITEM_VIEW);
290
        $TBL_LP_VIEW = Database::get_course_table(TABLE_LP_VIEW);
291
        $tbl_quiz_questions = Database::get_course_table(TABLE_QUIZ_QUESTION);
292
        $TBL_QUIZ = Database::get_course_table(TABLE_QUIZ_TEST);
293
        $tbl_stats_exercices = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
294
        $tbl_stats_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
295
296
        $sql = "SELECT max(view_count)
297
                FROM $TBL_LP_VIEW
298
                WHERE
299
                    c_id = $courseId AND
300
                    lp_id = $lp_id AND
301
                    user_id = $user_id
302
                    $session_condition";
303
        $res = Database::query($sql);
304
        $view = 0;
305
        if (Database::num_rows($res) > 0) {
306
            $myrow = Database::fetch_array($res);
307
            $view = (int) $myrow[0];
308
        }
309
310
        $counter = 0;
311
        $total_time = 0;
312
        $h = get_lang('h');
313
314
        if (!empty($export_csv)) {
315
            $csvHeaders = [
316
                get_lang('Learning object name'),
317
                get_lang('Status'),
318
                get_lang('Score'),
319
            ];
320
321
            if (false === $hideTime) {
322
                $csvHeaders[] = get_lang('Time');
323
            }
324
            $csv_content[] = $csvHeaders;
325
        }
326
327
        $result_disabled_ext_all = true;
328
        $chapterTypes = learnpath::getChapterTypes();
329
        $accessToPdfExport = api_is_allowed_to_edit(false, false, true);
330
331
        $minimumAvailable = self::minimumTimeAvailable($sessionId, $courseId);
332
        $timeCourse = [];
333
        if ($minimumAvailable) {
334
            $timeCourse = self::getCalculateTime($user_id, $courseId, $sessionId);
335
            Session::write('trackTimeCourse', $timeCourse);
336
        }
337
338
        // Show lp items
339
        if (is_array($list) && count($list) > 0) {
340
            foreach ($list as $my_item_id) {
341
                $extend_this = 0;
342
                $order = 'DESC';
343
                if ((!empty($extendId) && $extendId == $my_item_id) || $extend_all) {
344
                    $extend_this = 1;
345
                    $order = 'ASC';
346
                }
347
348
                // Prepare statement to go through each attempt.
349
                $viewCondition = null;
350
                if (!empty($view)) {
351
                    $viewCondition = " AND v.view_count = $view  ";
352
                }
353
354
                $sql = "SELECT
355
                    iv.status as mystatus,
356
                    v.view_count as mycount,
357
                    iv.score as myscore,
358
                    iv.total_time as mytime,
359
                    i.iid as myid,
360
                    i.lp_id as mylpid,
361
                    iv.lp_view_id as mylpviewid,
362
                    i.title as mytitle,
363
                    i.max_score as mymaxscore,
364
                    iv.max_score as myviewmaxscore,
365
                    i.item_type as item_type,
366
                    iv.view_count as iv_view_count,
367
                    iv.iid as iv_id,
368
                    path
369
                FROM $TBL_LP_ITEM as i
370
                INNER JOIN $TBL_LP_ITEM_VIEW as iv
371
                ON (i.iid = iv.lp_item_id)
372
                INNER JOIN $TBL_LP_VIEW as v
373
                ON (iv.lp_view_id = v.iid)
374
                WHERE
375
                    i.iid = $my_item_id AND
376
                    i.lp_id = $lp_id  AND
377
                    v.user_id = $user_id
378
                    $session_condition
379
                    $viewCondition
380
                ORDER BY iv.view_count $order ";
381
382
                $result = Database::query($sql);
383
                $num = Database::num_rows($result);
384
                $time_for_total = 0;
385
                $attemptResult = 0;
386
387
                if ($timeCourse) {
388
                    if (isset($timeCourse['learnpath_detailed']) &&
389
                        isset($timeCourse['learnpath_detailed'][$lp_id]) &&
390
                        isset($timeCourse['learnpath_detailed'][$lp_id][$my_item_id])
391
                    ) {
392
                        $attemptResult = $timeCourse['learnpath_detailed'][$lp_id][$my_item_id][$view];
393
                    }
394
                }
395
396
                // Extend all
397
                if (($extend_this || $extend_all) && $num > 0) {
398
                    $row = Database::fetch_array($result);
399
                    $result_disabled_ext_all = false;
400
                    if ('quiz' === $row['item_type']) {
401
                        // Check results_disabled in quiz table.
402
                        $my_path = Database::escape_string($row['path']);
403
                        $sql = "SELECT results_disabled
404
                                FROM $TBL_QUIZ
405
                                WHERE
406
                                    iid ='".$my_path."'";
407
                        $res_result_disabled = Database::query($sql);
408
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
409
410
                        if (Database::num_rows($res_result_disabled) > 0 &&
411
                            1 === (int) $row_result_disabled[0]
412
                        ) {
413
                            $result_disabled_ext_all = true;
414
                        }
415
                    }
416
417
                    // If there are several attempts, and the link to extend has been clicked, show each attempt...
418
                    $oddclass = 'row_even';
419
                    if (0 === ($counter % 2)) {
420
                        $oddclass = 'row_odd';
421
                    }
422
                    $extend_link = '';
423
                    if (!empty($inter_num)) {
424
                        $extend_link = Display::url(
425
                            Display::getMdiIcon(
426
                                ActionIcon::VISIBLE,
427
                                'ch-tool-icon',
428
                                null,
429
                                ICON_SIZE_SMALL,
430
                                get_lang('Hide attempt view')
431
                            ),
432
                            api_get_self().'?action=stats&fold_id='.$my_item_id.$url_suffix
433
                        );
434
                    }
435
                    $title = $row['mytitle'];
436
437
                    if (empty($title)) {
438
                        $title = learnpath::rl_get_resource_name($courseInfo['code'], $lp_id, $row['myid']);
439
                    }
440
441
                    if (in_array($row['item_type'], $chapterTypes)) {
442
                        $title = "<h4> $title </h4>";
443
                    }
444
                    $lesson_status = $row['mystatus'];
445
                    $title = Security::remove_XSS($title);
446
                    $counter++;
447
448
                    $action = null;
449
                    if ('classic' === $type) {
450
                        $action = '<td></td>';
451
                    }
452
453
                    if (in_array($row['item_type'], $chapterTypes)) {
454
                        $output .= '<tr class="'.$oddclass.'">
455
                                <td>'.$extend_link.'</td>
456
                                <td colspan="4">
457
                                   '.$title.'
458
                                </td>
459
                                <td colspan="2">'.learnpathItem::humanize_status($lesson_status, true, $type).'</td>
460
                                <td colspan="2"></td>
461
                                <td colspan="2"></td>
462
                                '.$action.'
463
                            </tr>';
464
                        continue;
465
                    } else {
466
                        $output .= '<tr class="'.$oddclass.'">
467
                                <td>'.$extend_link.'</td>
468
                                <td colspan="4">'.$title.'</td>
469
                                <td colspan="2"></td>
470
                                <td colspan="2"></td>
471
                                <td colspan="2"></td>
472
                                '.$action.'
473
                            </tr>';
474
                    }
475
476
                    $attemptCount = 1;
477
                    do {
478
                        // Check if there are interactions below.
479
                        $extend_attempt_link = '';
480
                        $extend_this_attempt = 0;
481
482
                        if ($timeCourse) {
483
                            //$attemptResult = 0;
484
                            if (isset($timeCourse['learnpath_detailed']) &&
485
                                isset($timeCourse['learnpath_detailed'][$lp_id]) &&
486
                                isset($timeCourse['learnpath_detailed'][$lp_id][$my_item_id])
487
                            ) {
488
                                $attemptResult = $timeCourse['learnpath_detailed'][$lp_id][$my_item_id][$row['iv_view_count']];
489
                            }
490
                        }
491
                        if ((
492
                            learnpath::get_interactions_count_from_db($row['iv_id'], $courseId) > 0 ||
493
                            learnpath::get_objectives_count_from_db($row['iv_id'], $courseId) > 0
494
                            ) &&
495
                            !$extend_all
496
                        ) {
497
                            if ($extendAttemptId == $row['iv_id']) {
498
                                // The extend button for this attempt has been clicked.
499
                                $extend_this_attempt = 1;
500
                                $extend_attempt_link = Display::url(
501
                                    Display::getMdiIcon(
502
                                        ActionIcon::VISIBLE,
503
                                        'ch-tool-icon',
504
                                        null,
505
                                        ICON_SIZE_SMALL,
506
                                        get_lang('Hide attempt view')
507
                                    ),
508
                                    api_get_self().'?action=stats&extend_id='.$my_item_id.'&fold_attempt_id='.$row['iv_id'].$url_suffix
509
                                );
510
                                if ($accessToPdfExport) {
511
                                    $extend_attempt_link .= '&nbsp;'.
512
                                        Display::url(
513
                                            Display::getMdiIcon(
514
                                                ActionIcon::EXPORT_PDF,
515
                                                'ch-tool-icon',
516
                                                null,
517
                                                ICON_SIZE_SMALL,
518
                                                get_lang('Export to PDF')
519
                                            ),
520
                                            api_get_self().'?action=export_stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix,
521
                                            ['class' => 'export']
522
                                        );
523
                                }
524
                            } else { // Same case if fold_attempt_id is set, so not implemented explicitly.
525
                                // The "extend" button for this attempt has not been clicked.
526
                                $extend_attempt_link = Display::url(
527
                                    Display::getMdiIcon(
528
                                        ActionIcon::INVISIBLE,
529
                                        'ch-tool-icon',
530
                                        null,
531
                                        ICON_SIZE_SMALL,
532
                                        get_lang('Extend attempt view')
533
                                    ),
534
                                    api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
535
                                );
536
                                if ($accessToPdfExport) {
537
                                    $extend_attempt_link .= '&nbsp;'.
538
                                        Display::url(
539
                                            Display::getMdiIcon(
540
                                                ActionIcon::EXPORT_PDF,
541
                                                'ch-tool-icon',
542
                                                null,
543
                                                ICON_SIZE_SMALL,
544
                                                get_lang('Export to PDF')
545
                                            ),
546
                                            api_get_self().'?action=export_stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix,
547
                                            ['class' => 'export']
548
                                        );
549
                                }
550
                            }
551
                        }
552
553
                        $oddclass = 'row_even';
554
                        if (0 == ($counter % 2)) {
555
                            $oddclass = 'row_odd';
556
                        }
557
558
                        $lesson_status = $row['mystatus'];
559
                        $score = $row['myscore'];
560
                        $time_for_total += $row['mytime'];
561
                        $attemptTime = $row['mytime'];
562
563
                        if ($minimumAvailable) {
564
                            $lp_time = $timeCourse[TOOL_LEARNPATH];
565
                            $lpTime = null;
566
                            if (isset($lp_time[$lp_id])) {
567
                                $lpTime = (int) $lp_time[$lp_id];
568
                            }
569
                            $time_for_total = $lpTime;
570
571
                            if ($timeCourse) {
572
                                $time_for_total = (int) $attemptResult;
573
                                $attemptTime = (int) $attemptResult;
574
                            }
575
                        }
576
577
                        $time = learnpathItem::getScormTimeFromParameter('js', $attemptTime);
578
579
                        if (0 == $score) {
580
                            $maxscore = $row['mymaxscore'];
581
                        } else {
582
                            if ('sco' === $row['item_type']) {
583
                                if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
584
                                    $maxscore = $row['myviewmaxscore'];
585
                                } elseif ('' === $row['myviewmaxscore']) {
586
                                    $maxscore = 0;
587
                                } else {
588
                                    $maxscore = $row['mymaxscore'];
589
                                }
590
                            } else {
591
                                $maxscore = $row['mymaxscore'];
592
                            }
593
                        }
594
595
                        // Remove "NaN" if any (@todo: locate the source of these NaN)
596
                        $time = str_replace('NaN', '00'.$h.'00\'00"', $time);
597
598
                        if ('dir' !== $row['item_type']) {
599
                            if (!$is_allowed_to_edit && $result_disabled_ext_all) {
600
                                $view_score = Display::getMdiIcon(
601
                                    ActionIcon::INVISIBLE,
602
                                    'ch-tool-icon',
603
                                    null,
604
                                    ICON_SIZE_SMALL,
605
                                    get_lang('Results hidden by the exercise setting')
606
                                );
607
                            } else {
608
                                switch ($row['item_type']) {
609
                                    case 'sco':
610
                                        if (0 == $maxscore) {
611
                                            $view_score = $score;
612
                                        } else {
613
                                            $view_score = ExerciseLib::show_score(
614
                                                $score,
615
                                                $maxscore,
616
                                                false
617
                                            );
618
                                        }
619
                                        break;
620
                                    case 'document':
621
                                        $view_score = (0 == $score ? '/' : ExerciseLib::show_score($score, $maxscore, false));
622
                                        break;
623
                                    default:
624
                                        $view_score = ExerciseLib::show_score(
625
                                            $score,
626
                                            $maxscore,
627
                                            false
628
                                        );
629
                                        break;
630
                                }
631
                            }
632
633
                            $action = null;
634
                            if ('classic' === $type) {
635
                                $action = '<td></td>';
636
                            }
637
                            $timeRow = '<td class="lp_time" colspan="2">'.$time.'</td>';
638
                            if ($hideTime) {
639
                                $timeRow = '';
640
                            }
641
                            $output .= '<tr class="'.$oddclass.'">
642
                                    <td></td>
643
                                    <td style="width:70px;float:left;">'.$extend_attempt_link.'</td>
644
                                    <td colspan="3">'.get_lang('Attempt').' '.$attemptCount.'</td>
645
                                    <td colspan="2">'.learnpathItem::humanize_status($lesson_status, true, $type).'</td>
646
                                    <td colspan="2">'.$view_score.'</td>
647
                                    '.$timeRow.'
648
                                    '.$action.'
649
                                </tr>';
650
                            $attemptCount++;
651
                            if (!empty($export_csv)) {
652
                                $temp = [];
653
                                $temp[] = $title = Security::remove_XSS($title);
654
                                $temp[] = Security::remove_XSS(
655
                                    learnpathItem::humanize_status($lesson_status, false, $type)
656
                                );
657
658
                                if ('quiz' === $row['item_type']) {
659
                                    if (!$is_allowed_to_edit && $result_disabled_ext_all) {
660
                                        $temp[] = '/';
661
                                    } else {
662
                                        $temp[] = (0 == $score ? '0/'.$maxscore : (0 == $maxscore ? $score : $score.'/'.float_format($maxscore, 1)));
663
                                    }
664
                                } else {
665
                                    $temp[] = (0 == $score ? '/' : (0 == $maxscore ? $score : $score.'/'.float_format($maxscore, 1)));
666
                                }
667
668
                                if (false === $hideTime) {
669
                                    $temp[] = $time;
670
                                }
671
                                $csv_content[] = $temp;
672
                            }
673
                        }
674
675
                        $counter++;
676
                        $action = null;
677
                        if ('classic' === $type) {
678
                            $action = '<td></td>';
679
                        }
680
681
                        if ($extend_this_attempt || $extend_all) {
682
                            $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $courseId);
683
                            foreach ($list1 as $id => $interaction) {
684
                                $oddclass = 'row_even';
685
                                if (0 == ($counter % 2)) {
686
                                    $oddclass = 'row_odd';
687
                                }
688
                                $timeRow = '<td class="lp_time">'.$interaction['time'].'</td>';
689
                                if ($hideTime) {
690
                                    $timeRow = '';
691
                                }
692
693
                                $output .= '<tr class="'.$oddclass.'">
694
                                        <td></td>
695
                                        <td></td>
696
                                        <td></td>
697
                                        <td>'.$interaction['order_id'].'</td>
698
                                        <td>'.$interaction['id'].'</td>';
699
700
                                $output .= '
701
                                        <td colspan="2">'.$interaction['type'].'</td>
702
                                        <td>'.$interaction['student_response_formatted'].'</td>
703
                                        <td>'.$interaction['result'].'</td>
704
                                        <td>'.$interaction['latency'].'</td>
705
                                        '.$timeRow.'
706
                                        '.$action.'
707
                                    </tr>';
708
                                $counter++;
709
                            }
710
                            $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $courseId);
711
                            foreach ($list2 as $id => $interaction) {
712
                                $oddclass = 'row_even';
713
                                if (0 === ($counter % 2)) {
714
                                    $oddclass = 'row_odd';
715
                                }
716
                                $output .= '<tr class="'.$oddclass.'">
717
                                        <td></td>
718
                                        <td></td>
719
                                        <td></td>
720
                                        <td>'.$interaction['order_id'].'</td>
721
                                        <td colspan="2">'.$interaction['objective_id'].'</td>
722
                                        <td colspan="2">'.$interaction['status'].'</td>
723
                                        <td>'.$interaction['score_raw'].'</td>
724
                                        <td>'.$interaction['score_max'].'</td>
725
                                        <td>'.$interaction['score_min'].'</td>
726
                                        '.$action.'
727
                                     </tr>';
728
                                $counter++;
729
                            }
730
                        }
731
                    } while ($row = Database::fetch_array($result));
732
                } elseif ($num > 0) {
733
                    // Not extended.
734
                    $row = Database::fetch_assoc($result);
735
                    $my_id = $row['myid'];
736
                    $my_lp_id = $row['mylpid'];
737
                    $my_lp_view_id = $row['mylpviewid'];
738
                    $my_path = $row['path'];
739
                    $result_disabled_ext_all = false;
740
                    if ('quiz' === $row['item_type']) {
741
                        // Check results_disabled in quiz table.
742
                        $my_path = Database::escape_string($my_path);
743
                        $sql = "SELECT results_disabled
744
                                FROM $TBL_QUIZ
745
                                WHERE iid = '$my_path' ";
746
                        $res_result_disabled = Database::query($sql);
747
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
748
749
                        if (Database::num_rows($res_result_disabled) > 0 &&
750
                            1 === (int) $row_result_disabled[0]
751
                        ) {
752
                            $result_disabled_ext_all = true;
753
                        }
754
                    }
755
756
                    // Check if there are interactions below
757
                    $extend_this_attempt = 0;
758
                    $inter_num = learnpath::get_interactions_count_from_db($row['iv_id'], $courseId);
759
                    $objec_num = learnpath::get_objectives_count_from_db($row['iv_id'], $courseId);
760
                    $extend_attempt_link = '';
761
                    if ($inter_num > 0 || $objec_num > 0) {
762
                        if (!empty($extendAttemptId) && $extendAttemptId == $row['iv_id']) {
763
                            // The extend button for this attempt has been clicked.
764
                            $extend_this_attempt = 1;
765
                            $extend_attempt_link = Display::url(
766
                                Display::getMdiIcon(
767
                                    ActionIcon::VISIBLE,
768
                                    'ch-tool-icon',
769
                                    null,
770
                                    ICON_SIZE_SMALL,
771
                                    get_lang('Hide attempt view')
772
                                ),
773
                                api_get_self().'?action=stats&extend_id='.$my_item_id.'&fold_attempt_id='.$row['iv_id'].$url_suffix
774
                            );
775
                        } else {
776
                            // Same case if fold_attempt_id is set, so not implemented explicitly.
777
                            // The "Extend" button for this attempt has not been clicked.
778
                            $extend_attempt_link = Display::url(
779
                                Display::getMdiIcon(
780
                                    ActionIcon::INVISIBLE,
781
                                    'ch-tool-icon',
782
                                    null,
783
                                    ICON_SIZE_SMALL,
784
                                    get_lang('Extend attempt view')
785
                                ),
786
                                api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
787
                            );
788
                        }
789
                    }
790
791
                    $oddclass = 'row_even';
792
                    if (0 == ($counter % 2)) {
793
                        $oddclass = 'row_odd';
794
                    }
795
796
                    $extend_link = '';
797
                    if ($inter_num > 1) {
798
                        $extend_link = Display::url(
799
                            Display::getMdiIcon(
800
                                ActionIcon::INVISIBLE,
801
                                'ch-tool-icon',
802
                                null,
803
                                ICON_SIZE_SMALL,
804
                                get_lang('Extend attempt view')
805
                            ),
806
                            api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
807
                        );
808
                    }
809
810
                    $lesson_status = $row['mystatus'];
811
                    $score = $row['myscore'];
812
                    $subtotal_time = $row['mytime'];
813
                    while ($tmp_row = Database::fetch_array($result)) {
814
                        $subtotal_time += $tmp_row['mytime'];
815
                    }
816
817
                    $title = $row['mytitle'];
818
                    $sessionCondition = api_get_session_condition($sessionId);
819
                    // Selecting the exe_id from stats attempts tables in order to look the max score value.
820
                    $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
821
                            WHERE
822
                                exe_exo_id="'.$row['path'].'" AND
823
                                exe_user_id="'.$user_id.'" AND
824
                                orig_lp_id = "'.$lp_id.'" AND
825
                                orig_lp_item_id = "'.$row['myid'].'" AND
826
                                c_id = '.$courseId.' AND
827
                                status <> "incomplete"
828
                                '.$sessionCondition.'
829
                             ORDER BY exe_date DESC
830
                             LIMIT 1';
831
832
                    $resultLastAttempt = Database::query($sql);
833
                    $num = Database::num_rows($resultLastAttempt);
834
                    $id_last_attempt = null;
835
                    if ($num > 0) {
836
                        while ($rowLA = Database::fetch_array($resultLastAttempt)) {
837
                            $id_last_attempt = $rowLA['exe_id'];
838
                        }
839
                    }
840
841
                    switch ($row['item_type']) {
842
                        case 'sco':
843
                            if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
844
                                $maxscore = $row['myviewmaxscore'];
845
                            } elseif ('' === $row['myviewmaxscore']) {
846
                                $maxscore = 0;
847
                            } else {
848
                                $maxscore = $row['mymaxscore'];
849
                            }
850
                            break;
851
                        case 'quiz':
852
                            // Get score and total time from last attempt of a exercise en lp.
853
                            $sql = "SELECT iid, score
854
                                    FROM $TBL_LP_ITEM_VIEW
855
                                    WHERE
856
                                        lp_item_id = '".(int) $my_id."' AND
857
                                        lp_view_id = '".(int) $my_lp_view_id."'
858
                                    ORDER BY view_count DESC
859
                                    LIMIT 1";
860
                            $res_score = Database::query($sql);
861
                            $row_score = Database::fetch_array($res_score);
862
863
                            $sql = "SELECT SUM(total_time) as total_time
864
                                    FROM $TBL_LP_ITEM_VIEW
865
                                    WHERE
866
                                        lp_item_id = '".(int) $my_id."' AND
867
                                        lp_view_id = '".(int) $my_lp_view_id."'";
868
                            $res_time = Database::query($sql);
869
                            $row_time = Database::fetch_array($res_time);
870
871
                            $score = 0;
872
                            $subtotal_time = 0;
873
                            if (Database::num_rows($res_score) > 0 &&
874
                                Database::num_rows($res_time) > 0
875
                            ) {
876
                                $score = (float) $row_score['score'];
877
                                $subtotal_time = (int) $row_time['total_time'];
878
                            }
879
                            // Selecting the max score from an attempt.
880
                            $sql = "SELECT SUM(t.ponderation) as maxscore
881
                                    FROM (
882
                                        SELECT DISTINCT
883
                                            question_id, marks, ponderation
884
                                        FROM $tbl_stats_attempts as at
885
                                        INNER JOIN $tbl_quiz_questions as q
886
                                        ON (q.iid = at.question_id)
887
                                        WHERE exe_id ='$id_last_attempt'
888
                                    ) as t";
889
890
                            $result = Database::query($sql);
891
                            $row_max_score = Database::fetch_array($result);
892
                            $maxscore = $row_max_score['maxscore'];
893
894
                            // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
895
                            $sql = 'SELECT SUM(exe_duration) exe_duration
896
                                    FROM '.$tbl_stats_exercices.'
897
                                    WHERE
898
                                        exe_exo_id="'.$row['path'].'" AND
899
                                        exe_user_id="'.$user_id.'" AND
900
                                        orig_lp_id = "'.$lp_id.'" AND
901
                                        orig_lp_item_id = "'.$row['myid'].'" AND
902
                                        c_id = '.$courseId.' AND
903
                                        status <> "incomplete" AND
904
                                        session_id = '.$sessionId.'
905
                                     ORDER BY exe_date DESC ';
906
                            $sumScoreResult = Database::query($sql);
907
                            $durationRow = Database::fetch_assoc($sumScoreResult);
908
                            if (!empty($durationRow['exe_duration'])) {
909
                                $exeDuration = $durationRow['exe_duration'];
910
                                if ($exeDuration != $subtotal_time &&
911
                                    !empty($row_score['iid']) &&
912
                                    !empty($exeDuration)
913
                                ) {
914
                                    $subtotal_time = $exeDuration;
915
                                    // Update c_lp_item_view.total_time
916
                                    $sqlUpdate = "UPDATE $TBL_LP_ITEM_VIEW SET total_time = '$exeDuration'
917
                                                  WHERE iid = ".$row_score['iid'];
918
                                    Database::query($sqlUpdate);
919
                                }
920
                            }
921
                            break;
922
                        default:
923
                            $maxscore = $row['mymaxscore'];
924
                            break;
925
                    }
926
927
                    $time_for_total = $subtotal_time;
928
                    $time = learnpathItem::getScormTimeFromParameter('js', $subtotal_time);
929
                    if (empty($title)) {
930
                        $title = learnpath::rl_get_resource_name(
931
                            $courseInfo['code'],
932
                            $lp_id,
933
                            $row['myid']
934
                        );
935
                    }
936
937
                    $action = null;
938
                    if ('classic' === $type) {
939
                        $action = '<td></td>';
940
                    }
941
942
                    if (in_array($row['item_type'], $chapterTypes)) {
943
                        $title = Security::remove_XSS($title);
944
                        $output .= '<tr class="'.$oddclass.'">
945
                                <td>'.$extend_link.'</td>
946
                                <td colspan="10">
947
                                <h4>'.$title.'</h4>
948
                                </td>
949
                                '.$action.'
950
                            </tr>';
951
                    } else {
952
                        $correct_test_link = '-';
953
                        $showRowspan = false;
954
                        if ('quiz' === $row['item_type']) {
955
                            $my_url_suffix = '&cid='.$courseId.'&student_id='.$user_id.'&lp_id='.intval($row['mylpid']).'&origin='.$origin;
956
                            $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
957
                                     WHERE
958
                                        exe_exo_id="'.$row['path'].'" AND
959
                                        exe_user_id="'.$user_id.'" AND
960
                                        orig_lp_id = "'.$lp_id.'" AND
961
                                        orig_lp_item_id = "'.$row['myid'].'" AND
962
                                        c_id = '.$courseId.' AND
963
                                        status <> "incomplete" AND
964
                                        session_id = '.$sessionId.'
965
                                     ORDER BY exe_date DESC ';
966
967
                            $resultLastAttempt = Database::query($sql);
968
                            $num = Database::num_rows($resultLastAttempt);
969
                            $showRowspan = false;
970
                            if ($num > 0) {
971
                                $linkId = 'link_'.$my_id;
972
                                if (1 == $extendedAttempt &&
973
                                    $lp_id == $my_lp_id &&
974
                                    $lp_item_id == $my_id
975
                                ) {
976
                                    $showRowspan = true;
977
                                    $correct_test_link = Display::url(
978
                                        Display::getMdiIcon(
979
                                            ActionIcon::VIEW_LESS,
980
                                            'ch-tool-icon',
981
                                            null,
982
                                            ICON_SIZE_SMALL,
983
                                            get_lang('Hide all attempts')
984
                                        ),
985
                                        api_get_self().'?action=stats'.$my_url_suffix.'&sid='.$sessionId.'&lp_item_id='.$my_id.'#'.$linkId,
986
                                        ['id' => $linkId]
987
                                    );
988
                                } else {
989
                                    $correct_test_link = Display::url(
990
                                        Display::getMdiIcon(
991
                                            ActionIcon::VIEW_MORE,
992
                                            'ch-tool-icon',
993
                                            null,
994
                                            ICON_SIZE_SMALL,
995
                                            get_lang(
996
                                                'Show all attemptsByExercise'
997
                                            )
998
                                        ),
999
                                        api_get_self().'?action=stats&extend_attempt=1'.$my_url_suffix.'&sid='.$sessionId.'&lp_item_id='.$my_id.'#'.$linkId,
1000
                                        ['id' => $linkId]
1001
                                    );
1002
                                }
1003
                            }
1004
                        }
1005
1006
                        $title = Security::remove_XSS($title);
1007
                        $action = null;
1008
                        if ('classic' === $type) {
1009
                            $action = '<td '.($showRowspan ? 'rowspan="2"' : '').'>'.$correct_test_link.'</td>';
1010
                        }
1011
1012
                        if ($lp_id == $my_lp_id && false) {
1013
                            $output .= '<tr class ='.$oddclass.'>
1014
                                    <td>'.$extend_link.'</td>
1015
                                    <td colspan="4">'.$title.'</td>
1016
                                    <td colspan="2">&nbsp;</td>
1017
                                    <td colspan="2">&nbsp;</td>
1018
                                    <td colspan="2">&nbsp;</td>
1019
                                    '.$action.'
1020
                                </tr>';
1021
                            $output .= '</tr>';
1022
                        } else {
1023
                            if ($lp_id == $my_lp_id && $lp_item_id == $my_id) {
1024
                                $output .= "<tr class='$oddclass'>";
1025
                            } else {
1026
                                $output .= "<tr class='$oddclass'>";
1027
                            }
1028
1029
                            $scoreItem = null;
1030
                            if ('quiz' === $row['item_type']) {
1031
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1032
                                    $scoreItem .= Display::getMdiIcon(
1033
                                        ActionIcon::INVISIBLE,
1034
                                        'ch-tool-icon',
1035
                                        null,
1036
                                        ICON_SIZE_SMALL,
1037
                                        get_lang('Results hidden by the exercise setting')
1038
                                    );
1039
                                } else {
1040
                                    $scoreItem .= ExerciseLib::show_score($score, $maxscore, false);
1041
                                }
1042
                            } else {
1043
                                $scoreItem .= 0 == $score ? '/' : (0 == $maxscore ? $score : $score.'/'.$maxscore);
1044
                            }
1045
1046
                            $timeRow = '<td class="lp_time" colspan="2">'.$time.'</td>';
1047
                            if ($hideTime) {
1048
                                $timeRow = '';
1049
                            }
1050
1051
                            $output .= '
1052
                                <td>'.$extend_link.'</td>
1053
                                <td colspan="4">'.$title.'</td>
1054
                                <td colspan="2">'.learnpathitem::humanize_status($lesson_status).'</td>
1055
                                <td colspan="2">'.$scoreItem.'</td>
1056
                                '.$timeRow.'
1057
                                '.$action.'
1058
                             ';
1059
                            $output .= '</tr>';
1060
                        }
1061
1062
                        if (!empty($export_csv)) {
1063
                            $temp = [];
1064
                            $temp[] = api_html_entity_decode($title, ENT_QUOTES);
1065
                            $temp[] = api_html_entity_decode($lesson_status, ENT_QUOTES);
1066
                            if ('quiz' === $row['item_type']) {
1067
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1068
                                    $temp[] = '/';
1069
                                } else {
1070
                                    $temp[] = (0 == $score ? '0/'.$maxscore : (0 == $maxscore ? $score : $score.'/'.float_format($maxscore, 1)));
1071
                                }
1072
                            } else {
1073
                                $temp[] = (0 == $score ? '/' : (0 == $maxscore ? $score : $score.'/'.float_format($maxscore, 1)));
1074
                            }
1075
1076
                            if (false === $hideTime) {
1077
                                $temp[] = $time;
1078
                            }
1079
                            $csv_content[] = $temp;
1080
                        }
1081
                    }
1082
1083
                    $counter++;
1084
                    $action = null;
1085
                    if ('classic' === $type) {
1086
                        $action = '<td></td>';
1087
                    }
1088
1089
                    if ($extend_this_attempt || $extend_all) {
1090
                        $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $courseId);
1091
                        foreach ($list1 as $id => $interaction) {
1092
                            $oddclass = 'row_even';
1093
                            if (0 == ($counter % 2)) {
1094
                                $oddclass = 'row_odd';
1095
                            }
1096
                            $timeRow = '<td class="lp_time">'.$interaction['time'].'</td>';
1097
                            if ($hideTime) {
1098
                                $timeRow = '';
1099
                            }
1100
1101
                            $output .= '<tr class="'.$oddclass.'">
1102
                                    <td></td>
1103
                                    <td></td>
1104
                                    <td></td>
1105
                                    <td>'.$interaction['order_id'].'</td>
1106
                                    <td>'.$interaction['id'].'</td>
1107
                                    <td colspan="2">'.$interaction['type'].'</td>
1108
                                    <td>'.urldecode($interaction['student_response']).'</td>
1109
                                    <td>'.$interaction['result'].'</td>
1110
                                    <td>'.$interaction['latency'].'</td>
1111
                                    '.$timeRow.'
1112
                                    '.$action.'
1113
                               </tr>';
1114
                            $counter++;
1115
                        }
1116
1117
                        $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $courseId);
1118
                        foreach ($list2 as $id => $interaction) {
1119
                            $oddclass = 'row_even';
1120
                            if (0 == ($counter % 2)) {
1121
                                $oddclass = 'row_odd';
1122
                            }
1123
                            $output .= '<tr class="'.$oddclass.'">
1124
                                    <td></td>
1125
                                    <td></td>
1126
                                    <td></td>
1127
                                    <td>'.$interaction['order_id'].'</td>
1128
                                    <td colspan="2">'.$interaction['objective_id'].'</td>
1129
                                    <td colspan="2">'.$interaction['status'].'</td>
1130
                                    <td>'.$interaction['score_raw'].'</td>
1131
                                    <td>'.$interaction['score_max'].'</td>
1132
                                    <td>'.$interaction['score_min'].'</td>
1133
                                    '.$action.'
1134
                               </tr>';
1135
                            $counter++;
1136
                        }
1137
                    }
1138
1139
                    // Attempts listing by exercise.
1140
                    if ($lp_id == $my_lp_id && $lp_item_id == $my_id && $extendedAttempt) {
1141
                        // Get attempts of a exercise.
1142
                        if (!empty($lp_id) &&
1143
                            !empty($lp_item_id) &&
1144
                            'quiz' === $row['item_type']
1145
                        ) {
1146
                            $sql = "SELECT path FROM $TBL_LP_ITEM
1147
                                    WHERE
1148
                                        iid = '$lp_item_id' AND
1149
                                        lp_id = '$lp_id'";
1150
                            $res_path = Database::query($sql);
1151
                            $row_path = Database::fetch_array($res_path);
1152
1153
                            if (Database::num_rows($res_path) > 0) {
1154
                                $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
1155
                                        WHERE
1156
                                            exe_exo_id="'.(int) $row_path['path'].'" AND
1157
                                            status <> "incomplete" AND
1158
                                            exe_user_id="'.$user_id.'" AND
1159
                                            orig_lp_id = "'.(int) $lp_id.'" AND
1160
                                            orig_lp_item_id = "'.(int) $lp_item_id.'" AND
1161
                                            c_id = '.$courseId.'  AND
1162
                                            session_id = '.$sessionId.'
1163
                                        ORDER BY exe_date';
1164
                                $res_attempts = Database::query($sql);
1165
                                $num_attempts = Database::num_rows($res_attempts);
1166
                                if ($num_attempts > 0) {
1167
                                    $n = 1;
1168
                                    while ($row_attempts = Database::fetch_array($res_attempts)) {
1169
                                        $my_score = $row_attempts['score'];
1170
                                        $my_maxscore = $row_attempts['max_score'];
1171
                                        $my_exe_id = $row_attempts['exe_id'];
1172
                                        $mktime_start_date = api_strtotime($row_attempts['start_date'], 'UTC');
1173
                                        $mktime_exe_date = api_strtotime($row_attempts['exe_date'], 'UTC');
1174
                                        $time_attemp = ' - ';
1175
                                        if ($mktime_start_date && $mktime_exe_date) {
1176
                                            $time_attemp = api_format_time($row_attempts['exe_duration'], 'js');
1177
                                        }
1178
                                        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1179
                                            $view_score = Display::getMdiIcon(
1180
                                                ActionIcon::INVISIBLE,
1181
                                                'ch-tool-icon',
1182
                                                null,
1183
                                                ICON_SIZE_SMALL,
1184
                                                get_lang('Results hidden by the exercise setting')
1185
                                            );
1186
                                        } else {
1187
                                            // Show only float when need it
1188
                                            if (0 == $my_score) {
1189
                                                $view_score = ExerciseLib::show_score(
1190
                                                    0,
1191
                                                    $my_maxscore,
1192
                                                    false
1193
                                                );
1194
                                            } else {
1195
                                                if (0 == $my_maxscore) {
1196
                                                    $view_score = $my_score;
1197
                                                } else {
1198
                                                    $view_score = ExerciseLib::show_score(
1199
                                                        $my_score,
1200
                                                        $my_maxscore,
1201
                                                        false
1202
                                                    );
1203
                                                }
1204
                                            }
1205
                                        }
1206
                                        $my_lesson_status = $row_attempts['status'];
1207
                                        if ('' === $my_lesson_status) {
1208
                                            $my_lesson_status = learnpathitem::humanize_status('completed');
1209
                                        } elseif ('incomplete' === $my_lesson_status) {
1210
                                            $my_lesson_status = learnpathitem::humanize_status('incomplete');
1211
                                        }
1212
                                        $timeRow = '<td class="lp_time" colspan="2">'.$time_attemp.'</td>';
1213
                                        if ($hideTime) {
1214
                                            $timeRow = '';
1215
                                        }
1216
1217
                                        $output .= '<tr class="'.$oddclass.'" >
1218
                                        <td></td>
1219
                                        <td>'.$extend_attempt_link.'</td>
1220
                                        <td colspan="3">'.get_lang('Attempt').' '.$n.'</td>
1221
                                        <td colspan="2">'.$my_lesson_status.'</td>
1222
                                        <td colspan="2">'.$view_score.'</td>
1223
                                        '.$timeRow;
1224
1225
                                        if ('classic' === $action) {
1226
                                            if ('tracking' !== $origin) {
1227
                                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1228
                                                    $output .= '<td>
1229
                                                            <img
1230
                                                                src="'.Display::returnIconPath('quiz_na.gif').'"
1231
                                                                alt="'.get_lang('Show attempt').'"
1232
                                                                title="'.get_lang('Show attempt').'" />
1233
                                                            </td>';
1234
                                                } else {
1235
                                                    $output .= '<td>
1236
                                                            <a
1237
                                                                href="../exercise/exercise_show.php?origin='.$origin.'&id='.$my_exe_id.'&cid='.$courseId.'"
1238
                                                                target="_parent">
1239
                                                            <img
1240
                                                                src="'.Display::returnIconPath('quiz.png').'"
1241
                                                                alt="'.get_lang('Show attempt').'"
1242
                                                                title="'.get_lang('Show attempt').'" />
1243
                                                            </a></td>';
1244
                                                }
1245
                                            } else {
1246
                                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1247
                                                    $output .= '<td>
1248
                                                                <img
1249
                                                                    src="'.Display::returnIconPath('quiz_na.gif').'"
1250
                                                                    alt="'.get_lang('Show and grade attempt').'"
1251
                                                                    title="'.get_lang('Show and grade attempt').'" />
1252
                                                                </td>';
1253
                                                } else {
1254
                                                    $output .= '<td>
1255
                                                                    <a
1256
                                                                        href="../exercise/exercise_show.php?cid='.$courseId.'&origin=correct_exercise_in_lp&id='.$my_exe_id.'"
1257
                                                                        target="_parent">
1258
                                                                    <img
1259
                                                                        src="'.Display::returnIconPath('quiz.gif').'"
1260
                                                                        alt="'.get_lang('Show and grade attempt').'"
1261
                                                                        title="'.get_lang('Show and grade attempt').'">
1262
                                                                    </a>
1263
                                                                    </td>';
1264
                                                }
1265
                                            }
1266
                                        }
1267
                                        $output .= '</tr>';
1268
                                        $n++;
1269
                                    }
1270
                                }
1271
                                $output .= '<tr><td colspan="12">&nbsp;</td></tr>';
1272
                            }
1273
                        }
1274
                    }
1275
                }
1276
1277
                $total_time += $time_for_total;
1278
                // QUIZZ IN LP
1279
                $a_my_id = [];
1280
                if (!empty($my_lp_id)) {
1281
                    $a_my_id[] = $my_lp_id;
1282
                }
1283
            }
1284
        }
1285
1286
        // NOT Extend all "left green cross"
1287
        if (!empty($a_my_id)) {
1288
            if ($extendedAttempt) {
1289
                // "Right green cross" extended
1290
                $total_score = self::get_avg_student_score(
1291
                    $user_id,
1292
                    $course,
1293
                    $a_my_id,
1294
                    $session,
1295
                    false,
1296
                    false
1297
                );
1298
            } else {
1299
                // "Left green cross" extended
1300
                $total_score = self::get_avg_student_score(
1301
                    $user_id,
1302
                    $course,
1303
                    $a_my_id,
1304
                    $session,
1305
                    false,
1306
                    true
1307
                );
1308
            }
1309
        } else {
1310
            // Extend all "left green cross"
1311
            $total_score = self::get_avg_student_score(
1312
                $user_id,
1313
                $course,
1314
                [$lp_id],
1315
                $session,
1316
                false,
1317
                false
1318
            );
1319
        }
1320
1321
        $total_time = learnpathItem::getScormTimeFromParameter('js', $total_time);
1322
        $total_time = str_replace('NaN', '00'.$h.'00\'00"', $total_time);
1323
1324
        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1325
            $final_score = Display::getMdiIcon(
1326
                ActionIcon::INVISIBLE,
1327
                'ch-tool-icon',
1328
                null,
1329
                ICON_SIZE_SMALL,
1330
                get_lang('Results hidden by the exercise setting')
1331
            );
1332
            $finalScoreToCsv = get_lang('Results hidden by the exercise setting');
1333
        } else {
1334
            if (is_numeric($total_score)) {
1335
                $final_score = $total_score.'%';
1336
            } else {
1337
                $final_score = $total_score;
1338
            }
1339
            $finalScoreToCsv = $final_score;
1340
        }
1341
        $progress = learnpath::getProgress($lp_id, $user_id, $courseId, $sessionId);
1342
1343
        $oddclass = 'row_even';
1344
        if (0 == ($counter % 2)) {
1345
            $oddclass = 'row_odd';
1346
        }
1347
1348
        $action = null;
1349
        if ('classic' === $type) {
1350
            $action = '<td></td>';
1351
        }
1352
1353
        $timeTotal = '<td class="lp_time" colspan="2">'.$total_time.'</div>';
1354
        if ($hideTime) {
1355
            $timeTotal = '';
1356
        }
1357
1358
        $output .= '<tr class="'.$oddclass.'">
1359
                <td></td>
1360
                <td colspan="4">
1361
                    <i>'.get_lang('Total of completed learning objects').'</i>
1362
                </td>
1363
                <td colspan="2">'.$progress.'%</td>
1364
                <td colspan="2">'.$final_score.'</td>
1365
                '.$timeTotal.'
1366
                '.$action.'
1367
           </tr>';
1368
1369
        $output .= '
1370
                    </tbody>
1371
                </table>
1372
            </div>
1373
        ';
1374
1375
        if (!empty($export_csv)) {
1376
            $temp = [
1377
                '',
1378
                '',
1379
                '',
1380
                '',
1381
            ];
1382
            $csv_content[] = $temp;
1383
            $temp = [
1384
                get_lang('Total of completed learning objects'),
1385
                '',
1386
                $finalScoreToCsv,
1387
            ];
1388
1389
            if (false === $hideTime) {
1390
                $temp[] = $total_time;
1391
            }
1392
1393
            $csv_content[] = $temp;
1394
            ob_end_clean();
1395
            Export::arrayToCsv($csv_content, 'reporting_learning_path_details');
1396
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

In general, usage of exit should be done with care and only when running in a scripting context like a CLI script.

Loading history...
1397
        }
1398
1399
        return $output;
1400
    }
1401
1402
    /**
1403
     * @param int  $userId
1404
     * @param bool $getCount
1405
     *
1406
     * @return array
1407
     */
1408
    public static function getStats($userId, $getCount = false)
1409
    {
1410
        $courses = [];
1411
        $assignedCourses = [];
1412
        $drhCount = 0;
1413
        $teachersCount = 0;
1414
        $studentsCount = 0;
1415
        $studentBossCount = 0;
1416
        $courseCount = 0;
1417
        $sessionCount = 0;
1418
        $assignedCourseCount = 0;
1419
1420
        if (api_is_drh() && api_drh_can_access_all_session_content()) {
1421
            $studentList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1422
                'drh_all',
1423
                $userId,
1424
                false,
1425
                null,
1426
                null,
1427
                null,
1428
                null,
1429
                null,
1430
                null,
1431
                null,
1432
                [],
1433
                [],
1434
                STUDENT
1435
            );
1436
1437
            $students = [];
1438
            if (is_array($studentList)) {
1439
                foreach ($studentList as $studentData) {
1440
                    $students[] = $studentData['user_id'];
1441
                }
1442
            }
1443
1444
            $studentBossesList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1445
                'drh_all',
1446
                $userId,
1447
                $getCount,
1448
                null,
1449
                null,
1450
                null,
1451
                null,
1452
                null,
1453
                null,
1454
                null,
1455
                [],
1456
                [],
1457
                STUDENT_BOSS
1458
            );
1459
1460
            if ($getCount) {
1461
                $studentBossCount = $studentBossesList;
1462
            } else {
1463
                $studentBosses = [];
1464
                if (is_array($studentBossesList)) {
1465
                    foreach ($studentBossesList as $studentBossData) {
1466
                        $studentBosses[] = $studentBossData['user_id'];
1467
                    }
1468
                }
1469
            }
1470
1471
            $teacherList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1472
                'drh_all',
1473
                $userId,
1474
                $getCount,
1475
                null,
1476
                null,
1477
                null,
1478
                null,
1479
                null,
1480
                null,
1481
                null,
1482
                [],
1483
                [],
1484
                COURSEMANAGER
1485
            );
1486
1487
            if ($getCount) {
1488
                $teachersCount = $teacherList;
1489
            } else {
1490
                $teachers = [];
1491
                foreach ($teacherList as $teacherData) {
1492
                    $teachers[] = $teacherData['user_id'];
1493
                }
1494
            }
1495
1496
            $humanResources = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
1497
                'drh_all',
1498
                $userId,
1499
                $getCount,
1500
                null,
1501
                null,
1502
                null,
1503
                null,
1504
                null,
1505
                null,
1506
                null,
1507
                [],
1508
                [],
1509
                DRH
1510
            );
1511
1512
            if ($getCount) {
1513
                $drhCount = $humanResources;
1514
            } else {
1515
                $humanResourcesList = [];
1516
                if (is_array($humanResources)) {
1517
                    foreach ($humanResources as $item) {
1518
                        $humanResourcesList[] = $item['user_id'];
1519
                    }
1520
                }
1521
            }
1522
1523
            $platformCourses = SessionManager::getAllCoursesFollowedByUser(
1524
                $userId,
1525
                null,
1526
                null,
1527
                null,
1528
                null,
1529
                null,
1530
                $getCount
1531
            );
1532
1533
            if ($getCount) {
1534
                $courseCount = $platformCourses;
1535
            } else {
1536
                foreach ($platformCourses as $course) {
1537
                    $courses[$course['code']] = $course['code'];
1538
                }
1539
            }
1540
1541
            $sessions = SessionManager::get_sessions_followed_by_drh(
1542
                $userId,
1543
                null,
1544
                null,
1545
                false
1546
            );
1547
        } else {
1548
            $studentList = UserManager::getUsersFollowedByUser(
1549
                $userId,
1550
                STUDENT,
1551
                false,
1552
                false,
1553
                false,
1554
                null,
1555
                null,
1556
                null,
1557
                null,
1558
                null,
1559
                null,
1560
                COURSEMANAGER
1561
            );
1562
1563
            $students = [];
1564
            if (is_array($studentList)) {
1565
                foreach ($studentList as $studentData) {
1566
                    $students[] = $studentData['user_id'];
1567
                }
1568
            }
1569
1570
            $studentBossesList = UserManager::getUsersFollowedByUser(
1571
                $userId,
1572
                STUDENT_BOSS,
1573
                false,
1574
                false,
1575
                $getCount,
1576
                null,
1577
                null,
1578
                null,
1579
                null,
1580
                null,
1581
                null,
1582
                COURSEMANAGER
1583
            );
1584
1585
            if ($getCount) {
1586
                $studentBossCount = $studentBossesList;
1587
            } else {
1588
                $studentBosses = [];
1589
                if (is_array($studentBossesList)) {
1590
                    foreach ($studentBossesList as $studentBossData) {
1591
                        $studentBosses[] = $studentBossData['user_id'];
1592
                    }
1593
                }
1594
            }
1595
1596
            $teacherList = UserManager::getUsersFollowedByUser(
1597
                $userId,
1598
                COURSEMANAGER,
1599
                false,
1600
                false,
1601
                $getCount,
1602
                null,
1603
                null,
1604
                null,
1605
                null,
1606
                null,
1607
                null,
1608
                COURSEMANAGER
1609
            );
1610
1611
            if ($getCount) {
1612
                $teachersCount = $teacherList;
1613
            } else {
1614
                $teachers = [];
1615
                foreach ($teacherList as $teacherData) {
1616
                    $teachers[] = $teacherData['user_id'];
1617
                }
1618
            }
1619
1620
            $humanResources = UserManager::getUsersFollowedByUser(
1621
                $userId,
1622
                DRH,
1623
                false,
1624
                false,
1625
                $getCount,
1626
                null,
1627
                null,
1628
                null,
1629
                null,
1630
                null,
1631
                null,
1632
                COURSEMANAGER
1633
            );
1634
1635
            if ($getCount) {
1636
                $drhCount = $humanResources;
1637
            } else {
1638
                $humanResourcesList = [];
1639
                foreach ($humanResources as $item) {
1640
                    $humanResourcesList[] = $item['user_id'];
1641
                }
1642
            }
1643
1644
            $platformCourses = CourseManager::getCoursesFollowedByUser(
1645
                $userId,
1646
                COURSEMANAGER,
1647
                null,
1648
                null,
1649
                null,
1650
                null,
1651
                $getCount,
1652
                null,
1653
                null,
1654
                true
1655
            );
1656
1657
            if ($getCount) {
1658
                $assignedCourseCount = $platformCourses;
1659
            } else {
1660
                foreach ($platformCourses as $course) {
1661
                    $assignedCourses[$course['code']] = $course['code'];
1662
                }
1663
            }
1664
1665
            $platformCourses = CourseManager::getCoursesFollowedByUser(
1666
                $userId,
1667
                COURSEMANAGER,
1668
                null,
1669
                null,
1670
                null,
1671
                null,
1672
                $getCount
1673
            );
1674
1675
            if ($getCount) {
1676
                $courseCount = $platformCourses;
1677
            } else {
1678
                foreach ($platformCourses as $course) {
1679
                    $courses[$course['code']] = $course['code'];
1680
                }
1681
            }
1682
1683
            $sessions = SessionManager::getSessionsFollowedByUser(
1684
                $userId,
1685
                COURSEMANAGER,
1686
                null,
1687
                null,
1688
                false
1689
            );
1690
        }
1691
1692
        if ($getCount) {
1693
            return [
1694
                'drh' => $drhCount,
1695
                'teachers' => $teachersCount,
1696
                'student_count' => count($students),
1697
                'student_list' => $students,
1698
                'student_bosses' => $studentBossCount,
1699
                'courses' => $courseCount,
1700
                'session_count' => count($sessions),
1701
                'session_list' => $sessions,
1702
                'assigned_courses' => $assignedCourseCount,
1703
            ];
1704
        }
1705
1706
        return [
1707
            'drh' => $humanResourcesList,
1708
            'teachers' => $teachers,
1709
            'student_list' => $students,
1710
            'student_bosses' => $studentBosses,
1711
            'courses' => $courses,
1712
            'sessions' => $sessions,
1713
            'assigned_courses' => $assignedCourses,
1714
        ];
1715
    }
1716
1717
    /**
1718
     * Calculates the time spent on the platform by a user.
1719
     *
1720
     * @param int|array $userId
1721
     * @param string    $timeFilter       type of time filter: 'last_week' or 'custom'
1722
     * @param string    $start_date       start date date('Y-m-d H:i:s')
1723
     * @param string    $end_date         end date date('Y-m-d H:i:s')
1724
     * @param bool      $returnAllRecords
1725
     *
1726
     * @return int|array
1727
     */
1728
    public static function get_time_spent_on_the_platform(
1729
        $userId,
1730
        $timeFilter = 'last_7_days',
1731
        $start_date = null,
1732
        $end_date = null,
1733
        $returnAllRecords = false
1734
    ) {
1735
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1736
        $condition_time = '';
1737
1738
        if (is_array($userId)) {
1739
            $userList = array_map('intval', $userId);
1740
            $userCondition = " login_user_id IN ('".implode("','", $userList)."')";
1741
        } else {
1742
            $userId = (int) $userId;
1743
            $userCondition = " login_user_id = $userId ";
1744
        }
1745
1746
        $url_condition = null;
1747
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1748
        $url_table = null;
1749
        if (AccessUrlHelper::isMultiple()) {
1750
            $access_url_id = api_get_current_access_url_id();
1751
            $url_table = ", $tbl_url_rel_user as url_users";
1752
            $url_condition = " AND u.login_user_id = url_users.user_id AND access_url_id='$access_url_id'";
1753
        }
1754
1755
        if (empty($timeFilter)) {
1756
            $timeFilter = 'last_week';
1757
        }
1758
1759
        $today = new DateTime('now', new DateTimeZone('UTC'));
1760
1761
        switch ($timeFilter) {
1762
            case 'last_7_days':
1763
                $newDate = new DateTime('-7 day', new DateTimeZone('UTC'));
1764
                $condition_time = " AND (login_date >= '{$newDate->format('Y-m-d H:i:s')}'";
1765
                $condition_time .= " AND logout_date <= '{$today->format('Y-m-d H:i:s')}') ";
1766
                break;
1767
            case 'last_30_days':
1768
                $newDate = new DateTime('-30 days', new DateTimeZone('UTC'));
1769
                $condition_time = " AND (login_date >= '{$newDate->format('Y-m-d H:i:s')}'";
1770
                $condition_time .= "AND logout_date <= '{$today->format('Y-m-d H:i:s')}') ";
1771
                break;
1772
            case 'wide':
1773
                if (!empty($start_date) && !empty($end_date)) {
1774
                    $start_date = Database::escape_string($start_date);
1775
                    $end_date = Database::escape_string($end_date);
1776
                    $condition_time = ' AND (
1777
                        (login_date >= "'.$start_date.'" AND login_date <= "'.$end_date.'") OR
1778
                        (logout_date >= "'.$start_date.'" AND logout_date <= "'.$end_date.'") OR
1779
                        (login_date <= "'.$start_date.'" AND logout_date >= "'.$end_date.'")
1780
                    ) ';
1781
                }
1782
                break;
1783
            case 'custom':
1784
                if (!empty($start_date) && !empty($end_date)) {
1785
                    $start_date = Database::escape_string($start_date);
1786
                    $end_date = Database::escape_string($end_date);
1787
                    $condition_time = ' AND (login_date >= "'.$start_date.'" AND logout_date <= "'.$end_date.'" ) ';
1788
                }
1789
                break;
1790
        }
1791
1792
        if ($returnAllRecords) {
1793
            $sql = "SELECT login_date, logout_date, TIMESTAMPDIFF(SECOND, login_date, logout_date) diff
1794
                    FROM $tbl_track_login u $url_table
1795
                    WHERE $userCondition $condition_time $url_condition
1796
                    ORDER BY login_date";
1797
            $rs = Database::query($sql);
1798
1799
            return Database::store_result($rs, 'ASSOC');
1800
        }
1801
1802
        $sql = "SELECT SUM(TIMESTAMPDIFF(SECOND, login_date, logout_date)) diff
1803
    	        FROM $tbl_track_login u $url_table
1804
                WHERE $userCondition $condition_time $url_condition";
1805
        $rs = Database::query($sql);
1806
        $row = Database::fetch_assoc($rs);
1807
        $diff = $row['diff'];
1808
1809
        if ($diff >= 0) {
1810
            return $diff;
1811
        }
1812
1813
        return -1;
1814
    }
1815
1816
    /**
1817
     * @param string $startDate
1818
     * @param string $endDate
1819
     *
1820
     * @return int
1821
     */
1822
    public static function getTotalTimeSpentOnThePlatform(
1823
        $startDate = '',
1824
        $endDate = ''
1825
    ) {
1826
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1827
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
1828
1829
        $url_table = null;
1830
        $url_condition = null;
1831
        if (AccessUrlHelper::isMultiple()) {
1832
            $access_url_id = api_get_current_access_url_id();
1833
            $url_table = ", ".$tbl_url_rel_user." as url_users";
1834
            $url_condition = " AND u.login_user_id = url_users.user_id AND access_url_id='$access_url_id'";
1835
        }
1836
1837
        if (!empty($startDate) && !empty($endDate)) {
1838
            $startDate = Database::escape_string($startDate);
1839
            $endDate = Database::escape_string($endDate);
1840
            $condition_time = ' (login_date >= "'.$startDate.'" AND logout_date <= "'.$endDate.'" ) ';
1841
        }
1842
        $sql = "SELECT SUM(TIMESTAMPDIFF(SECOND, login_date, logout_date)) diff
1843
    	        FROM $tbl_track_login u $url_table
1844
                WHERE $condition_time $url_condition";
1845
        $rs = Database::query($sql);
1846
        $row = Database::fetch_assoc($rs);
1847
        $diff = $row['diff'];
1848
1849
        if ($diff >= 0) {
1850
            return $diff;
1851
        }
1852
1853
        return -1;
1854
    }
1855
1856
    /**
1857
     * Checks if the "lp_minimum_time" feature is available for the course.
1858
     *
1859
     * @param int $sessionId
1860
     * @param int $courseId
1861
     *
1862
     * @return bool
1863
     */
1864
    public static function minimumTimeAvailable($sessionId, $courseId)
1865
    {
1866
        if ('true' !== api_get_setting('lp.lp_minimum_time')) {
1867
            return false;
1868
        }
1869
1870
        if (!empty($sessionId)) {
1871
            $extraFieldValue = new ExtraFieldValue('session');
1872
            $value = $extraFieldValue->get_values_by_handler_and_field_variable($sessionId, 'new_tracking_system');
1873
1874
            if ($value && isset($value['value']) && 1 == $value['value']) {
1875
                return true;
1876
            }
1877
        } else {
1878
            if ($courseId) {
1879
                $extraFieldValue = new ExtraFieldValue('course');
1880
                $value = $extraFieldValue->get_values_by_handler_and_field_variable($courseId, 'new_tracking_system');
1881
                if ($value && isset($value['value']) && 1 == $value['value']) {
1882
                    return true;
1883
                }
1884
            }
1885
        }
1886
1887
        return false;
1888
    }
1889
1890
    /**
1891
     * Calculates the time spent on the course.
1892
     *
1893
     * @param array|int $userId
1894
     * @param int       $courseId
1895
     * @param int       $sessionId
1896
     *
1897
     * @return int Time in seconds
1898
     */
1899
    public static function get_time_spent_on_the_course(
1900
        $userId,
1901
        $courseId,
1902
        $sessionId = 0
1903
    ) {
1904
        $courseId = (int) $courseId;
1905
1906
        if (empty($courseId) || empty($userId)) {
1907
            return 0;
1908
        }
1909
1910
        if (self::minimumTimeAvailable($sessionId, $courseId)) {
1911
            $courseTime = self::getCalculateTime($userId, $courseId, $sessionId);
1912
1913
            return isset($courseTime['total_time']) ? $courseTime['total_time'] : 0;
1914
        }
1915
1916
        $conditionUser = '';
1917
        $sessionId = (int) $sessionId;
1918
        if (is_array($userId)) {
1919
            $userId = array_map('intval', $userId);
1920
            $conditionUser = " AND user_id IN (".implode(',', $userId).") ";
1921
        } else {
1922
            if (!empty($userId)) {
1923
                $userId = (int) $userId;
1924
                $conditionUser = " AND user_id = $userId ";
1925
            }
1926
        }
1927
1928
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1929
        $sql = "SELECT
1930
                SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as nb_seconds
1931
                FROM $table
1932
                WHERE
1933
                    UNIX_TIMESTAMP(logout_course_date) > UNIX_TIMESTAMP(login_course_date) AND
1934
                    c_id = '$courseId' ";
1935
1936
        if (-1 != $sessionId) {
1937
            $sql .= "AND session_id = '$sessionId' ";
1938
        }
1939
1940
        $sql .= $conditionUser;
1941
1942
        $rs = Database::query($sql);
1943
        $row = Database::fetch_array($rs);
1944
1945
        return $row['nb_seconds'];
1946
    }
1947
1948
    /**
1949
     * Get first connection date for a student.
1950
     *
1951
     * @param int $student_id
1952
     *
1953
     * @return string|bool Date format long without day or false if there are no connections
1954
     */
1955
    public static function get_first_connection_date($student_id)
1956
    {
1957
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1958
        $sql = 'SELECT login_date
1959
                FROM '.$table.'
1960
                WHERE login_user_id = '.intval($student_id).'
1961
                ORDER BY login_date ASC
1962
                LIMIT 0,1';
1963
1964
        $rs = Database::query($sql);
1965
        if (Database::num_rows($rs) > 0) {
1966
            if ($first_login_date = Database::result($rs, 0, 0)) {
1967
                return api_convert_and_format_date(
1968
                    $first_login_date,
1969
                    DATE_FORMAT_SHORT
1970
                );
1971
            }
1972
        }
1973
1974
        return false;
1975
    }
1976
1977
    /**
1978
     * Get las connection date for a student.
1979
     *
1980
     * @param int  $student_id
1981
     * @param bool $warning_message  Show a warning message (optional)
1982
     * @param bool $return_timestamp True for returning results in timestamp (optional)
1983
     *
1984
     * @return string|int|bool Date format long without day, false if there are no connections or
1985
     *                         timestamp if parameter $return_timestamp is true
1986
     */
1987
    public static function get_last_connection_date(
1988
        $student_id,
1989
        $warning_message = false,
1990
        $return_timestamp = false
1991
    ) {
1992
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
1993
        $sql = 'SELECT login_date
1994
                FROM '.$table.'
1995
                WHERE login_user_id = '.intval($student_id).'
1996
                ORDER BY login_date
1997
                DESC LIMIT 0,1';
1998
1999
        $rs = Database::query($sql);
2000
        if (Database::num_rows($rs) > 0) {
2001
            if ($last_login_date = Database::result($rs, 0, 0)) {
2002
                $last_login_date = api_get_local_time($last_login_date);
2003
                if ($return_timestamp) {
2004
                    return api_strtotime($last_login_date, 'UTC');
2005
                } else {
2006
                    if (!$warning_message) {
2007
                        return api_format_date($last_login_date, DATE_FORMAT_SHORT);
2008
                    } else {
2009
                        $timestamp = api_strtotime($last_login_date, 'UTC');
2010
                        $currentTimestamp = time();
2011
2012
                        //If the last connection is > than 7 days, the text is red
2013
                        //345600 = 7 days in seconds
2014
                        if ($currentTimestamp - $timestamp > 604800) {
2015
                            return '<em style="color: #F00;">'.api_format_date($last_login_date, DATE_FORMAT_SHORT).'</em>';
2016
                        } else {
2017
                            return api_format_date($last_login_date, DATE_FORMAT_SHORT);
2018
                        }
2019
                    }
2020
                }
2021
            }
2022
        }
2023
2024
        return false;
2025
    }
2026
2027
    /**
2028
     * Get first user's connection date on the course.
2029
     *
2030
     * @param int User id
2031
     * @param int $courseId
2032
     * @param int Session id (optional, default=0)
2033
     * @param bool $convert_date
2034
     *
2035
     * @return string|bool Date with format long without day or false if there is no date
2036
     */
2037
    public static function get_first_connection_date_on_the_course(
2038
        $student_id,
2039
        $courseId,
2040
        $sessionId = 0,
2041
        $convert_date = true
2042
    ) {
2043
        $student_id = (int) $student_id;
2044
        $courseId = (int) $courseId;
2045
        $sessionId = (int) $sessionId;
2046
2047
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2048
        $sql = 'SELECT login_course_date
2049
                FROM '.$table.'
2050
                WHERE
2051
                    user_id = '.$student_id.' AND
2052
                    c_id = '.$courseId.' AND
2053
                    session_id = '.$sessionId.'
2054
                ORDER BY login_course_date ASC
2055
                LIMIT 0,1';
2056
        $rs = Database::query($sql);
2057
        if (Database::num_rows($rs) > 0) {
2058
            if ($first_login_date = Database::result($rs, 0, 0)) {
2059
                if (empty($first_login_date)) {
2060
                    return false;
2061
                }
2062
2063
                if ($convert_date) {
2064
                    return api_convert_and_format_date(
2065
                        $first_login_date,
2066
                        DATE_FORMAT_SHORT
2067
                    );
2068
                }
2069
2070
                return $first_login_date;
2071
            }
2072
        }
2073
2074
        return false;
2075
    }
2076
2077
    /**
2078
     * Get last user's connection date on the course.
2079
     *
2080
     * @param     int         User id
2081
     * @param array $courseInfo real_id and code are used
2082
     * @param    int            Session id (optional, default=0)
2083
     * @param bool $convert_date
2084
     *
2085
     * @return string|bool Date with format long without day or false if there is no date
2086
     */
2087
    public static function get_last_connection_date_on_the_course(
2088
        $student_id,
2089
        $courseInfo,
2090
        $sessionId = 0,
2091
        $convert_date = true
2092
    ) {
2093
        // protect data
2094
        $student_id = (int) $student_id;
2095
        $sessionId = (int) $sessionId;
2096
2097
        if (empty($courseInfo) || empty($student_id)) {
2098
            return false;
2099
        }
2100
2101
        $courseId = (int) $courseInfo['real_id'];
2102
2103
        if (empty($courseId)) {
2104
            return false;
2105
        }
2106
2107
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2108
2109
        if (self::minimumTimeAvailable($sessionId, $courseId)) {
2110
            // Show the last date on which the user acceed the session when it was active
2111
            $where_condition = '';
2112
            $userInfo = api_get_user_info($student_id);
2113
            if (STUDENT == $userInfo['status'] && !empty($sessionId)) {
2114
                // fin de acceso a la sesión
2115
                $sessionInfo = SessionManager::fetch($sessionId);
2116
                $last_access = $sessionInfo['access_end_date'];
2117
                if (!empty($last_access)) {
2118
                    $where_condition = ' AND logout_course_date < "'.$last_access.'" ';
2119
                }
2120
            }
2121
            $sql = "SELECT logout_course_date
2122
                    FROM $table
2123
                    WHERE   user_id = $student_id AND
2124
                            c_id = $courseId AND
2125
                            session_id = $sessionId $where_condition
2126
                    ORDER BY logout_course_date DESC
2127
                    LIMIT 0,1";
2128
2129
            $rs = Database::query($sql);
2130
            if (Database::num_rows($rs) > 0) {
2131
                if ($last_login_date = Database::result($rs, 0, 0)) {
2132
                    if (empty($last_login_date)) {
2133
                        return false;
2134
                    }
2135
                    if ($convert_date) {
2136
                        return api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
2137
                    }
2138
2139
                    return $last_login_date;
2140
                }
2141
            }
2142
        } else {
2143
            $sql = "SELECT logout_course_date
2144
                    FROM $table
2145
                    WHERE   user_id = $student_id AND
2146
                            c_id = $courseId AND
2147
                            session_id = $sessionId
2148
                    ORDER BY logout_course_date DESC
2149
                    LIMIT 0,1";
2150
2151
            $rs = Database::query($sql);
2152
            if (Database::num_rows($rs) > 0) {
2153
                if ($last_login_date = Database::result($rs, 0, 0)) {
2154
                    if (empty($last_login_date)) {
2155
                        return false;
2156
                    }
2157
                    //see #5736
2158
                    $last_login_date_timestamp = api_strtotime($last_login_date);
2159
                    $now = time();
2160
                    //If the last connection is > than 7 days, the text is red
2161
                    //345600 = 7 days in seconds
2162
                    if ($now - $last_login_date_timestamp > 604800) {
2163
                        if ($convert_date) {
2164
                            $last_login_date = api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
2165
                            $icon = null;
2166
                            if (api_is_allowed_to_edit()) {
2167
                                $url = api_get_path(WEB_CODE_PATH).
2168
                                    'announcements/announcements.php?action=add&remind_inactive='.$student_id.'&cid='.$courseInfo['real_id'];
2169
                                $icon = '<a href="'.$url.'" title="'.get_lang('Remind inactive user').'">
2170
                                  '.Display::getMdiIcon(
2171
                                      StateIcon::WARNING,
2172
                                      'ch-tool-icon',
2173
                                      null,
2174
                                      ICON_SIZE_SMALL
2175
                                    ).'
2176
                                 </a>';
2177
                            }
2178
2179
                            return $icon.Display::label($last_login_date, 'warning');
2180
                        }
2181
2182
                        return $last_login_date;
2183
                    } else {
2184
                        if ($convert_date) {
2185
                            return api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
2186
                        }
2187
2188
                        return $last_login_date;
2189
                    }
2190
                }
2191
            }
2192
        }
2193
2194
        return false;
2195
    }
2196
2197
    public static function getLastConnectionInAnyCourse($studentId)
2198
    {
2199
        $studentId = (int) $studentId;
2200
2201
        if (empty($studentId)) {
2202
            return false;
2203
        }
2204
2205
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2206
        $sql = "SELECT logout_course_date
2207
                FROM $table
2208
                WHERE user_id = $studentId
2209
                ORDER BY logout_course_date DESC
2210
                LIMIT 1";
2211
        $result = Database::query($sql);
2212
        if (Database::num_rows($result)) {
2213
            $row = Database::fetch_array($result);
2214
2215
            return $row['logout_course_date'];
2216
        }
2217
2218
        return false;
2219
    }
2220
2221
    /**
2222
     * Get last course access by course/session.
2223
     */
2224
    public static function getLastConnectionDateByCourse($courseId, $sessionId = 0)
2225
    {
2226
        $courseId = (int) $courseId;
2227
        $sessionId = (int) $sessionId;
2228
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2229
2230
        $sql = "SELECT logout_course_date
2231
                FROM $table
2232
                WHERE
2233
                        c_id = $courseId AND
2234
                        session_id = $sessionId
2235
                ORDER BY logout_course_date DESC
2236
                LIMIT 0,1";
2237
2238
        $result = Database::query($sql);
2239
        if (Database::num_rows($result)) {
2240
            $row = Database::fetch_array($result);
2241
            if ($row) {
2242
                return $row['logout_course_date'];
2243
            }
2244
        }
2245
2246
        return '';
2247
    }
2248
2249
    /**
2250
     * Get count of the connections to the course during a specified period.
2251
     *
2252
     * @param int $courseId
2253
     * @param   int     Session id (optional)
2254
     * @param   int     Datetime from which to collect data (defaults to 0)
2255
     * @param   int     Datetime to which to collect data (defaults to now)
2256
     *
2257
     * @return int count connections
2258
     */
2259
    public static function get_course_connections_count(
2260
        $courseId,
2261
        $sessionId = 0,
2262
        $start = 0,
2263
        $stop = null
2264
    ) {
2265
        if ($start < 0) {
2266
            $start = 0;
2267
        }
2268
        if (!isset($stop) || $stop < 0) {
2269
            $stop = api_get_utc_datetime();
2270
        }
2271
2272
        // Given we're storing in cache, round the start and end times
2273
        // to the lower minute
2274
        $roundedStart = substr($start, 0, -2).'00';
2275
        $roundedStop = substr($stop, 0, -2).'00';
2276
        $roundedStart = Database::escape_string($roundedStart);
2277
        $roundedStop = Database::escape_string($roundedStop);
2278
        $month_filter = " AND login_course_date > '$roundedStart' AND login_course_date < '$roundedStop' ";
2279
        $courseId = (int) $courseId;
2280
        $sessionId = (int) $sessionId;
2281
        $count = 0;
2282
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2283
        $sql = "SELECT count(*) as count_connections
2284
                FROM $table
2285
                WHERE
2286
                    c_id = $courseId AND
2287
                    session_id = $sessionId
2288
                    $month_filter";
2289
2290
        //This query can be very slow (several seconds on an indexed table
2291
        // with 14M rows). As such, we'll try to use APCu if it is
2292
        // available to store the resulting value for a few seconds
2293
        $cacheAvailable = api_get_configuration_value('apc');
2294
        if (true === $cacheAvailable) {
2295
            $apc = apcu_cache_info(true);
2296
            $apc_end = $apc['start_time'] + $apc['ttl'];
2297
            $apc_var = api_get_configuration_value('apc_prefix').'course_access_'.$courseId.'_'.$sessionId.'_'.strtotime($roundedStart).'_'.strtotime($roundedStop);
2298
            if (apcu_exists($apc_var) && (time() < $apc_end) &&
2299
                apcu_fetch($apc_var) > 0
2300
            ) {
2301
                $count = apcu_fetch($apc_var);
2302
            } else {
2303
                $rs = Database::query($sql);
2304
                if (Database::num_rows($rs) > 0) {
2305
                    $row = Database::fetch_object($rs);
2306
                    $count = $row->count_connections;
2307
                }
2308
                apcu_clear_cache();
2309
                apcu_store($apc_var, $count, 60);
2310
            }
2311
        } else {
2312
            $rs = Database::query($sql);
2313
            if (Database::num_rows($rs) > 0) {
2314
                $row = Database::fetch_object($rs);
2315
                $count = $row->count_connections;
2316
            }
2317
        }
2318
2319
        return $count;
2320
    }
2321
2322
    /**
2323
     * Get count courses per student.
2324
     *
2325
     * @param int  $user_id          Student id
2326
     * @param bool $include_sessions Include sessions (optional)
2327
     *
2328
     * @return int count courses
2329
     */
2330
    public static function count_course_per_student($user_id, $include_sessions = true)
2331
    {
2332
        $user_id = (int) $user_id;
2333
        $tbl_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
2334
        $tbl_session_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
2335
2336
        $sql = 'SELECT DISTINCT c_id
2337
                FROM '.$tbl_course_rel_user.'
2338
                WHERE user_id = '.$user_id.' AND relation_type<>'.COURSE_RELATION_TYPE_RRHH;
2339
        $rs = Database::query($sql);
2340
        $nb_courses = Database::num_rows($rs);
2341
2342
        if ($include_sessions) {
2343
            $sql = 'SELECT DISTINCT c_id
2344
                    FROM '.$tbl_session_course_rel_user.'
2345
                    WHERE user_id = '.$user_id;
2346
            $rs = Database::query($sql);
2347
            $nb_courses += Database::num_rows($rs);
2348
        }
2349
2350
        return $nb_courses;
2351
    }
2352
2353
    /**
2354
     * Gets the score average from all tests in a course by student.
2355
     *
2356
     * @param $student_id
2357
     * @param $course_code
2358
     * @param int  $exercise_id
2359
     * @param null $sessionId
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $sessionId is correct as it would always require null to be passed?
Loading history...
2360
     * @param int  $active_filter 2 for consider all tests
2361
     *                            1 for active <> -1
2362
     *                            0 for active <> 0
2363
     * @param int  $into_lp       1 for all exercises
2364
     *                            0 for without LP
2365
     * @param mixed id
2366
     * @param string code
2367
     * @param int id (optional), filtered by exercise
2368
     * @param int id (optional), if param $sessionId is null
2369
     *                                               it'll return results including sessions, 0 = session is not
2370
     *                                               filtered
2371
     *
2372
     * @return string value (number %) Which represents a round integer about the score average
2373
     */
2374
    public static function get_avg_student_exercise_score(
2375
        $student_id,
2376
        $course_code,
2377
        $exercise_id = 0,
2378
        $sessionId = null,
2379
        $active_filter = 1,
2380
        $into_lp = 0
2381
    ) {
2382
        $course_code = Database::escape_string($course_code);
2383
        $course_info = api_get_course_info($course_code);
2384
        if (!empty($course_info)) {
2385
            // table definition
2386
            $tbl_course_quiz = Database::get_course_table(TABLE_QUIZ_TEST);
2387
            $tbl_stats_exercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2388
2389
            // Compose a filter based on optional exercise given
2390
            $condition_quiz = '';
2391
            if (!empty($exercise_id)) {
2392
                $exercise_id = (int) $exercise_id;
2393
                $condition_quiz = " AND iid = $exercise_id ";
2394
            }
2395
2396
            // Compose a filter based on optional session id given
2397
            $condition_session = '';
2398
            $session = null;
2399
            if (isset($sessionId)) {
2400
                $session = api_get_session_entity($course_info['real_id']);
2401
            }
2402
            $sessionCondition = api_get_session_condition($sessionId);
2403
2404
            $condition_active = '';
2405
            if (1 == $active_filter) {
2406
                $condition_active = 'AND active <> -1';
2407
            } elseif (0 == $active_filter) {
2408
                $condition_active = 'AND active <> 0';
2409
            }
2410
            $condition_into_lp = '';
2411
            $select_lp_id = '';
2412
            if (0 == $into_lp) {
2413
                $condition_into_lp = 'AND orig_lp_id = 0 AND orig_lp_item_id = 0';
2414
            } else {
2415
                $select_lp_id = ', orig_lp_id as lp_id ';
2416
            }
2417
2418
            $quizRepo = Container::getQuizRepository();
2419
            $course = api_get_course_entity($course_info['real_id']);
2420
            $qb = $quizRepo->getResourcesByCourse($course, $session);
2421
            $qb
2422
                ->select('count(resource)')
2423
                ->setMaxResults(1);
2424
            $count_quiz = $qb->getQuery()->getSingleScalarResult();
2425
2426
            /*$sql = "SELECT count(iid)
2427
    		        FROM $tbl_course_quiz
2428
    				WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz ";
2429
            $count_quiz = 0;
2430
            $countQuizResult = Database::query($sql);
2431
            if (!empty($countQuizResult)) {
2432
                $count_quiz = Database::fetch_row($countQuizResult);
2433
            }*/
2434
            if (!empty($count_quiz) && !empty($student_id)) {
2435
                if (is_array($student_id)) {
2436
                    $student_id = array_map('intval', $student_id);
2437
                    $condition_user = " AND exe_user_id IN (".implode(',', $student_id).") ";
2438
                } else {
2439
                    $student_id = (int) $student_id;
2440
                    $condition_user = " AND exe_user_id = '$student_id' ";
2441
                }
2442
2443
                if (empty($exercise_id)) {
2444
                    $sql = "SELECT iid FROM $tbl_course_quiz
2445
                            WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz";
2446
                    $result = Database::query($sql);
2447
                    $exercise_list = [];
2448
                    $exercise_id = null;
2449
                    if (!empty($result) && Database::num_rows($result)) {
2450
                        while ($row = Database::fetch_array($result)) {
2451
                            $exercise_list[] = $row['iid'];
2452
                        }
2453
                    }
2454
                    if (!empty($exercise_list)) {
2455
                        $exercise_id = implode("','", $exercise_list);
2456
                    }
2457
                }
2458
2459
                $sql = "SELECT
2460
                        SUM(score/max_score*100) as avg_score,
2461
                        COUNT(*) as num_attempts
2462
                        $select_lp_id
2463
                        FROM $tbl_stats_exercise
2464
                        WHERE
2465
                            exe_exo_id IN ('".$exercise_id."')
2466
                            $condition_user AND
2467
                            status = '' AND
2468
                            c_id = {$course_info['real_id']}
2469
                            $sessionCondition
2470
                            $condition_into_lp
2471
                        ORDER BY exe_date DESC";
2472
2473
                $res = Database::query($sql);
2474
                $row = Database::fetch_array($res);
2475
                $quiz_avg_score = null;
2476
2477
                if (!empty($row['avg_score'])) {
2478
                    $quiz_avg_score = round($row['avg_score'], 2);
2479
                }
2480
2481
                if (!empty($row['num_attempts'])) {
2482
                    $quiz_avg_score = round($quiz_avg_score / $row['num_attempts'], 2);
2483
                }
2484
                if (is_array($student_id)) {
2485
                    $quiz_avg_score = round($quiz_avg_score / count($student_id), 2);
2486
                }
2487
                if (0 == $into_lp) {
2488
                    return $quiz_avg_score;
2489
                } else {
2490
                    if (!empty($row['lp_id'])) {
2491
                        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
2492
                        $sql = "SELECT title FROM $tbl_lp WHERE iid = ".(int) $row['lp_id'];
2493
                        $result = Database::query($sql);
2494
                        $row_lp = Database::fetch_row($result);
2495
                        $lp_name = null;
2496
                        if ($row_lp && isset($row_lp[0])) {
2497
                            $lp_name = $row_lp[0];
2498
                        }
2499
2500
                        return [$quiz_avg_score, $lp_name];
2501
                    }
2502
2503
                    return [$quiz_avg_score, null];
2504
                }
2505
            }
2506
        }
2507
2508
        return null;
2509
    }
2510
2511
    /**
2512
     * Get count student's exercise COMPLETED attempts.
2513
     *
2514
     * @param int $student_id
2515
     * @param int $courseId
2516
     * @param int $exercise_id
2517
     * @param int $lp_id
2518
     * @param int $lp_item_id
2519
     * @param int $sessionId
2520
     * @param int $find_all_lp 0 = just LP specified
2521
     *                         1 = LP specified or whitout LP,
2522
     *                         2 = all rows
2523
     *
2524
     * @internal param \Student $int id
2525
     * @internal param \Course $string code
2526
     * @internal param \Exercise $int id
2527
     * @internal param \Learning $int path id (optional),
2528
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required
2529
     * @internal param \Learning $int path item id (optional),
2530
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required
2531
     *
2532
     * @return int count of attempts
2533
     */
2534
    public static function count_student_exercise_attempts(
2535
        $student_id,
2536
        $courseId,
2537
        $exercise_id,
2538
        $lp_id = 0,
2539
        $lp_item_id = 0,
2540
        $sessionId = 0,
2541
        $find_all_lp = 0
2542
    ) {
2543
        $courseId = intval($courseId);
2544
        $student_id = intval($student_id);
2545
        $exercise_id = intval($exercise_id);
2546
        $sessionId = intval($sessionId);
2547
2548
        $lp_id = intval($lp_id);
2549
        $lp_item_id = intval($lp_item_id);
2550
        $tbl_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2551
        $sessionCondition = api_get_session_condition($sessionId);
2552
        $sql = "SELECT COUNT(exe_id) as essais
2553
                FROM $tbl_stats_exercises
2554
                WHERE
2555
                    c_id = $courseId AND
2556
                    exe_exo_id = $exercise_id AND
2557
                    status = '' AND
2558
                    exe_user_id= $student_id
2559
                    $sessionCondition
2560
                    ";
2561
2562
        if (1 == $find_all_lp) {
2563
            $sql .= "AND (orig_lp_id = $lp_id OR orig_lp_id = 0)
2564
                AND (orig_lp_item_id = $lp_item_id OR orig_lp_item_id = 0)";
2565
        } elseif (0 == $find_all_lp) {
2566
            $sql .= "AND orig_lp_id = $lp_id
2567
                AND orig_lp_item_id = $lp_item_id";
2568
        }
2569
2570
        $rs = Database::query($sql);
2571
        $row = Database::fetch_row($rs);
2572
        $count_attempts = $row[0];
2573
2574
        return $count_attempts;
2575
    }
2576
2577
    /**
2578
     * Get count student's exercise progress.
2579
     *
2580
     * @param CQuiz[] $exerciseList
2581
     * @param int     $user_id
2582
     * @param int     $courseId
2583
     * @param int     $sessionId
2584
     *
2585
     * @return string
2586
     */
2587
    public static function get_exercise_student_progress(
2588
        $exerciseList,
2589
        $user_id,
2590
        $courseId,
2591
        $sessionId
2592
    ) {
2593
        $courseId = (int) $courseId;
2594
        $user_id = (int) $user_id;
2595
        $sessionId = (int) $sessionId;
2596
2597
        if (empty($exerciseList)) {
2598
            return '0%';
2599
        }
2600
        $tbl_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2601
        $exerciseIdList = [];
2602
        foreach ($exerciseList as $exercise) {
2603
            $exerciseIdList[] = $exercise->getIid();
2604
        }
2605
        $exercise_list_imploded = implode("' ,'", $exerciseIdList);
2606
        $sessionCondition = api_get_session_condition($sessionId);
2607
        $sql = "SELECT COUNT(DISTINCT exe_exo_id)
2608
                FROM $tbl_stats_exercises
2609
                WHERE
2610
                    c_id = $courseId AND
2611
                    session_id  = $sessionId AND
2612
                    exe_user_id = $user_id AND
2613
                    status = '' AND
2614
                    exe_exo_id IN ('$exercise_list_imploded')
2615
                    $sessionCondition
2616
                    ";
2617
2618
        $rs = Database::query($sql);
2619
        $count = 0;
2620
        if ($rs) {
2621
            $row = Database::fetch_row($rs);
2622
            $count = (int) $row[0];
2623
        }
2624
        $count = (0 != $count) ? 100 * round($count / count($exerciseList), 2).'%' : '0%';
2625
2626
        return $count;
2627
    }
2628
2629
    /**
2630
     * @param CQuiz $exercise_list
2631
     * @param int   $user_id
2632
     * @param int   $courseId
2633
     * @param int   $sessionId
2634
     *
2635
     * @return string
2636
     */
2637
    public static function get_exercise_student_average_best_attempt(
2638
        $exercise_list,
2639
        $user_id,
2640
        $courseId,
2641
        $sessionId
2642
    ) {
2643
        $result = 0;
2644
        if (!empty($exercise_list) && (is_array($exercise_list) || $exercise_list instanceof \Countable)) {
2645
            foreach ($exercise_list as $exercise_data) {
2646
                $exercise_id = $exercise_data->getIid();
2647
                $best_attempt = Event::get_best_attempt_exercise_results_per_user(
2648
                    $user_id,
2649
                    $exercise_id,
2650
                    $courseId,
2651
                    $sessionId,
2652
                    false
2653
                );
2654
2655
                if (!empty($best_attempt) && !empty($best_attempt['max_score'])) {
2656
                    $result += $best_attempt['score'] / $best_attempt['max_score'];
2657
                }
2658
            }
2659
2660
            if (count($exercise_list) > 0) {
2661
                $result = $result / count($exercise_list);
2662
                $result = round($result, 2) * 100;
2663
            }
2664
        }
2665
2666
        return $result.'%';
2667
    }
2668
2669
    /**
2670
     * Returns the average student progress in the learning paths of the given
2671
     * course, it will take into account the progress that were not started.
2672
     *
2673
     * @param int|array     $studentId
2674
     * @param Course        $course          The course object
2675
     * @param array         $lpIdList        Limit average to listed lp ids
2676
     * @param SessionEntity $session         Session id (optional),
2677
     *                                       if parameter $sessionId is null(default) it'll return results including
2678
     *                                       sessions, 0 = session is not filtered
2679
     * @param bool          $returnArray     Will return an array of the type:
2680
     *                                       [sum_of_progresses, number] if it is set to true
2681
     * @param bool          $onlySeriousGame Optional. Limit average to lp on seriousgame mode
2682
     *
2683
     * @return float Average progress of the user in this course from 0 to 100
2684
     */
2685
    public static function get_avg_student_progress(
2686
        $studentId,
2687
        Course $course = null,
2688
        $lpIdList = [],
2689
        SessionEntity $session = null,
2690
        $returnArray = false,
2691
        $onlySeriousGame = false
2692
    ) {
2693
        // If there is at least one learning path and one student.
2694
        if (empty($studentId)) {
2695
            return false;
2696
        }
2697
        if (empty($course)) {
2698
            return false;
2699
        }
2700
2701
        $repo = Container::getLpRepository();
2702
        $qb = $repo->findAllByCourse($course, $session);
2703
        $lps = $qb->getQuery()->getResult();
2704
        $filteredLP = [];
2705
2706
        $sessionId = null !== $session ? $session->getId() : 0;
2707
2708
        /** @var CLp $lp */
2709
        foreach ($lps as $lp) {
2710
            $filteredLP[] = $lp->getIid();
2711
        }
2712
2713
        if (empty($filteredLP)) {
2714
            return false;
2715
        }
2716
2717
        $lpViewTable = Database::get_course_table(TABLE_LP_VIEW);
2718
        /*$lpConditions = [];
2719
        $lpConditions['c_id = ? '] = $courseInfo['real_id'];
2720
2721
        if ($sessionId > 0) {
2722
            $lpConditions['AND (session_id = ? OR session_id = 0 OR session_id IS NULL)'] = $sessionId;
2723
        } else {
2724
            $lpConditions['AND session_id = ?'] = $sessionId;
2725
        }
2726
2727
        if (is_array($lpIdList) && count($lpIdList) > 0) {
2728
            $placeHolders = [];
2729
            for ($i = 0; $i < count($lpIdList); $i++) {
2730
                $placeHolders[] = '?';
2731
            }
2732
            $lpConditions['AND iid IN('.implode(', ', $placeHolders).') '] = $lpIdList;
2733
        }
2734
2735
        if ($onlySeriousGame) {
2736
            $lpConditions['AND seriousgame_mode = ? '] = true;
2737
        }
2738
2739
        $resultLP = Database::select(
2740
            'iid',
2741
            $lPTable,
2742
            ['where' => $lpConditions]
2743
        );
2744
        $filteredLP = array_keys($resultLP);
2745
2746
        if (empty($filteredLP)) {
2747
            return false;
2748
        }*/
2749
2750
        $conditions = [
2751
            //" c_id = {$courseInfo['real_id']} ",
2752
            " lp_view.lp_id IN (".implode(', ', $filteredLP).") ",
2753
        ];
2754
2755
        $groupBy = 'GROUP BY lp_id';
2756
2757
        if (is_array($studentId)) {
2758
            $studentId = array_map('intval', $studentId);
2759
            $conditions[] = " lp_view.user_id IN (".implode(',', $studentId).")  ";
2760
        } else {
2761
            $studentId = (int) $studentId;
2762
            $conditions[] = " lp_view.user_id = '$studentId' ";
2763
2764
            if (empty($lpIdList)) {
2765
                $lpList = new LearnpathList(
2766
                    $studentId,
2767
                    ['real_id' => $course->getId()],
2768
                    $sessionId,
2769
                    null,
2770
                    false,
2771
                    null,
2772
                    true
2773
                );
2774
                $lpList = $lpList->get_flat_list();
2775
                if (!empty($lpList)) {
2776
                    /** @var $lp */
2777
                    foreach ($lpList as $lpId => $lp) {
2778
                        $lpIdList[] = $lp['lp_old_id'];
2779
                    }
2780
                }
2781
            }
2782
        }
2783
2784
        if (!empty($sessionId)) {
2785
            $conditions[] = " session_id = $sessionId ";
2786
        } else {
2787
            $conditions[] = ' (session_id = 0 OR session_id IS NULL) ';
2788
        }
2789
2790
        $conditionToString = implode('AND', $conditions);
2791
        $sql = "SELECT lp_id, view_count, progress
2792
                FROM $lpViewTable lp_view
2793
                WHERE
2794
                    $conditionToString
2795
                    $groupBy
2796
                ORDER BY view_count DESC";
2797
2798
        $result = Database::query($sql);
2799
2800
        $progress = [];
2801
        $viewCount = [];
2802
        while ($row = Database::fetch_assoc($result)) {
2803
            if (!isset($viewCount[$row['lp_id']])) {
2804
                $progress[$row['lp_id']] = $row['progress'];
2805
            }
2806
            $viewCount[$row['lp_id']] = $row['view_count'];
2807
        }
2808
2809
        // Fill with lp ids
2810
        $newProgress = [];
2811
        if (!empty($lpIdList)) {
2812
            foreach ($lpIdList as $lpId) {
2813
                if (isset($progress[$lpId])) {
2814
                    $newProgress[] = $progress[$lpId];
2815
                }
2816
            }
2817
            $total = count($lpIdList);
2818
        } else {
2819
            $newProgress = $progress;
2820
            $total = count($newProgress);
2821
        }
2822
2823
        $average = 0;
2824
        $sum = 0;
2825
        if (!empty($newProgress)) {
2826
            $sum = array_sum($newProgress);
2827
            $average = $sum / $total;
2828
        }
2829
2830
        if ($returnArray) {
2831
            return [
2832
                $sum,
2833
                $total,
2834
            ];
2835
        }
2836
2837
        return round($average, 1);
2838
    }
2839
2840
    /**
2841
     * This function gets:
2842
     * 1. The score average from all SCORM Test items in all LP in a course-> All the answers / All the max scores.
2843
     * 2. The score average from all Tests (quiz) in all LP in a course-> All the answers / All the max scores.
2844
     * 3. And finally it will return the average between 1. and 2.
2845
     *
2846
     * @param mixed         $student_id                      Array of user ids or an user id
2847
     * @param array         $lp_ids                          List of LP ids
2848
     * @param SessionEntity $session
2849
     *                                                       if param $sessionId is null(default) it'll return results
2850
     *                                                       including sessions, 0 = session is not filtered
2851
     * @param bool          $return_array                    Returns an array of the
2852
     *                                                       type [sum_score, num_score] if set to true
2853
     * @param bool          $get_only_latest_attempt_results get only the latest attempts or ALL attempts
2854
     * @param bool          $getOnlyBestAttempt
2855
     *
2856
     * @return string value (number %) Which represents a round integer explain in got in 3
2857
     *
2858
     * @todo improve performance, when loading 1500 users with 20 lps the script dies
2859
     * This function does not take the results of a Test out of a LP
2860
     */
2861
    public static function get_avg_student_score(
2862
        $student_id,
2863
        Course $course,
2864
        $lp_ids = [],
2865
        SessionEntity $session = null,
2866
        $return_array = false,
2867
        $get_only_latest_attempt_results = false,
2868
        $getOnlyBestAttempt = false
2869
    ) {
2870
        if (empty($student_id)) {
2871
            return null;
2872
        }
2873
2874
        $debug = false;
2875
        $tbl_stats_exercices = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
2876
        $tbl_stats_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
2877
2878
        // Get course tables names
2879
        $tbl_quiz_questions = Database::get_course_table(TABLE_QUIZ_QUESTION);
2880
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
2881
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
2882
        $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
2883
        $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
2884
        $courseId = $course->getId();
2885
2886
        // Compose a filter based on optional learning paths list given
2887
        $condition_lp = '';
2888
        if (count($lp_ids) > 0) {
2889
            $condition_lp = " iid IN(".implode(',', $lp_ids).") ";
2890
        }
2891
2892
        // Compose a filter based on optional session id
2893
        $sessionId = null;
2894
        if (null !== $session) {
2895
            $sessionId = $session->getId();
2896
        }
2897
        $sessionCondition = api_get_session_condition($sessionId);
2898
2899
        $lp_list = $use_max_score = [];
2900
        if (empty($condition_lp)) {
2901
            $repo = Container::getLpRepository();
2902
            $qb = $repo->findAllByCourse($course, $session);
2903
            $lps = $qb->getQuery()->getResult();
2904
            /** @var CLp $lp */
2905
            foreach ($lps as $lp) {
2906
                $lpId = $lp->getIid();
2907
                $lp_list[] = $lpId;
2908
                $use_max_score[$lpId] = $lp->getUseMaxScore();
2909
            }
2910
        } else {
2911
            $sql = "SELECT DISTINCT(iid), use_max_score
2912
                    FROM $lp_table
2913
                    WHERE $condition_lp ";
2914
            $res_row_lp = Database::query($sql);
2915
            while ($row_lp = Database::fetch_array($res_row_lp)) {
2916
                $lp_list[] = $row_lp['iid'];
2917
                $use_max_score[$row_lp['iid']] = $row_lp['use_max_score'];
2918
            }
2919
        }
2920
2921
        if (empty($lp_list)) {
2922
            return null;
2923
        }
2924
2925
        // prepare filter on users
2926
        if (is_array($student_id)) {
2927
            array_walk($student_id, 'intval');
2928
            $condition_user1 = " AND user_id IN (".implode(',', $student_id).") ";
2929
        } else {
2930
            $condition_user1 = " AND user_id = $student_id ";
2931
        }
2932
2933
        // Getting latest LP result for a student
2934
        //@todo problem when a  course have more than 1500 users
2935
        $sql = "SELECT MAX(view_count) as vc, iid, progress, lp_id, user_id
2936
                FROM $lp_view_table
2937
                WHERE
2938
                    lp_id IN (".implode(',', $lp_list).")
2939
                    $condition_user1
2940
                    $sessionCondition
2941
                GROUP BY lp_id, user_id";
2942
        //AND        session_id = $sessionId
2943
2944
        $rs_last_lp_view_id = Database::query($sql);
2945
        $global_result = 0;
2946
        if (Database::num_rows($rs_last_lp_view_id) > 0) {
2947
            // Cycle through each line of the results (grouped by lp_id, user_id)
2948
            while ($row_lp_view = Database::fetch_array($rs_last_lp_view_id)) {
2949
                $count_items = 0;
2950
                $lpPartialTotal = 0;
2951
                $list = [];
2952
                $lp_view_id = $row_lp_view['iid'];
2953
                $lp_id = $row_lp_view['lp_id'];
2954
                $user_id = $row_lp_view['user_id'];
2955
2956
                if ($debug) {
2957
                    echo '<h2>LP id '.$lp_id.'</h2>';
2958
                    echo "get_only_latest_attempt_results: $get_only_latest_attempt_results <br />";
2959
                    echo "getOnlyBestAttempt: $getOnlyBestAttempt <br />";
2960
                }
2961
2962
                if ($get_only_latest_attempt_results || $getOnlyBestAttempt) {
2963
                    // Getting lp_items done by the user
2964
                    $sql = "SELECT DISTINCT lp_item_id
2965
                            FROM $lp_item_view_table
2966
                            WHERE
2967
                                lp_view_id = $lp_view_id
2968
                            ORDER BY lp_item_id";
2969
                    $res_lp_item = Database::query($sql);
2970
2971
                    while ($row_lp_item = Database::fetch_assoc($res_lp_item)) {
2972
                        $my_lp_item_id = $row_lp_item['lp_item_id'];
2973
                        $order = ' view_count DESC';
2974
                        if ($getOnlyBestAttempt) {
2975
                            $order = ' lp_iv.score DESC';
2976
                        }
2977
2978
                        // Getting the most recent attempt
2979
                        $sql = "SELECT
2980
                                    lp_iv.iid as lp_item_view_id,
2981
                                    lp_iv.score as score,
2982
                                    lp_i.max_score,
2983
                                    lp_iv.max_score as max_score_item_view,
2984
                                    lp_i.path,
2985
                                    lp_i.item_type,
2986
                                    lp_i.iid
2987
                                FROM $lp_item_view_table as lp_iv
2988
                                INNER JOIN $lp_item_table as lp_i
2989
                                ON (
2990
                                    lp_i.iid = lp_iv.lp_item_id
2991
                                )
2992
                                WHERE
2993
                                    lp_item_id = $my_lp_item_id AND
2994
                                    lp_view_id = $lp_view_id AND
2995
                                    (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."')
2996
                                ORDER BY $order
2997
                                LIMIT 1";
2998
2999
                        $res_lp_item_result = Database::query($sql);
3000
                        while ($row_max_score = Database::fetch_assoc($res_lp_item_result)) {
3001
                            $list[] = $row_max_score;
3002
                        }
3003
                    }
3004
                } else {
3005
                    // For the currently analysed view, get the score and
3006
                    // max_score of each item if it is a sco or a TOOL_QUIZ
3007
                    $sql = "SELECT
3008
                                lp_iv.iid as lp_item_view_id,
3009
                                lp_iv.score as score,
3010
                                lp_i.max_score,
3011
                                lp_iv.max_score as max_score_item_view,
3012
                                lp_i.path,
3013
                                lp_i.item_type,
3014
                                lp_i.iid
3015
                              FROM $lp_item_view_table as lp_iv
3016
                              INNER JOIN $lp_item_table as lp_i
3017
                              ON lp_i.iid = lp_iv.lp_item_id
3018
                              WHERE
3019
                                lp_view_id = $lp_view_id AND
3020
                                (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."')
3021
                            ";
3022
                    $res_max_score = Database::query($sql);
3023
                    while ($row_max_score = Database::fetch_assoc($res_max_score)) {
3024
                        $list[] = $row_max_score;
3025
                    }
3026
                }
3027
3028
                // Go through each scorable element of this view
3029
                $score_of_scorm_calculate = 0;
3030
                foreach ($list as $row_max_score) {
3031
                    // Came from the original lp_item
3032
                    $max_score = $row_max_score['max_score'];
3033
                    // Came from the lp_item_view
3034
                    $max_score_item_view = $row_max_score['max_score_item_view'];
3035
                    $score = $row_max_score['score'];
3036
                    if ($debug) {
3037
                        echo '<h3>Item Type: '.$row_max_score['item_type'].'</h3>';
3038
                    }
3039
3040
                    if ('sco' === $row_max_score['item_type']) {
3041
                        /* Check if it is sco (easier to get max_score)
3042
                           when there's no max score, we assume 100 as the max score,
3043
                           as the SCORM 1.2 says that the value should always be between 0 and 100.
3044
                        */
3045
                        if (0 == $max_score || is_null($max_score) || '' == $max_score) {
3046
                            // Chamilo style
3047
                            if ($use_max_score[$lp_id]) {
3048
                                $max_score = 100;
3049
                            } else {
3050
                                // Overwrites max score = 100 to use the one that came in the lp_item_view see BT#1613
3051
                                $max_score = $max_score_item_view;
3052
                            }
3053
                        }
3054
                        // Avoid division by zero errors
3055
                        if (!empty($max_score)) {
3056
                            $lpPartialTotal += $score / $max_score;
3057
                        }
3058
                        if ($debug) {
3059
                            var_dump("lpPartialTotal: $lpPartialTotal");
0 ignored issues
show
Security Debugging Code introduced by
var_dump('lpPartialTotal: '.$lpPartialTotal) looks like debug code. Are you sure you do not want to remove it?
Loading history...
3060
                            var_dump("score: $score");
3061
                            var_dump("max_score: $max_score");
3062
                        }
3063
                    } else {
3064
                        // Case of a TOOL_QUIZ element
3065
                        $item_id = $row_max_score['iid'];
3066
                        $item_path = $row_max_score['path'];
3067
                        $lp_item_view_id = (int) $row_max_score['lp_item_view_id'];
3068
3069
                        if (empty($lp_item_view_id)) {
3070
                            $lpItemCondition = ' (orig_lp_item_view_id = 0 OR orig_lp_item_view_id IS NULL) ';
3071
                        } else {
3072
                            $lpItemCondition = " orig_lp_item_view_id = $lp_item_view_id ";
3073
                        }
3074
3075
                        // Get last attempt to this exercise through
3076
                        // the current lp for the current user
3077
                        $order = 'exe_date DESC';
3078
                        if ($getOnlyBestAttempt) {
3079
                            $order = 'score DESC';
3080
                        }
3081
                        $sql = "SELECT exe_id, score
3082
                                FROM $tbl_stats_exercices
3083
                                WHERE
3084
                                    exe_exo_id = '$item_path' AND
3085
                                    exe_user_id = $user_id AND
3086
                                    orig_lp_item_id = $item_id AND
3087
                                    $lpItemCondition AND
3088
                                    c_id = $courseId AND
3089
                                    status = ''
3090
                                    $sessionCondition
3091
                                ORDER BY $order
3092
                                LIMIT 1";
3093
3094
                        $result_last_attempt = Database::query($sql);
3095
                        $num = Database::num_rows($result_last_attempt);
3096
                        if ($num > 0) {
3097
                            $attemptResult = Database::fetch_assoc($result_last_attempt);
3098
                            $id_last_attempt = $attemptResult['exe_id'];
3099
                            // We overwrite the score with the best one not the one saved in the LP (latest)
3100
                            if ($getOnlyBestAttempt && false == $get_only_latest_attempt_results) {
3101
                                if ($debug) {
3102
                                    echo "Following score comes from the track_exercise table not in the LP because the score is the best<br />";
3103
                                }
3104
                                $score = $attemptResult['score'];
3105
                            }
3106
3107
                            if ($debug) {
3108
                                echo "Attempt id: $id_last_attempt with score $score<br />";
3109
                            }
3110
                            // Within the last attempt number tracking, get the sum of
3111
                            // the max_scores of all questions that it was
3112
                            // made of (we need to make this call dynamic because of random questions selection)
3113
                            $sql = "SELECT SUM(t.ponderation) as maxscore FROM
3114
                                        (
3115
                                            SELECT DISTINCT
3116
                                                question_id,
3117
                                                marks,
3118
                                                ponderation
3119
                                            FROM $tbl_stats_attempts AS at
3120
                                            INNER JOIN $tbl_quiz_questions AS q
3121
                                            ON (q.iid = at.question_id)
3122
                                            WHERE
3123
                                                exe_id ='$id_last_attempt'
3124
                                        )
3125
                                        AS t";
3126
3127
                            $res_max_score_bis = Database::query($sql);
3128
                            $row_max_score_bis = Database::fetch_array($res_max_score_bis);
3129
3130
                            if (!empty($row_max_score_bis['maxscore'])) {
3131
                                $max_score = $row_max_score_bis['maxscore'];
3132
                            }
3133
                            if (!empty($max_score) && floatval($max_score) > 0) {
3134
                                $lpPartialTotal += $score / $max_score;
3135
                            }
3136
                            if ($debug) {
3137
                                var_dump("score: $score");
3138
                                var_dump("max_score: $max_score");
3139
                                var_dump("lpPartialTotal: $lpPartialTotal");
3140
                            }
3141
                        }
3142
                    }
3143
3144
                    if (in_array($row_max_score['item_type'], ['quiz', 'sco'])) {
3145
                        // Normal way
3146
                        if ($use_max_score[$lp_id]) {
3147
                            $count_items++;
3148
                        } else {
3149
                            if ('' != $max_score) {
3150
                                $count_items++;
3151
                            }
3152
                        }
3153
                        if ($debug) {
3154
                            echo '$count_items: '.$count_items;
3155
                        }
3156
                    }
3157
                }
3158
3159
                $score_of_scorm_calculate += $count_items ? (($lpPartialTotal / $count_items) * 100) : 0;
3160
                $global_result += $score_of_scorm_calculate;
3161
3162
                if ($debug) {
3163
                    var_dump("count_items: $count_items");
3164
                    var_dump("score_of_scorm_calculate: $score_of_scorm_calculate");
3165
                    var_dump("global_result: $global_result");
3166
                }
3167
            }
3168
        }
3169
3170
        $lp_with_quiz = 0;
3171
        foreach ($lp_list as $lp_id) {
3172
            // Check if LP have a score we assume that all SCO have an score
3173
            $sql = "SELECT count(iid) as count
3174
                    FROM $lp_item_table
3175
                    WHERE
3176
                        (item_type = 'quiz' OR item_type = 'sco') AND
3177
                        lp_id = ".$lp_id;
3178
            $result_have_quiz = Database::query($sql);
3179
            if (Database::num_rows($result_have_quiz) > 0) {
3180
                $row = Database::fetch_assoc($result_have_quiz);
3181
                if (is_numeric($row['count']) && 0 != $row['count']) {
3182
                    $lp_with_quiz++;
3183
                }
3184
            }
3185
        }
3186
3187
        if ($debug) {
3188
            echo '<h3>$lp_with_quiz '.$lp_with_quiz.' </h3>';
3189
            echo '<h3>Final return</h3>';
3190
        }
3191
3192
        if (0 != $lp_with_quiz) {
3193
            if (!$return_array) {
3194
                $score_of_scorm_calculate = round(($global_result / $lp_with_quiz), 2);
3195
                if ($debug) {
3196
                    var_dump($score_of_scorm_calculate);
3197
                }
3198
                if (empty($lp_ids)) {
3199
                    if ($debug) {
3200
                        echo '<h2>All lps fix: '.$score_of_scorm_calculate.'</h2>';
3201
                    }
3202
                }
3203
3204
                return $score_of_scorm_calculate;
3205
            }
3206
3207
            if ($debug) {
3208
                var_dump($global_result, $lp_with_quiz);
3209
            }
3210
3211
            return [$global_result, $lp_with_quiz];
3212
        }
3213
3214
        return '-';
3215
    }
3216
3217
    /**
3218
     * This function gets:
3219
     * 1. The score average from all SCORM Test items in all LP in a course-> All the answers / All the max scores.
3220
     * 2. The score average from all Tests (quiz) in all LP in a course-> All the answers / All the max scores.
3221
     * 3. And finally it will return the average between 1. and 2.
3222
     * This function does not take the results of a Test out of a LP.
3223
     *
3224
     * @param int|array $student_id  Array of user ids or an user id
3225
     * @param string    $course_code Course code
3226
     * @param array     $lp_ids      List of LP ids
3227
     * @param int       $sessionId   Session id (optional), if param $sessionId is 0(default)
3228
     *                               it'll return results including sessions, 0 = session is not filtered
3229
     *
3230
     * @return string value (number %) Which represents a round integer explain in got in 3
3231
     */
3232
    public static function getAverageStudentScore(
3233
        $student_id,
3234
        $course_code = '',
3235
        $lp_ids = [],
3236
        $sessionId = 0
3237
    ) {
3238
        if (empty($student_id)) {
3239
            return 0;
3240
        }
3241
3242
        $conditions = [];
3243
        if (!empty($course_code)) {
3244
            $course = api_get_course_info($course_code);
3245
            $courseId = $course['real_id'];
3246
            //$conditions[] = " lp.c_id = $courseId";
3247
        }
3248
3249
        // Get course tables names
3250
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3251
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
3252
        $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
3253
        $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3254
3255
        // Compose a filter based on optional learning paths list given
3256
        if (!empty($lp_ids) && count($lp_ids) > 0) {
3257
            $conditions[] = ' lp.iid IN ('.implode(',', $lp_ids).') ';
3258
        }
3259
3260
        // Compose a filter based on optional session id
3261
        $sessionId = (int) $sessionId;
3262
        if (!empty($sessionId)) {
3263
            $conditions[] = " lp_view.session_id = $sessionId ";
3264
        }
3265
3266
        if (is_array($student_id)) {
3267
            array_walk($student_id, 'intval');
3268
            $conditions[] = " lp_view.user_id IN (".implode(',', $student_id).") ";
3269
        } else {
3270
            $student_id = (int) $student_id;
3271
            $conditions[] = " lp_view.user_id = $student_id ";
3272
        }
3273
3274
        $conditionsToString = implode(' AND ', $conditions);
3275
        $sql = "SELECT
3276
                    SUM(lp_iv.score) sum_score,
3277
                    SUM(lp_i.max_score) sum_max_score
3278
                FROM $lp_table as lp
3279
                INNER JOIN $lp_item_table as lp_i
3280
                ON lp.iid = lp_i.lp_id
3281
                INNER JOIN $lp_view_table as lp_view
3282
                ON lp_view.lp_id = lp_i.lp_id
3283
                INNER JOIN $lp_item_view_table as lp_iv
3284
                ON
3285
                    lp_i.iid = lp_iv.lp_item_id AND
3286
                    lp_iv.lp_view_id = lp_view.iid
3287
                WHERE (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."') AND
3288
                $conditionsToString
3289
        ";
3290
        $result = Database::query($sql);
3291
        $row = Database::fetch_assoc($result);
3292
3293
        if (empty($row['sum_max_score'])) {
3294
            return 0;
3295
        }
3296
3297
        return ($row['sum_score'] / $row['sum_max_score']) * 100;
3298
    }
3299
3300
    /**
3301
     * This function gets time spent in learning path for a student inside a course.
3302
     *
3303
     * @param int|array $student_id Student id(s)
3304
     * @param Course    $course     Course code
3305
     * @param array     $lp_ids     Limit average to listed lp ids
3306
     * @param int       $sessionId  Session id (optional), if param $sessionId is null(default)
3307
     *                              it'll return results including sessions, 0 = session is not filtered
3308
     *
3309
     * @return int Total time in seconds
3310
     */
3311
    public static function get_time_spent_in_lp(
3312
        $student_id,
3313
        Course $course,
3314
        $lp_ids = [],
3315
        $sessionId = 0
3316
    ) {
3317
        $student_id = (int) $student_id;
3318
        $sessionId = (int) $sessionId;
3319
        $total_time = 0;
3320
3321
        if (!empty($course)) {
3322
            $lpTable = Database::get_course_table(TABLE_LP_MAIN);
3323
            $lpItemTable = Database::get_course_table(TABLE_LP_ITEM);
3324
            $lpViewTable = Database::get_course_table(TABLE_LP_VIEW);
3325
            $lpItemViewTable = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3326
            $trackExercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3327
            $courseId = $course->getId();
3328
3329
            // Compose a filter based on optional learning paths list given
3330
            $condition_lp = '';
3331
            if (count($lp_ids) > 0) {
3332
                $condition_lp = " AND iid IN(".implode(',', $lp_ids).") ";
3333
            }
3334
3335
            // Check the real number of LPs corresponding to the filter in the
3336
            // database (and if no list was given, get them all)
3337
            $sql = "SELECT DISTINCT(iid) FROM $lpTable
3338
                    WHERE 1=1 $condition_lp";
3339
            $result = Database::query($sql);
3340
            $session_condition = api_get_session_condition($sessionId);
3341
3342
            // calculates time
3343
            if (Database::num_rows($result) > 0) {
3344
                while ($row = Database::fetch_array($result)) {
3345
                    $lp_id = (int) $row['iid'];
3346
                    $lp = Container::getLpRepository()->find($lp_id);
3347
                    // Start Exercise in LP total_time
3348
                    // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
3349
                    $list = learnpath::get_flat_ordered_items_list($lp, 0, $courseId);
3350
                    foreach ($list as $itemId) {
3351
                        $sql = "SELECT max(view_count)
3352
                                FROM $lpViewTable
3353
                                WHERE
3354
                                    c_id = $courseId AND
3355
                                    lp_id = $lp_id AND
3356
                                    user_id = $student_id
3357
                                    $session_condition";
3358
                        $res = Database::query($sql);
3359
                        $view = '';
3360
                        if (Database::num_rows($res) > 0) {
3361
                            $myrow = Database::fetch_array($res);
3362
                            $view = $myrow[0];
3363
                        }
3364
                        $viewCondition = null;
3365
                        if (!empty($view)) {
3366
                            $viewCondition = " AND v.view_count = $view  ";
3367
                        }
3368
                        $sql = "SELECT
3369
                            iv.iid,
3370
                            iv.total_time as mytime,
3371
                            i.iid as myid,
3372
                            iv.view_count as iv_view_count,
3373
                            path
3374
                        FROM $lpItemTable as i
3375
                        INNER JOIN $lpItemViewTable as iv
3376
                        ON (i.iid = iv.lp_item_id)
3377
                        INNER JOIN $lpViewTable as v
3378
                        ON (iv.lp_view_id = v.iid)
3379
                        WHERE
3380
                            v.c_id = $courseId AND
3381
                            i.iid = $itemId AND
3382
                            i.lp_id = $lp_id  AND
3383
                            v.user_id = $student_id AND
3384
                            item_type = 'quiz' AND
3385
                            path <> '' AND
3386
                            v.session_id = $sessionId
3387
                            $viewCondition
3388
                        ORDER BY iv.view_count DESC ";
3389
3390
                        $resultRow = Database::query($sql);
3391
                        if (Database::num_rows($resultRow)) {
3392
                            $row = Database::fetch_array($resultRow);
3393
                            $totalTimeInLpItemView = $row['mytime'];
3394
                            $lpItemViewId = $row['iid'];
3395
                            $sessionCondition = api_get_session_condition($sessionId);
3396
                            $sql = 'SELECT SUM(exe_duration) exe_duration
3397
                                    FROM '.$trackExercises.'
3398
                                    WHERE
3399
                                        exe_exo_id="'.$row['path'].'" AND
3400
                                        exe_user_id="'.$student_id.'" AND
3401
                                        orig_lp_id = "'.$lp_id.'" AND
3402
                                        orig_lp_item_id = "'.$row['myid'].'" AND
3403
                                        c_id = '.$courseId.' AND
3404
                                        status <> "incomplete"
3405
                                        '.$sessionCondition.'
3406
                                     ORDER BY exe_date DESC ';
3407
3408
                            $sumScoreResult = Database::query($sql);
3409
                            $durationRow = Database::fetch_assoc($sumScoreResult);
3410
                            if (!empty($durationRow['exe_duration'])) {
3411
                                $exeDuration = $durationRow['exe_duration'];
3412
                                if ($exeDuration != $totalTimeInLpItemView &&
3413
                                    !empty($lpItemViewId) &&
3414
                                    !empty($exeDuration)
3415
                                ) {
3416
                                    // Update c_lp_item_view.total_time
3417
                                    $sqlUpdate = "UPDATE $lpItemViewTable
3418
                                                  SET total_time = '$exeDuration'
3419
                                                  WHERE iid = ".$lpItemViewId;
3420
                                    Database::query($sqlUpdate);
3421
                                }
3422
                            }
3423
                        }
3424
                    }
3425
3426
                    // End total_time fix
3427
3428
                    // Calculate total time
3429
                    $sql = "SELECT SUM(total_time)
3430
                            FROM $lpItemViewTable AS item_view
3431
                            INNER JOIN $lpViewTable AS view
3432
                            ON (
3433
                                item_view.lp_view_id = view.iid
3434
                            )
3435
                            WHERE
3436
                                view.c_id = $courseId AND
3437
                                view.lp_id = $lp_id AND
3438
                                view.user_id = $student_id AND
3439
                                session_id = $sessionId";
3440
3441
                    $rs = Database::query($sql);
3442
                    if (Database::num_rows($rs) > 0) {
3443
                        $total_time += Database::result($rs, 0, 0);
3444
                    }
3445
                }
3446
            }
3447
        }
3448
3449
        return $total_time;
3450
    }
3451
3452
    /**
3453
     * This function gets last connection time to one learning path.
3454
     *
3455
     * @param int|array $student_id  Student id(s)
3456
     * @param string    $course_code Course code
3457
     * @param int       $lp_id       Learning path id
3458
     * @param int       $sessionId
3459
     *
3460
     * @return int last connection timestamp
3461
     */
3462
    public static function get_last_connection_time_in_lp(
3463
        $student_id,
3464
        $course_code,
3465
        $lp_id,
3466
        $sessionId = 0
3467
    ) {
3468
        $course = api_get_course_info($course_code);
3469
        if (empty($course)) {
3470
            return 0;
3471
        }
3472
3473
        $courseId = $course['real_id'];
3474
        $student_id = (int) $student_id;
3475
        $lp_id = (int) $lp_id;
3476
        $sessionId = (int) $sessionId;
3477
        $lastTime = 0;
3478
3479
        if (self::minimumTimeAvailable($sessionId, $courseId)) {
3480
            $sql = "SELECT MAX(date_reg) max
3481
                    FROM track_e_access_complete
3482
                    WHERE
3483
                        user_id = $student_id AND
3484
                        c_id = $courseId AND
3485
                        session_id = $sessionId AND
3486
                        tool = 'learnpath' AND
3487
                        tool_id = $lp_id AND
3488
                        action = 'view' AND
3489
                        login_as = 0
3490
                    ORDER BY date_reg ASC
3491
                    LIMIT 1";
3492
            $rs = Database::query($sql);
3493
3494
            $lastConnection = 0;
3495
            if (Database::num_rows($rs) > 0) {
3496
                $value = Database::fetch_array($rs);
3497
                if (isset($value['max']) && !empty($value['max'])) {
3498
                    $lastConnection = api_strtotime($value['max'], 'UTC');
3499
                }
3500
            }
3501
3502
            if (!empty($lastConnection)) {
3503
                return $lastConnection;
3504
            }
3505
        }
3506
        if (!empty($course)) {
3507
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3508
            $t_lpv = Database::get_course_table(TABLE_LP_VIEW);
3509
            $t_lpiv = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3510
3511
            // Check the real number of LPs corresponding to the filter in the
3512
            // database (and if no list was given, get them all)
3513
            $sql = "SELECT iid FROM $lp_table
3514
                    WHERE iid = $lp_id ";
3515
            $row = Database::query($sql);
3516
            $count = Database::num_rows($row);
3517
3518
            // calculates last connection time
3519
            if ($count > 0) {
3520
                $sql = 'SELECT MAX(start_time)
3521
                        FROM '.$t_lpiv.' AS item_view
3522
                        INNER JOIN '.$t_lpv.' AS view
3523
                        ON (item_view.lp_view_id = view.iid)
3524
                        WHERE
3525
                            status != "not attempted" AND
3526
                            view.c_id = '.$courseId.' AND
3527
                            view.lp_id = '.$lp_id.' AND
3528
                            view.user_id = '.$student_id.' AND
3529
                            view.session_id = '.$sessionId;
3530
                $rs = Database::query($sql);
3531
                if (Database::num_rows($rs) > 0) {
3532
                    $lastTime = Database::result($rs, 0, 0);
3533
                }
3534
            }
3535
        }
3536
3537
        return $lastTime;
3538
    }
3539
3540
    public static function getFirstConnectionTimeInLp(
3541
        $student_id,
3542
        $course_code,
3543
        $lp_id,
3544
        $sessionId = 0
3545
    ) {
3546
        $course = api_get_course_info($course_code);
3547
        $student_id = (int) $student_id;
3548
        $lp_id = (int) $lp_id;
3549
        $sessionId = (int) $sessionId;
3550
        $time = 0;
3551
3552
        if (!empty($course)) {
3553
            $courseId = $course['real_id'];
3554
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3555
            $t_lpv = Database::get_course_table(TABLE_LP_VIEW);
3556
            $t_lpiv = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3557
3558
            // Check the real number of LPs corresponding to the filter in the
3559
            // database (and if no list was given, get them all)
3560
            $sql = "SELECT iid FROM $lp_table
3561
                    WHERE iid = $lp_id ";
3562
            $row = Database::query($sql);
3563
            $count = Database::num_rows($row);
3564
3565
            // calculates first connection time
3566
            if ($count > 0) {
3567
                $sql = 'SELECT MIN(start_time)
3568
                        FROM '.$t_lpiv.' AS item_view
3569
                        INNER JOIN '.$t_lpv.' AS view
3570
                        ON (item_view.lp_view_id = view.iid)
3571
                        WHERE
3572
                            status != "not attempted" AND
3573
                            view.c_id = '.$courseId.' AND
3574
                            view.lp_id = '.$lp_id.' AND
3575
                            view.user_id = '.$student_id.' AND
3576
                            view.session_id = '.$sessionId;
3577
                $rs = Database::query($sql);
3578
                if (Database::num_rows($rs) > 0) {
3579
                    $time = Database::result($rs, 0, 0);
3580
                }
3581
            }
3582
        }
3583
3584
        return $time;
3585
    }
3586
3587
    /**
3588
     * gets the list of students followed by coach.
3589
     *
3590
     * @param int $coach_id Coach id
3591
     *
3592
     * @return array List of students
3593
     */
3594
    public static function get_student_followed_by_coach($coach_id)
3595
    {
3596
        $coach_id = (int) $coach_id;
3597
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3598
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3599
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3600
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3601
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3602
3603
        $accessUrlEnabled = AccessUrlHelper::isMultiple();
3604
        $access_url_id = $accessUrlEnabled ? api_get_current_access_url_id() : -1;
3605
3606
        $students = [];
3607
        // At first, courses where $coach_id is coach of the course //
3608
        $sql = 'SELECT session_id, c_id
3609
                FROM '.$tbl_session_course_user.'
3610
                WHERE user_id='.$coach_id.' AND status = '.SessionEntity::COURSE_COACH;
3611
3612
        if (-1 != $access_url_id) {
3613
            $sql = 'SELECT scu.session_id, scu.c_id
3614
                    FROM '.$tbl_session_course_user.' scu
3615
                    INNER JOIN '.$tbl_session_rel_access_url.'  sru
3616
                    ON (scu.session_id=sru.session_id)
3617
                    WHERE
3618
                        scu.user_id='.$coach_id.' AND
3619
                        scu.status = '.SessionEntity::COURSE_COACH.' AND
3620
                        sru.access_url_id = '.$access_url_id;
3621
        }
3622
3623
        $result = Database::query($sql);
3624
3625
        while ($a_courses = Database::fetch_array($result)) {
3626
            $courseId = $a_courses['c_id'];
3627
            $sessionId = $a_courses['session_id'];
3628
3629
            $sql = "SELECT DISTINCT srcru.user_id
3630
                    FROM $tbl_session_course_user AS srcru
3631
                    INNER JOIN $tbl_session_user sru
3632
                    ON (srcru.user_id = sru.user_id AND srcru.session_id = sru.session_id)
3633
                    WHERE
3634
                        sru.relation_type = ".SessionEntity::STUDENT." AND
3635
                        srcru.c_id = '$courseId' AND
3636
                        srcru.session_id = '$sessionId'";
3637
3638
            $rs = Database::query($sql);
3639
            while ($row = Database::fetch_array($rs)) {
3640
                $students[$row['user_id']] = $row['user_id'];
3641
            }
3642
        }
3643
3644
        // Then, courses where $coach_id is coach of the session
3645
        $sql = "SELECT srcru.user_id
3646
            FROM $tbl_session_course_user srcru
3647
            INNER JOIN $tbl_session_course src
3648
            ON (srcru.c_id = src.c_id AND srcru.session_id = src.session_id)
3649
            INNER JOIN $tbl_session s
3650
            ON srcru.session_id = s.id AND src.session_id = s.id
3651
            INNER JOIN $tbl_session_user sru on s.id = sru.session_id
3652
            WHERE
3653
               srcru.status = ".SessionEntity::STUDENT." AND
3654
               sru.relation_type = ".SessionEntity::GENERAL_COACH." AND
3655
               sru.user_id = $coach_id";
3656
3657
        if (-1 != $access_url_id) {
3658
            $sql = "SELECT srcru.user_id
3659
                    FROM $tbl_session_course_user srcru
3660
                    INNER JOIN $tbl_session_course src
3661
                    ON (srcru.c_id = src.c_id AND srcru.session_id = src.session_id)
3662
                    INNER JOIN $tbl_session s
3663
                    ON srcru.session_id = s.id AND src.session_id = s.id
3664
                    INNER JOIN $tbl_session_user sru
3665
                    ON s.id = sru.session_id
3666
                    INNER JOIN $tbl_session_rel_access_url aurs
3667
                    ON s.id = aurs.session_id
3668
                    WHERE
3669
                        srcru.status = ".SessionEntity::STUDENT." AND
3670
                        sru.relation_type = ".SessionEntity::GENERAL_COACH." AND
3671
                        sru.user_id = $coach_id AND
3672
                        aurs.access_url_id = $access_url_id";
3673
        }
3674
3675
        $result = Database::query($sql);
3676
        while ($row = Database::fetch_array($result)) {
3677
            $students[$row['user_id']] = $row['user_id'];
3678
        }
3679
3680
        return $students;
3681
    }
3682
3683
    /**
3684
     * Check if a coach is allowed to follow a student.
3685
     *
3686
     * @param    int        Coach id
3687
     * @param    int        Student id
3688
     *
3689
     * @return bool
3690
     */
3691
    public static function is_allowed_to_coach_student($coach_id, $student_id)
3692
    {
3693
        $coach_id = intval($coach_id);
3694
        $student_id = intval($student_id);
3695
3696
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3697
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3698
        $tblSessionRelUser = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3699
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3700
3701
        // At first, courses where $coach_id is coach of the course
3702
        $sql = 'SELECT 1 FROM '.$tbl_session_course_user.'
3703
                WHERE user_id='.$coach_id.' AND status = '.SessionEntity::COURSE_COACH;
3704
        $result = Database::query($sql);
3705
        if (Database::num_rows($result) > 0) {
3706
            return true;
3707
        }
3708
3709
        // Then, courses where $coach_id is coach of the session
3710
        $sql = "SELECT srcru.user_id
3711
            FROM $tbl_session_course_user srcru
3712
            INNER JOIN $tbl_session_course src
3713
            ON (srcru.c_id = src.c_id AND srcru.session_id = src.session_id)
3714
            INNER JOIN $tbl_session s
3715
            ON srcru.session_id = s.id AND src.session_id = s.id
3716
            INNER JOIN $tblSessionRelUser sru
3717
            ON s.id = sru.session_id
3718
            WHERE
3719
                (srcru.status = ".SessionEntity::STUDENT." AND srcru.user_id = $student_id) AND
3720
                (sru.relation_type = ".SessionEntity::GENERAL_COACH." AND sru.user_id = $coach_id)";
3721
        $result = Database::query($sql);
3722
        if (Database::num_rows($result) > 0) {
3723
            return true;
3724
        }
3725
3726
        return false;
3727
    }
3728
3729
    /**
3730
     * Get courses followed by coach.
3731
     *
3732
     * @param     int        Coach id
3733
     * @param    int        Session id (optional)
3734
     *
3735
     * @return array Courses list
3736
     */
3737
    public static function get_courses_followed_by_coach($coach_id, $sessionId = 0)
3738
    {
3739
        $coach_id = intval($coach_id);
3740
        if (!empty($sessionId)) {
3741
            $sessionId = intval($sessionId);
3742
        }
3743
3744
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3745
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
3746
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3747
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3748
        $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3749
        $tblSessionRelUser = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3750
3751
        // At first, courses where $coach_id is coach of the course.
3752
        $sql = 'SELECT DISTINCT c.code
3753
                FROM '.$tbl_session_course_user.' sc
3754
                INNER JOIN '.$tbl_course.' c
3755
                ON (c.id = sc.c_id)
3756
                WHERE sc.user_id = '.$coach_id.' AND sc.status = '.SessionEntity::COURSE_COACH;
3757
3758
        if (AccessUrlHelper::isMultiple()) {
3759
            $access_url_id = api_get_current_access_url_id();
3760
            if (-1 != $access_url_id) {
3761
                $sql = 'SELECT DISTINCT c.code
3762
                        FROM '.$tbl_session_course_user.' scu
3763
                        INNER JOIN '.$tbl_course.' c
3764
                        ON (c.code = scu.c_id)
3765
                        INNER JOIN '.$tbl_course_rel_access_url.' cru
3766
                        ON (c.id = cru.c_id)
3767
                        WHERE
3768
                            scu.user_id='.$coach_id.' AND
3769
                            scu.status = '.SessionEntity::COURSE_COACH.' AND
3770
                            cru.access_url_id = '.$access_url_id;
3771
            }
3772
        }
3773
3774
        if (!empty($sessionId)) {
3775
            $sql .= ' AND session_id='.$sessionId;
3776
        }
3777
3778
        $courseList = [];
3779
        $result = Database::query($sql);
3780
        while ($row = Database::fetch_array($result)) {
3781
            $courseList[$row['code']] = $row['code'];
3782
        }
3783
3784
        // Then, courses where $coach_id is coach of the session
3785
        $sql = "SELECT DISTINCT course.code
3786
                FROM $tbl_session_course as session_course
3787
                INNER JOIN $tbl_session as session
3788
                    ON (session.id = session_course.session_id)
3789
                INNER JOIN $tblSessionRelUser session_user
3790
                    ON (session.id = session_user.session_id
3791
                    AND session_user.user_id = $coach_id
3792
                    AND session_user.relation_type = ".SessionEntity::GENERAL_COACH.")
3793
                INNER JOIN $tbl_course as course
3794
                    ON course.id = session_course.c_id";
3795
3796
        if (AccessUrlHelper::isMultiple()) {
3797
            $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
3798
            $access_url_id = api_get_current_access_url_id();
3799
            if (-1 != $access_url_id) {
3800
                $sql = "SELECT DISTINCT c.code
3801
                    FROM $tbl_session_course as session_course
3802
                    INNER JOIN $tbl_course c
3803
                    ON (c.id = session_course.c_id)
3804
                    INNER JOIN $tbl_session as session
3805
                    ON session.id = session_course.session_id
3806
                    INNER JOIN $tblSessionRelUser session_user
3807
                        ON (session.id = session_user.session_id
3808
                        AND session_user.user_id = $coach_id
3809
                        AND session_user.relation_type = ".SessionEntity::GENERAL_COACH.")
3810
                    INNER JOIN $tbl_course as course
3811
                        ON course.id = session_course.c_id
3812
                     INNER JOIN $tbl_course_rel_access_url course_rel_url
3813
                    ON (course_rel_url.c_id = c.id)";
3814
            }
3815
        }
3816
3817
        if (!empty($sessionId)) {
3818
            $sql .= ' WHERE session_course.session_id='.$sessionId;
3819
            if (AccessUrlHelper::isMultiple()) {
3820
                $sql .= ' AND access_url_id = '.$access_url_id;
3821
            }
3822
        } else {
3823
            if (AccessUrlHelper::isMultiple()) {
3824
                $sql .= ' WHERE access_url_id = '.$access_url_id;
3825
            }
3826
        }
3827
3828
        $result = Database::query($sql);
3829
        while ($row = Database::fetch_array($result)) {
3830
            $courseList[$row['code']] = $row['code'];
3831
        }
3832
3833
        return $courseList;
3834
    }
3835
3836
    /**
3837
     * Get sessions coached by user.
3838
     *
3839
     * @param int    $coach_id
3840
     * @param int    $start
3841
     * @param int    $limit
3842
     * @param bool   $getCount
3843
     * @param string $keyword
3844
     * @param string $description
3845
     * @param string $orderByName
3846
     * @param string $orderByDirection
3847
     * @param array  $options
3848
     *
3849
     * @return mixed
3850
     */
3851
    public static function get_sessions_coached_by_user(
3852
        $coach_id,
3853
        $start = 0,
3854
        $limit = 0,
3855
        $getCount = false,
3856
        $keyword = '',
3857
        $description = '',
3858
        $orderByName = '',
3859
        $orderByDirection = '',
3860
        $options = []
3861
    ) {
3862
        // table definition
3863
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
3864
        $tblSessionRelUser = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3865
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3866
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
3867
3868
        $coach_id = (int) $coach_id;
3869
3870
        $select = ' SELECT * FROM ';
3871
        if ($getCount) {
3872
            $select = ' SELECT count(DISTINCT id) as count FROM ';
3873
        }
3874
3875
        $limitCondition = null;
3876
        if (!empty($start) && !empty($limit)) {
3877
            $limitCondition = " LIMIT ".intval($start).", ".intval($limit);
3878
        }
3879
3880
        $keywordCondition = null;
3881
        if (!empty($keyword)) {
3882
            $keyword = Database::escape_string($keyword);
3883
            $keywordCondition = " AND (title LIKE '%$keyword%' ) ";
3884
3885
            if (!empty($description)) {
3886
                $description = Database::escape_string($description);
3887
                $keywordCondition = " AND (title LIKE '%$keyword%' OR description LIKE '%$description%' ) ";
3888
            }
3889
        }
3890
3891
        $extraFieldModel = new ExtraFieldModel('session');
3892
        $conditions = $extraFieldModel->parseConditions($options);
3893
        $sqlInjectJoins = $conditions['inject_joins'];
3894
        $extraFieldsConditions = $conditions['where'];
3895
        $sqlInjectWhere = $conditions['inject_where'];
3896
        $injectExtraFields = $conditions['inject_extra_fields'];
3897
3898
        $access_url_id = api_get_current_access_url_id();
3899
3900
        $orderBy = '';
3901
        if (!empty($orderByName)) {
3902
            if (in_array($orderByName, ['title', 'access_start_date'])) {
3903
                $orderByDirection = in_array(strtolower($orderByDirection), ['asc', 'desc']) ? $orderByDirection : 'asc';
3904
                $orderByName = Database::escape_string($orderByName);
3905
                $orderBy .= " ORDER BY `$orderByName` $orderByDirection";
3906
            }
3907
        }
3908
3909
        $sql = "
3910
            $select
3911
            (
3912
                SELECT DISTINCT
3913
                    s.id,
3914
                    title,
3915
                    $injectExtraFields
3916
                    access_start_date,
3917
                    access_end_date
3918
                FROM $tbl_session s
3919
                INNER JOIN $tbl_session_rel_access_url session_rel_url
3920
                ON (s.id = session_rel_url.session_id)
3921
                $sqlInjectJoins
3922
                INNER JOIN $tblSessionRelUser sru ON s.id = sru.session_id
3923
                WHERE
3924
                    (sru.user_id = $coach_id AND sru.relation_type = ".SessionEntity::GENERAL_COACH.") AND
3925
                    access_url_id = $access_url_id
3926
                    $keywordCondition
3927
                    $extraFieldsConditions
3928
                    $sqlInjectWhere
3929
            UNION
3930
                SELECT DISTINCT
3931
                    s.id,
3932
                    s.title,
3933
                    $injectExtraFields
3934
                    s.access_start_date,
3935
                    s.access_end_date
3936
                FROM $tbl_session as s
3937
                INNER JOIN $tbl_session_course_user as session_course_user
3938
                ON
3939
                    s.id = session_course_user.session_id AND
3940
                    session_course_user.user_id = $coach_id AND
3941
                    session_course_user.status = ".SessionEntity::COURSE_COACH."
3942
                INNER JOIN $tbl_session_rel_access_url session_rel_url
3943
                ON (s.id = session_rel_url.session_id)
3944
                $sqlInjectJoins
3945
                WHERE
3946
                    access_url_id = $access_url_id
3947
                    $keywordCondition
3948
                    $extraFieldsConditions
3949
                    $sqlInjectWhere
3950
            ) as sessions $limitCondition $orderBy
3951
            ";
3952
3953
        $rs = Database::query($sql);
3954
        if ($getCount) {
3955
            $row = Database::fetch_array($rs);
3956
3957
            return $row['count'];
3958
        }
3959
3960
        $sessions = [];
3961
        while ($row = Database::fetch_array($rs)) {
3962
            if ('0000-00-00 00:00:00' === $row['access_start_date']) {
3963
                $row['access_start_date'] = null;
3964
            }
3965
3966
            $sessions[$row['id']] = $row;
3967
        }
3968
3969
        if (!empty($sessions)) {
3970
            foreach ($sessions as &$session) {
3971
                if (empty($session['access_start_date'])) {
3972
                    $session['status'] = get_lang('active');
3973
                } else {
3974
                    $time_start = api_strtotime($session['access_start_date'], 'UTC');
3975
                    $time_end = api_strtotime($session['access_end_date'], 'UTC');
3976
                    if ($time_start < time() && time() < $time_end) {
3977
                        $session['status'] = get_lang('active');
3978
                    } else {
3979
                        if (time() < $time_start) {
3980
                            $session['status'] = get_lang('Not yet begun');
3981
                        } else {
3982
                            if (time() > $time_end) {
3983
                                $session['status'] = get_lang('Past');
3984
                            }
3985
                        }
3986
                    }
3987
                }
3988
            }
3989
        }
3990
3991
        return $sessions;
3992
    }
3993
3994
    /**
3995
     * Get courses list from a session.
3996
     *
3997
     * @param    int        Session id
3998
     *
3999
     * @return array Courses list
4000
     */
4001
    public static function get_courses_list_from_session($sessionId)
4002
    {
4003
        $sessionId = (int) $sessionId;
4004
4005
        // table definition
4006
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4007
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
4008
4009
        $sql = "SELECT DISTINCT code, c_id
4010
                FROM $tbl_session_course sc
4011
                INNER JOIN $courseTable c
4012
                ON sc.c_id = c.id
4013
                WHERE session_id= $sessionId";
4014
4015
        $result = Database::query($sql);
4016
4017
        $courses = [];
4018
        while ($row = Database::fetch_array($result)) {
4019
            $courses[$row['code']] = $row;
4020
        }
4021
4022
        return $courses;
4023
    }
4024
4025
    /**
4026
     * Count the number of documents that an user has uploaded to a course.
4027
     *
4028
     * @param    int|array   Student id(s)
4029
     * @param    string      Course code
4030
     * @param    int         Session id (optional),
4031
     * if param $sessionId is null(default)
4032
     * return count of assignments including sessions, 0 = session is not filtered
4033
     *
4034
     * @return int Number of documents
4035
     */
4036
    public static function count_student_uploaded_documents(
4037
        $student_id,
4038
        $course_code,
4039
        $sessionId = null
4040
    ) {
4041
        $a_course = api_get_course_info($course_code);
4042
        $repo = Container::getDocumentRepository();
4043
4044
        $user = api_get_user_entity($student_id);
4045
        $course = api_get_course_entity($a_course['real_id']);
4046
        $session = api_get_session_entity($sessionId);
4047
        //$group = api_get_group_entity(api_get_group_id());
4048
4049
        $qb = $repo->getResourcesByCourseLinkedToUser($user, $course, $session);
4050
4051
        $qb->select('count(resource)');
4052
        $count = $qb->getQuery()->getSingleScalarResult();
4053
4054
        return $count;
4055
    }
4056
4057
    /**
4058
     * This function counts the number of post by course.
4059
     *
4060
     * @param string $courseId
4061
     * @param int    $sessionId (optional), if is null(default) it'll return results including sessions,
4062
     *                          0 = session is not filtered
4063
     * @param int    $groupId
4064
     *
4065
     * @return int The number of post by course
4066
     */
4067
    public static function count_number_of_posts_by_course($courseId, $sessionId = null, $groupId = 0)
4068
    {
4069
        $repo = Container::getForumPostRepository();
4070
        $course = api_get_course_entity($courseId);
4071
        $session = api_get_session_entity($sessionId);
4072
        $group = api_get_group_entity($groupId);
4073
        $qb = $repo->getResourcesByCourse($course, $session, $group);
4074
4075
        $qb->select('count(resource)');
4076
        $count = $qb->getQuery()->getSingleScalarResult();
4077
4078
        return $count;
4079
    }
4080
4081
    /**
4082
     * This function counts the number of threads by course.
4083
     *
4084
     * @param int Course id
4085
     * @param int Session id (optional),
4086
     * if param $sessionId is null(default) it'll return results including
4087
     * sessions, 0 = session is not filtered
4088
     * @param int $groupId
4089
     *
4090
     * @return int The number of threads by course
4091
     */
4092
    public static function count_number_of_threads_by_course(
4093
        $courseId,
4094
        $sessionId = null,
4095
        $groupId = 0
4096
    ) {
4097
        $repo = Container::getForumThreadRepository();
4098
        $course = api_get_course_entity($courseId);
4099
        $session = api_get_session_entity($sessionId);
4100
        $group = api_get_group_entity($groupId);
4101
        $qb = $repo->getResourcesByCourse($course, $session, $group);
4102
4103
        $qb->select('count(resource)');
4104
        $count = $qb->getQuery()->getSingleScalarResult();
4105
4106
        return $count;
4107
    }
4108
4109
    /**
4110
     * This function counts the number of forums by course.
4111
     *
4112
     * @param int     Course id
4113
     * @param int     Session id (optional),
4114
     * if param $sessionId is null(default) it'll return results
4115
     * including sessions, 0 = session is not filtered
4116
     * @param int $groupId
4117
     *
4118
     * @return int The number of forums by course
4119
     */
4120
    public static function count_number_of_forums_by_course(
4121
        $courseId,
4122
        $sessionId = null,
4123
        $groupId = 0
4124
    ) {
4125
        $repo = Container::getForumRepository();
4126
        $course = api_get_course_entity($courseId);
4127
        $session = api_get_session_entity($sessionId);
4128
        $group = api_get_group_entity($groupId);
4129
4130
        $qb = $repo->getResourcesByCourse($course, $session, $group);
4131
        $qb->select('count(resource)');
4132
        $count = $qb->getQuery()->getSingleScalarResult();
4133
4134
        return $count;
4135
    }
4136
4137
    /**
4138
     * This function counts the chat last connections by course in x days.
4139
     *
4140
     * @param      string     Course code
4141
     * @param      int     Last x days
4142
     * @param    int        Session id (optional)
4143
     *
4144
     * @return int Chat last connections by course in x days
4145
     */
4146
    public static function chat_connections_during_last_x_days_by_course(
4147
        $course_code,
4148
        $last_days,
4149
        $session_id = 0
4150
    ) {
4151
        $course_info = api_get_course_info($course_code);
4152
        if (empty($course_info)) {
4153
            return null;
4154
        }
4155
        $courseId = $course_info['real_id'];
4156
4157
        // Protect data
4158
        $last_days = (int) $last_days;
4159
        $session_id = (int) $session_id;
4160
4161
        $tbl_stats_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
4162
        $now = api_get_utc_datetime();
4163
4164
        $sql = "SELECT count(*) FROM $tbl_stats_access
4165
                WHERE
4166
                    DATE_SUB('$now',INTERVAL $last_days DAY) <= access_date AND
4167
                    c_id = '$courseId' AND
4168
                    access_tool='".TOOL_CHAT."' AND
4169
                    session_id = '$session_id' ";
4170
        $result = Database::query($sql);
4171
        if (Database::num_rows($result)) {
4172
            $row = Database::fetch_row($result);
4173
            $count = $row[0];
4174
4175
            return $count;
4176
        }
4177
4178
        return 0;
4179
    }
4180
4181
    /**
4182
     * This function gets the last student's connection in chat.
4183
     *
4184
     * @param      int     Student id
4185
     * @param      string     Course code
4186
     * @param    int        Session id (optional)
4187
     *
4188
     * @return string datetime formatted without day (e.g: February 23, 2010 10:20:50 )
4189
     */
4190
    public static function chat_last_connection(
4191
        $student_id,
4192
        $courseId,
4193
        $session_id = 0
4194
    ) {
4195
        $student_id = (int) $student_id;
4196
        $courseId = (int) $courseId;
4197
        $session_id = (int) $session_id;
4198
        $date_time = '';
4199
4200
        // table definition
4201
        $tbl_stats_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
4202
        $sql = "SELECT access_date
4203
                FROM $tbl_stats_access
4204
                WHERE
4205
                     access_tool='".TOOL_CHAT."' AND
4206
                     access_user_id='$student_id' AND
4207
                     c_id = $courseId AND
4208
                     session_id = '$session_id'
4209
                ORDER BY access_date DESC limit 1";
4210
        $rs = Database::query($sql);
4211
        if (Database::num_rows($rs) > 0) {
4212
            $row = Database::fetch_array($rs);
4213
            $date_time = api_convert_and_format_date(
4214
                $row['access_date'],
4215
                null,
4216
                date_default_timezone_get()
4217
            );
4218
        }
4219
4220
        return $date_time;
4221
    }
4222
4223
    /**
4224
     * Get count student's visited links.
4225
     *
4226
     * @param int $student_id Student id
4227
     * @param int $courseId
4228
     * @param int $session_id Session id (optional)
4229
     *
4230
     * @return int count of visited links
4231
     */
4232
    public static function count_student_visited_links($student_id, $courseId, $session_id = 0)
4233
    {
4234
        $student_id = (int) $student_id;
4235
        $courseId = (int) $courseId;
4236
        $session_id = (int) $session_id;
4237
4238
        // table definition
4239
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
4240
4241
        $sql = 'SELECT 1
4242
                FROM '.$table.'
4243
                WHERE
4244
                    links_user_id= '.$student_id.' AND
4245
                    c_id = "'.$courseId.'" AND
4246
                    session_id = '.$session_id.' ';
4247
4248
        $rs = Database::query($sql);
4249
4250
        return Database::num_rows($rs);
4251
    }
4252
4253
    public static function countStudentDownloadedDocuments(int $studentId, int $courseId, int $sessionId = 0): int
4254
    {
4255
        $em = Database::getManager();
4256
        $qb = $em->createQueryBuilder();
4257
4258
        $qb->select('COUNT(td.downId)')
4259
            ->from(TrackEDownloads::class, 'td')
4260
            ->leftJoin('td.resourceLink', 'rl')
4261
            ->where('td.downUserId = :studentId')
4262
            ->andWhere('rl.course = :courseId')
4263
            ->setParameter('studentId', $studentId)
4264
            ->setParameter('courseId', $courseId);
4265
4266
        if ($sessionId > 0) {
4267
            $qb->andWhere('rl.session = :sessionId')
4268
                ->setParameter('sessionId', $sessionId);
4269
        }
4270
4271
        $query = $qb->getQuery();
4272
4273
        return (int) $query->getSingleScalarResult();
4274
    }
4275
4276
    /**
4277
     * Get course list inside a session from a student.
4278
     *
4279
     * @param int $user_id   Student id
4280
     * @param int $sessionId Session id (optional)
4281
     *
4282
     * @return array Courses list
4283
     */
4284
    public static function get_course_list_in_session_from_student($user_id, $sessionId = 0)
4285
    {
4286
        $user_id = (int) $user_id;
4287
        $sessionId = (int) $sessionId;
4288
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4289
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
4290
4291
        $sql = "SELECT c.code
4292
                FROM $tbl_session_course_user sc
4293
                INNER JOIN $courseTable c
4294
                WHERE
4295
                    user_id= $user_id  AND
4296
                    session_id = $sessionId";
4297
        $result = Database::query($sql);
4298
        $courses = [];
4299
        while ($row = Database::fetch_array($result)) {
4300
            $courses[$row['code']] = $row['code'];
4301
        }
4302
4303
        return $courses;
4304
    }
4305
4306
    /**
4307
     * Get inactive students in course.
4308
     *
4309
     * @param int        $courseId
4310
     * @param string|int $since      Since login course date (optional, default = 'never')
4311
     * @param int        $session_id (optional)
4312
     *
4313
     * @return array Inactive users
4314
     */
4315
    public static function getInactiveStudentsInCourse(
4316
        $courseId,
4317
        $since = 'never',
4318
        $session_id = 0
4319
    ) {
4320
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
4321
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4322
        $table_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4323
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
4324
        $now = api_get_utc_datetime();
4325
        $courseId = (int) $courseId;
4326
        $session_id = (int) $session_id;
4327
4328
        if (empty($courseId)) {
4329
            return false;
4330
        }
4331
4332
        if ('never' === $since) {
4333
            if (empty($session_id)) {
4334
                $sql = 'SELECT course_user.user_id
4335
                        FROM '.$table_course_rel_user.' course_user
4336
                        LEFT JOIN '.$tbl_track_login.' stats_login
4337
                        ON course_user.user_id = stats_login.user_id AND
4338
                        relation_type<>'.COURSE_RELATION_TYPE_RRHH.'
4339
                        INNER JOIN '.$tableCourse.' c
4340
                        ON (c.id = course_user.c_id)
4341
                        WHERE
4342
                            course_user.c_id = '.$courseId.' AND
4343
                            stats_login.login_course_date IS NULL
4344
                        GROUP BY course_user.user_id';
4345
            } else {
4346
                $sql = 'SELECT session_course_user.user_id
4347
                        FROM '.$tbl_session_course_user.' session_course_user
4348
                        LEFT JOIN '.$tbl_track_login.' stats_login
4349
                        ON session_course_user.user_id = stats_login.user_id
4350
                        INNER JOIN '.$tableCourse.' c
4351
                        ON (c.id = session_course_user.c_id)
4352
                        WHERE
4353
                            session_course_user.c_id = '.$courseId.' AND
4354
                            stats_login.login_course_date IS NULL
4355
                        GROUP BY session_course_user.user_id';
4356
            }
4357
        } else {
4358
            $since = (int) $since;
4359
            if (empty($session_id)) {
4360
                $inner = 'INNER JOIN '.$table_course_rel_user.' course_user
4361
                          ON course_user.user_id = stats_login.user_id AND course_user.c_id = c.id ';
4362
            } else {
4363
                $inner = 'INNER JOIN '.$tbl_session_course_user.' session_course_user
4364
                          ON
4365
                            c.id = session_course_user.c_id AND
4366
                            session_course_user.session_id = '.$session_id.' AND
4367
                            session_course_user.user_id = stats_login.user_id ';
4368
            }
4369
4370
            $sql = 'SELECT
4371
                    stats_login.user_id,
4372
                    MAX(login_course_date) max_date
4373
                FROM '.$tbl_track_login.' stats_login
4374
                INNER JOIN '.$tableCourse.' c
4375
                ON (c.id = stats_login.c_id)
4376
                '.$inner.'
4377
                WHERE c.id = '.$courseId.'
4378
                GROUP BY stats_login.user_id
4379
                HAVING DATE_SUB("'.$now.'", INTERVAL '.$since.' DAY) > max_date ';
4380
        }
4381
4382
        $rs = Database::query($sql);
4383
4384
        $allow = 'true' === api_get_plugin_setting('pausetraining', 'tool_enable');
4385
        $allowPauseFormation = 'true' === api_get_plugin_setting('pausetraining', 'allow_users_to_edit_pause_formation');
4386
4387
        $extraFieldValue = new ExtraFieldValue('user');
4388
        $users = [];
4389
        while ($user = Database::fetch_array($rs)) {
4390
            $userId = $user['user_id'];
4391
4392
            if ($allow && $allowPauseFormation) {
4393
                $pause = $extraFieldValue->get_values_by_handler_and_field_variable($userId, 'pause_formation');
4394
                if (!empty($pause) && isset($pause['value']) && 1 == $pause['value']) {
4395
                    // Skip user because he paused his formation.
4396
                    continue;
4397
                }
4398
            }
4399
4400
            $users[] = $userId;
4401
        }
4402
4403
        return $users;
4404
    }
4405
4406
    /**
4407
     * get count clicks about tools most used by course.
4408
     *
4409
     * @param int $courseId
4410
     * @param    int        Session id (optional),
4411
     * if param $session_id is null(default) it'll return results
4412
     * including sessions, 0 = session is not filtered
4413
     *
4414
     * @return array tools data
4415
     */
4416
    public static function get_tools_most_used_by_course($courseId, $session_id = null)
4417
    {
4418
        $courseId = (int) $courseId;
4419
        $data = [];
4420
        $TABLETRACK_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
4421
        $condition_session = '';
4422
        if (isset($session_id)) {
4423
            $session_id = (int) $session_id;
4424
            $condition_session = ' AND session_id = '.$session_id;
4425
        }
4426
        $sql = "SELECT
4427
                    access_tool,
4428
                    COUNT(DISTINCT access_user_id),
4429
                    count(access_tool) as count_access_tool
4430
                FROM $TABLETRACK_ACCESS
4431
                WHERE
4432
                    access_tool IS NOT NULL AND
4433
                    access_tool != '' AND
4434
                    c_id = '$courseId'
4435
                    $condition_session
4436
                GROUP BY access_tool
4437
                ORDER BY count_access_tool DESC
4438
                LIMIT 0, 3";
4439
        $rs = Database::query($sql);
4440
        if (Database::num_rows($rs) > 0) {
4441
            while ($row = Database::fetch_array($rs)) {
4442
                $data[] = $row;
4443
            }
4444
        }
4445
4446
        return $data;
4447
    }
4448
4449
    /**
4450
     * get documents most downloaded by course.
4451
     *
4452
     * @param      string     Course code
4453
     * @param    int        Session id (optional),
4454
     * if param $session_id is null(default) it'll return results including
4455
     * sessions, 0 = session is not filtered
4456
     * @param    int        Limit (optional, default = 0, 0 = without limit)
4457
     *
4458
     * @return array documents downloaded
4459
     */
4460
    public static function get_documents_most_downloaded_by_course(
4461
        $course_code,
4462
        $session_id = 0,
4463
        $limit = 0
4464
    ) {
4465
        $courseId = api_get_course_int_id($course_code);
4466
        $data = [];
4467
4468
        $TABLETRACK_DOWNLOADS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
4469
        $tableResourceLink = Database::get_main_table('resource_link');
4470
        $tableResourceNode = Database::get_main_table('resource_node');
4471
        $condition_session = '';
4472
        $session_id = intval($session_id);
4473
        if (!empty($session_id)) {
4474
            $condition_session = ' AND l.session_id = '.$session_id;
4475
        }
4476
        $sql = "SELECT t.resource_link_id as lid,
4477
                       n.path as npath,
4478
                       n.title as ntitle,
4479
                       n.uuid as uuid,
4480
                       n.id as nid,
4481
                    COUNT(t.down_id) as count_down
4482
                FROM $TABLETRACK_DOWNLOADS t
4483
                    INNER JOIN $tableResourceLink l
4484
                    ON t.resource_link_id = l.id
4485
                    INNER JOIN $tableResourceNode n
4486
                    ON l.resource_node_id = n.id
4487
                WHERE l.c_id = $courseId
4488
                    $condition_session
4489
                GROUP BY nid
4490
                ORDER BY count_down DESC
4491
                LIMIT 0,  $limit";
4492
        $rs = Database::query($sql);
4493
4494
        if (Database::num_rows($rs) > 0) {
4495
            while ($row = Database::fetch_array($rs)) {
4496
                $data[] = $row;
4497
            }
4498
        }
4499
4500
        return $data;
4501
    }
4502
4503
    /**
4504
     * get links most visited by course.
4505
     *
4506
     * @param      string     Course code
4507
     * @param    int        Session id (optional),
4508
     * if param $session_id is null(default) it'll
4509
     * return results including sessions, 0 = session is not filtered
4510
     *
4511
     * @return array links most visited
4512
     */
4513
    public static function get_links_most_visited_by_course($course_code, $session_id = null)
4514
    {
4515
        $course_code = Database::escape_string($course_code);
4516
        $course_info = api_get_course_info($course_code);
4517
        $courseId = $course_info['real_id'];
4518
        $data = [];
4519
4520
        $TABLETRACK_LINKS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
4521
        $TABLECOURSE_LINKS = Database::get_course_table(TABLE_LINK);
4522
4523
        $condition_session = '';
4524
        if (isset($session_id)) {
4525
            $session_id = intval($session_id);
4526
            $condition_session = ' AND sl.session_id = '.$session_id;
4527
        }
4528
4529
        $sql = "SELECT cl.title, cl.url,count(DISTINCT sl.links_user_id), count(cl.title) as count_visits
4530
                FROM $TABLETRACK_LINKS AS sl, $TABLECOURSE_LINKS AS cl
4531
                WHERE
4532
                    sl.links_link_id = cl.iid AND
4533
                    sl.c_id = $courseId
4534
                    $condition_session
4535
                GROUP BY cl.title, cl.url
4536
                ORDER BY count_visits DESC
4537
                LIMIT 0, 3";
4538
        $rs = Database::query($sql);
4539
        if (Database::num_rows($rs) > 0) {
4540
            while ($row = Database::fetch_array($rs)) {
4541
                $data[] = $row;
4542
            }
4543
        }
4544
4545
        return $data;
4546
    }
4547
4548
    /**
4549
     * Shows the user progress (when clicking in the Progress tab).
4550
     *
4551
     * @param int    $user_id
4552
     * @param int    $session_id
4553
     * @param string $extra_params
4554
     * @param bool   $show_courses
4555
     * @param bool   $showAllSessions
4556
     * @param bool   $returnArray
4557
     *
4558
     * @return string|array
4559
     */
4560
    public static function show_user_progress(
4561
        $user_id,
4562
        $session_id = 0,
4563
        $extra_params = '',
4564
        $show_courses = true,
4565
        $showAllSessions = true,
4566
        $returnArray = false
4567
    ) {
4568
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
4569
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4570
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
4571
        $tbl_access_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
4572
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4573
        $tbl_access_rel_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4574
4575
        $trackingColumns = [
4576
            'course_session' => [
4577
                'course_title' => true,
4578
                'published_exercises' => true,
4579
                'new_exercises' => true,
4580
                'my_average' => true,
4581
                'average_exercise_result' => true,
4582
                'time_spent' => true,
4583
                'lp_progress' => true,
4584
                'score' => true,
4585
                'best_score' => true,
4586
                'last_connection' => true,
4587
                'details' => true,
4588
            ],
4589
        ];
4590
4591
        $trackingColumnsConfig = api_get_setting('session.tracking_columns', true);
4592
        if (!empty($trackingColumnsConfig)) {
4593
            $trackingColumns = $trackingColumnsConfig;
4594
        }
4595
4596
        $user_id = (int) $user_id;
4597
        $session_id = (int) $session_id;
4598
        $urlId = api_get_current_access_url_id();
4599
4600
        if (AccessUrlHelper::isMultiple()) {
4601
            $sql = "SELECT c.id, c.code, title
4602
                    FROM $tbl_course_user cu
4603
                    INNER JOIN $tbl_course c
4604
                    ON (cu.c_id = c.id)
4605
                    INNER JOIN $tbl_access_rel_course a
4606
                    ON (a.c_id = c.id)
4607
                    WHERE
4608
                        cu.user_id = $user_id AND
4609
                        relation_type<> ".COURSE_RELATION_TYPE_RRHH." AND
4610
                        access_url_id = $urlId
4611
                    ORDER BY title";
4612
        } else {
4613
            $sql = "SELECT c.id, c.code, title
4614
                    FROM $tbl_course_user cu
4615
                    INNER JOIN $tbl_course c
4616
                    ON (cu.c_id = c.id)
4617
                    WHERE
4618
                        cu.user_id = $user_id AND
4619
                        relation_type <> ".COURSE_RELATION_TYPE_RRHH."
4620
                    ORDER BY title";
4621
        }
4622
4623
        $rs = Database::query($sql);
4624
        $courses = $course_in_session = $temp_course_in_session = [];
4625
        $courseIdList = [];
4626
        while ($row = Database::fetch_assoc($rs)) {
4627
            $courses[$row['id']] = $row['title'];
4628
            $courseIdList[] = $row['id'];
4629
        }
4630
4631
        $orderBy = ' ORDER BY title ';
4632
        $extraInnerJoin = null;
4633
4634
        if (SessionManager::orderCourseIsEnabled() && !empty($session_id)) {
4635
            $orderBy = ' ORDER BY s.id, src.position ';
4636
            $tableSessionRelCourse = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4637
            $extraInnerJoin = " INNER JOIN $tableSessionRelCourse src
4638
                                ON (cu.c_id = src.c_id AND src.session_id = $session_id) ";
4639
        }
4640
4641
        $sessionCondition = '';
4642
        if (!empty($session_id)) {
4643
            $sessionCondition = " AND s.id = $session_id";
4644
        }
4645
4646
        // Get the list of sessions where the user is subscribed as student
4647
        if (AccessUrlHelper::isMultiple()) {
4648
            $sql = "SELECT DISTINCT c.code, s.id as session_id, s.title
4649
                    FROM $tbl_session_course_user cu
4650
                    INNER JOIN $tbl_access_rel_session a
4651
                    ON (a.session_id = cu.session_id)
4652
                    INNER JOIN $tbl_session s
4653
                    ON (s.id = a.session_id)
4654
                    INNER JOIN $tbl_course c
4655
                    ON (c.id = cu.c_id)
4656
                    $extraInnerJoin
4657
                    WHERE
4658
                        cu.user_id = $user_id AND
4659
                        access_url_id = ".$urlId."
4660
                        $sessionCondition
4661
                    $orderBy ";
4662
        } else {
4663
            $sql = "SELECT DISTINCT c.code, s.id as session_id, s.title
4664
                    FROM $tbl_session_course_user cu
4665
                    INNER JOIN $tbl_session s
4666
                    ON (s.id = cu.session_id)
4667
                    INNER JOIN $tbl_course c
4668
                    ON (c.id = cu.c_id)
4669
                    $extraInnerJoin
4670
                    WHERE
4671
                        cu.user_id = $user_id
4672
                        $sessionCondition
4673
                    $orderBy ";
4674
        }
4675
4676
        $rs = Database::query($sql);
4677
        $simple_session_array = [];
4678
        while ($row = Database::fetch_assoc($rs)) {
4679
            $course_info = api_get_course_info($row['code']);
4680
            $temp_course_in_session[$row['session_id']]['course_list'][$course_info['real_id']] = $course_info;
4681
            $temp_course_in_session[$row['session_id']]['title'] = $row['title'];
4682
            $simple_session_array[$row['session_id']] = $row['title'];
4683
        }
4684
4685
        foreach ($simple_session_array as $my_session_id => $session_title) {
4686
            $course_list = $temp_course_in_session[$my_session_id]['course_list'];
4687
            $my_course_data = [];
4688
            foreach ($course_list as $courseId => $course_data) {
4689
                $my_course_data[$courseId] = $course_data['title'];
4690
            }
4691
4692
            if (empty($session_id)) {
4693
                $my_course_data = utf8_sort($my_course_data);
4694
            }
4695
4696
            $final_course_data = [];
4697
            foreach ($my_course_data as $course_id => $value) {
4698
                if (isset($course_list[$course_id])) {
4699
                    $final_course_data[$course_id] = $course_list[$course_id];
4700
                }
4701
            }
4702
            $course_in_session[$my_session_id]['course_list'] = $final_course_data;
4703
            $course_in_session[$my_session_id]['title'] = $session_title;
4704
        }
4705
4706
        if ($returnArray) {
4707
            $course_in_session[0] = $courseIdList;
4708
4709
            return $course_in_session;
4710
        }
4711
4712
        $html = '';
4713
        // Course list
4714
        if ($show_courses) {
4715
            if (!empty($courses)) {
4716
                $html .= Display::page_subheader(
4717
                    Display::getMdiIcon(
4718
                        'book-open-page-variant',
4719
                        'ch-tool-icon',
4720
                        null,
4721
                        ICON_SIZE_SMALL,
4722
                        get_lang('My courses')
4723
                    ).' '.get_lang('My courses')
4724
                );
4725
4726
                $columns = [
4727
                    'course_title' => get_lang('Course'),
4728
                    'time_spent' => get_lang('Time spent in the course'),
4729
                    'progress' => get_lang('Progress'),
4730
                    'best_score_in_lp' => get_lang('Best score in learning path'),
4731
                    'best_score_not_in_lp' => get_lang('Best score not in learning path'),
4732
                    'latest_login' => get_lang('Latest login'),
4733
                    'details' => get_lang('Details'),
4734
                ];
4735
                $availableColumns = [];
4736
                if (isset($trackingColumns['my_progress_courses'])) {
4737
                    $availableColumns = $trackingColumns['my_progress_courses'];
4738
                }
4739
                $html .= '<div class="table-responsive">';
4740
                $html .= '<table class="table table-striped table-hover">';
4741
                $html .= '<thead><tr>';
4742
                foreach ($columns as $columnKey => $name) {
4743
                    if (!empty($availableColumns)) {
4744
                        if (isset($availableColumns[$columnKey]) && false == $availableColumns[$columnKey]) {
4745
                            continue;
4746
                        }
4747
                    }
4748
                    $html .= Display::tag('th', $name);
4749
                }
4750
                $html .= '</tr></thead><tbody>';
4751
4752
                foreach ($courses as $courseId => $course_title) {
4753
                    $course = api_get_course_entity($courseId);
4754
                    $courseCode = $course->getCode();
4755
4756
                    $total_time_login = self::get_time_spent_on_the_course(
4757
                        $user_id,
4758
                        $courseId
4759
                    );
4760
                    $time = api_time_to_hms($total_time_login);
4761
                    $progress = self::get_avg_student_progress(
4762
                        $user_id,
4763
                        $course
4764
                    );
4765
                    $bestScore = self::get_avg_student_score(
4766
                        $user_id,
4767
                        $course,
4768
                        [],
4769
                        null,
4770
                        false,
4771
                        false,
4772
                        true
4773
                    );
4774
4775
                    /*$exerciseList = ExerciseLib::get_all_exercises(
4776
                        $courseInfo,
4777
                        0,
4778
                        false,
4779
                        null,
4780
                        false,
4781
                        1
4782
                    );*/
4783
4784
                    $qb = Container::getQuizRepository()->findAllByCourse($course, null, null, 1, false);
4785
                    /** @var CQuiz[] $exercises */
4786
                    $exercises = $qb->getQuery()->getResult();
4787
4788
                    $bestScoreAverageNotInLP = 0;
4789
                    if (!empty($exercises)) {
4790
                        foreach ($exercises as $exerciseData) {
4791
                            $results = Event::get_best_exercise_results_by_user(
4792
                                $exerciseData->getIid(),
4793
                                $courseId,
4794
                                0,
4795
                                $user_id
4796
                            );
4797
                            $best = 0;
4798
                            if (!empty($results)) {
4799
                                foreach ($results as $result) {
4800
                                    if (!empty($result['max_score'])) {
4801
                                        $score = $result['score'] / $result['max_score'];
4802
                                        if ($score > $best) {
4803
                                            $best = $score;
4804
                                        }
4805
                                    }
4806
                                }
4807
                            }
4808
                            $bestScoreAverageNotInLP += $best;
4809
                        }
4810
                        $bestScoreAverageNotInLP = round($bestScoreAverageNotInLP / count($exercises) * 100, 2);
4811
                    }
4812
4813
                    $last_connection = self::get_last_connection_date_on_the_course(
4814
                        $user_id,
4815
                        ['real_id' => $courseId]
4816
                    );
4817
4818
                    if (is_null($progress) || empty($progress)) {
4819
                        $progress = '0%';
4820
                    } else {
4821
                        $progress = $progress.'%';
4822
                    }
4823
4824
                    if (isset($_GET['course']) &&
4825
                        $courseCode == $_GET['course'] &&
4826
                        empty($_GET['session_id'])
4827
                    ) {
4828
                        $html .= '<tr class="row_odd" style="background-color:#FBF09D">';
4829
                    } else {
4830
                        $html .= '<tr class="row_even">';
4831
                    }
4832
                    $url = api_get_course_url($courseId, $session_id);
4833
                    $course_url = Display::url($course_title, $url, ['target' => SESSION_LINK_TARGET]);
4834
                    if (empty($bestScore)) {
4835
                        $bestScoreResult = '-';
4836
                    } else {
4837
                        $bestScoreResult = $bestScore.'%';
4838
                    }
4839
                    if (empty($bestScoreAverageNotInLP)) {
4840
                        $bestScoreNotInLP = '-';
4841
                    } else {
4842
                        $bestScoreNotInLP = $bestScoreAverageNotInLP.'%';
4843
                    }
4844
4845
                    $detailsLink = '';
4846
                    if (isset($_GET['course']) &&
4847
                        $courseCode == $_GET['course'] &&
4848
                        empty($_GET['session_id'])
4849
                    ) {
4850
                        $detailsLink .= '<a href="#course_session_header">';
4851
                        $detailsLink .= Display::getMdiIcon(
4852
                            'fast-forward-outline',
4853
                            'ch-tool-icon',
4854
                            null,
4855
                            ICON_SIZE_SMALL,
4856
                            get_lang('Details')
4857
                        );
4858
                        $detailsLink .= '</a>';
4859
                    } else {
4860
                        $detailsLink .= '<a href="'.api_get_self().'?course='.$courseCode.$extra_params.'#course_session_header">';
4861
                        $detailsLink .= Display::getMdiIcon(
4862
                            'fast-forward-outline',
4863
                            'ch-tool-icon',
4864
                            null,
4865
                            ICON_SIZE_SMALL,
4866
                            get_lang('Details')
4867
                        );
4868
                        $detailsLink .= '</a>';
4869
                    }
4870
4871
                    $result = [
4872
                        'course_title' => $course_url,
4873
                        'time_spent' => $time,
4874
                        'progress' => $progress,
4875
                        'best_score_in_lp' => $bestScoreResult,
4876
                        'best_score_not_in_lp' => $bestScoreNotInLP,
4877
                        'latest_login' => $last_connection,
4878
                        'details' => $detailsLink,
4879
                    ];
4880
4881
                    foreach ($result as $columnKey => $data) {
4882
                        if (!empty($availableColumns)) {
4883
                            if (isset($availableColumns[$columnKey]) && false == $availableColumns[$columnKey]) {
4884
                                continue;
4885
                            }
4886
                        }
4887
                        $html .= '<td>'.$data.'</td>';
4888
                    }
4889
4890
                    $html .= '</tr>';
4891
                }
4892
                $html .= '</tbody></table>';
4893
                $html .= '</div>';
4894
            }
4895
        }
4896
4897
        // Session list
4898
        if (!empty($course_in_session)) {
4899
            $main_session_graph = '';
4900
            // Load graphics only when calling to an specific session
4901
            $all_exercise_graph_name_list = [];
4902
            $my_results = [];
4903
            $all_exercise_graph_list = [];
4904
            $all_exercise_start_time = [];
4905
            foreach ($course_in_session as $my_session_id => $session_data) {
4906
                $course_list = $session_data['course_list'];
4907
                $user_count = count(SessionManager::get_users_by_session($my_session_id));
4908
                $exercise_graph_name_list = [];
4909
                $exercise_graph_list = [];
4910
4911
                foreach ($course_list as $course_data) {
4912
                    $course = api_get_course_entity($course_data['real_id']);
4913
                    $courseId = $course->getId();
4914
                    /*$exercise_list = ExerciseLib::get_all_exercises(
4915
                        $course_data,
4916
                        $my_session_id,
4917
                        false,
4918
                        null,
4919
                        false,
4920
                        1
4921
                    );*/
4922
4923
                    $qb = Container::getQuizRepository()->findAllByCourse($course, null, null, 1, false);
4924
                    /** @var CQuiz[] $exercises */
4925
                    $exercises = $qb->getQuery()->getResult();
4926
                    $countExercises = count($exercises);
4927
                    foreach ($exercises as $exercise_data) {
4928
                        //$exercise_obj = new Exercise($course_data['real_id']);
4929
                        //$exercise_obj->read($exercise_data['id']);
4930
                        // Exercise is not necessary to be visible to show results check the result_disable configuration instead
4931
                        //$visible_return = $exercise_obj->is_visible();
4932
                        $disabled = $exercise_data->getResultsDisabled();
4933
                        $exerciseId = $exercise_data->getIid();
4934
                        if (0 == $disabled || 2 == $disabled) {
4935
                            $best_average = (int)
4936
                                ExerciseLib::get_best_average_score_by_exercise(
4937
                                    $exerciseId,
4938
                                    $courseId,
4939
                                    $my_session_id,
4940
                                    $user_count
4941
                                )
4942
                            ;
4943
4944
                            $exercise_graph_list[] = $best_average;
4945
                            $all_exercise_graph_list[] = $best_average;
4946
4947
                            $user_result_data = ExerciseLib::get_best_attempt_by_user(
4948
                                api_get_user_id(),
4949
                                $exerciseId,
4950
                                $courseId,
4951
                                $my_session_id
4952
                            );
4953
4954
                            $score = 0;
4955
                            if (!empty($user_result_data['max_score']) && 0 != intval($user_result_data['max_score'])) {
4956
                                $score = intval($user_result_data['score'] / $user_result_data['max_score'] * 100);
4957
                            }
4958
                            $start = $exercise_data->getStartTime() ? $exercise_data->getStartTime()->getTimestamp() : null;
4959
                            $time = null !== $start ? $start : 0;
4960
                            $all_exercise_start_time[] = $time;
4961
                            $my_results[] = $score;
4962
                            $exerciseTitle = $exercise_data->getTitle();
4963
                            if ($countExercises <= 10) {
4964
                                $title = cut($course_data['title'], 30)." \n ".cut($exerciseTitle, 30);
4965
                                $exercise_graph_name_list[] = $title;
4966
                                $all_exercise_graph_name_list[] = $title;
4967
                            } else {
4968
                                // if there are more than 10 results, space becomes difficult to find,
4969
                                // so only show the title of the exercise, not the tool
4970
                                $title = cut($exerciseTitle, 30);
4971
                                $exercise_graph_name_list[] = $title;
4972
                                $all_exercise_graph_name_list[] = $title;
4973
                            }
4974
                        }
4975
                    }
4976
                }
4977
            }
4978
4979
            // Complete graph
4980
            if (!empty($my_results) && !empty($all_exercise_graph_list)) {
4981
                asort($all_exercise_start_time);
4982
4983
                //Fix exams order
4984
                $final_all_exercise_graph_name_list = [];
4985
                $my_results_final = [];
4986
                $final_all_exercise_graph_list = [];
4987
4988
                foreach ($all_exercise_start_time as $key => $time) {
4989
                    $label_time = '';
4990
                    if (!empty($time)) {
4991
                        $label_time = date('d-m-y', $time);
4992
                    }
4993
                    $final_all_exercise_graph_name_list[] = $all_exercise_graph_name_list[$key].' '.$label_time;
4994
                    $my_results_final[] = $my_results[$key];
4995
                    $final_all_exercise_graph_list[] = $all_exercise_graph_list[$key];
4996
                }
4997
                $main_session_graph = self::generate_session_exercise_graph(
4998
                    $final_all_exercise_graph_name_list,
4999
                    $my_results_final,
5000
                    $final_all_exercise_graph_list
5001
                );
5002
            }
5003
5004
            $sessionIcon = Display::getMdiIcon(
5005
                'google-classroom',
5006
                'ch-tool-icon',
5007
                null,
5008
                ICON_SIZE_SMALL,
5009
                get_lang('Course sessions')
5010
            );
5011
5012
            $anchor = Display::url('', '', ['name' => 'course_session_header']);
5013
            $html .= $anchor.Display::page_subheader(
5014
                $sessionIcon.' '.get_lang('Course sessions')
5015
            );
5016
5017
            $html .= '<div class="table-responsive">';
5018
            $html .= '<table class="table table-striped table-hover">';
5019
            $html .= '<thead>';
5020
            $html .= '<tr>
5021
                  '.Display::tag('th', get_lang('Session'), ['width' => '300px']).'
5022
                  '.Display::tag('th', get_lang('Tests available'), ['width' => '300px']).'
5023
                  '.Display::tag('th', get_lang('New exercises')).'
5024
                  '.Display::tag('th', get_lang('Average exercise result')).'
5025
                  '.Display::tag('th', get_lang('Details')).'
5026
                  </tr>';
5027
            $html .= '</thead>';
5028
            $html .= '<tbody>';
5029
5030
            $session = api_get_session_entity($my_session_id);
5031
5032
            foreach ($course_in_session as $my_session_id => $session_data) {
5033
                $course_list = $session_data['course_list'];
5034
                $session_name = $session_data['title'];
5035
                if (false == $showAllSessions) {
5036
                    if (isset($session_id) && !empty($session_id)) {
5037
                        if ($session_id != $my_session_id) {
5038
                            continue;
5039
                        }
5040
                    }
5041
                }
5042
5043
                $all_exercises = 0;
5044
                $all_unanswered_exercises_by_user = 0;
5045
                $all_average = 0;
5046
                $stats_array = [];
5047
5048
                foreach ($course_list as $course_data) {
5049
                    $courseId = $course_data['real_id'];
5050
                    $course = api_get_course_entity($courseId);
5051
5052
                    // All exercises in the course @todo change for a real count
5053
                    //$exercises = ExerciseLib::get_all_exercises($course_data, $my_session_id);
5054
5055
                    $qb = Container::getQuizRepository()->findAllByCourse($course, $session, null, 2);
5056
5057
                    /** @var CQuiz[] $exercises */
5058
                    $exercises = $qb->getQuery()->getResult();
5059
                    $count_exercises = count($exercises);
5060
5061
                    // Count of user results
5062
                    $done_exercises = null;
5063
                    $answered_exercises = 0;
5064
                    if (!empty($exercises)) {
5065
                        foreach ($exercises as $exercise_item) {
5066
                            $attempts = Event::count_exercise_attempts_by_user(
5067
                                api_get_user_id(),
5068
                                $exercise_item->getIid(),
5069
                                $courseId,
5070
                                $my_session_id
5071
                            );
5072
                            if ($attempts > 1) {
5073
                                $answered_exercises++;
5074
                            }
5075
                        }
5076
                    }
5077
5078
                    // Average
5079
                    $average = ExerciseLib::get_average_score_by_course(
5080
                        $courseId,
5081
                        $my_session_id
5082
                    );
5083
                    $all_exercises += $count_exercises;
5084
                    $all_unanswered_exercises_by_user += $count_exercises - $answered_exercises;
5085
                    $all_average += $average;
5086
                }
5087
5088
                if (!empty($course_list)) {
5089
                    $all_average = $all_average / count($course_list);
5090
                }
5091
5092
                if (isset($_GET['session_id']) && $my_session_id == $_GET['session_id']) {
5093
                    $html .= '<tr style="background-color:#FBF09D">';
5094
                } else {
5095
                    $html .= '<tr>';
5096
                }
5097
                $url = api_get_path(WEB_CODE_PATH)."session/index.php?session_id={$my_session_id}";
5098
5099
                $html .= Display::tag('td', Display::url($session_title, $url, ['target' => SESSION_LINK_TARGET]));
5100
                $html .= Display::tag('td', $all_exercises);
5101
                $html .= Display::tag('td', $all_unanswered_exercises_by_user);
5102
                $html .= Display::tag('td', ExerciseLib::convert_to_percentage($all_average));
5103
5104
                if (isset($_GET['session_id']) && $my_session_id == $_GET['session_id']) {
5105
                    $icon = Display::url(
5106
                        Display::getMdiIcon(
5107
                            'fast-forward-outline',
5108
                            'ch-tool-icon',
5109
                            null,
5110
                            ICON_SIZE_SMALL,
5111
                            get_lang('Details')
5112
                        ),
5113
                        api_get_self().'?session_id='.$my_session_id.'#course_session_list'
5114
                    );
5115
                } else {
5116
                    $icon = Display::url(
5117
                        Display::getMdiIcon(
5118
                            'fast-forward-outline',
5119
                            'ch-tool-icon',
5120
                            null,
5121
                            ICON_SIZE_SMALL,
5122
                            get_lang('Details')
5123
                        ),
5124
                        api_get_self().'?session_id='.$my_session_id.'#course_session_list'
5125
                    );
5126
                }
5127
                $html .= Display::tag('td', $icon);
5128
                $html .= '</tr>';
5129
            }
5130
            $html .= '</tbody>';
5131
            $html .= '</table></div><br />';
5132
            $html .= Display::div(
5133
                $main_session_graph,
5134
                [
5135
                    'id' => 'session_graph',
5136
                    'class' => 'chart-session',
5137
                    'style' => 'position:relative; text-align: center;',
5138
                ]
5139
            );
5140
5141
            // Checking selected session.
5142
            if (isset($_GET['session_id'])) {
5143
                $session_id_from_get = (int) $_GET['session_id'];
5144
                $session_data = $course_in_session[$session_id_from_get];
5145
                $course_list = $session_data['course_list'];
5146
5147
                $html .= '<a name= "course_session_list"></a>';
5148
                $html .= Display::tag('h3', $session_data['title'].' - '.get_lang('Course list'));
5149
5150
                $html .= '<div class="table-responsive">';
5151
                $html .= '<table class="table table-hover table-striped">';
5152
5153
                $columnHeaders = [
5154
                    'course_title' => [
5155
                        get_lang('Course'),
5156
                        ['width' => '300px'],
5157
                    ],
5158
                    'published_exercises' => [
5159
                        get_lang('Tests available'),
5160
                    ],
5161
                    'new_exercises' => [
5162
                        get_lang('New exercises'),
5163
                    ],
5164
                    'my_average' => [
5165
                        get_lang('My average'),
5166
                    ],
5167
                    'average_exercise_result' => [
5168
                        get_lang('Average exercise result'),
5169
                    ],
5170
                    'time_spent' => [
5171
                        get_lang('Time spent in the course'),
5172
                    ],
5173
                    'lp_progress' => [
5174
                        get_lang('Learning path progress'),
5175
                    ],
5176
                    'score' => [
5177
                        get_lang('Score').
5178
                        Display::getMdiIcon(
5179
                            ActionIcon::INFORMATION,
5180
                            'ch-tool-icon',
5181
                            null,
5182
                            ICON_SIZE_SMALL,
5183
                            get_lang('Average of tests in Learning Paths')
5184
                        ),
5185
                    ],
5186
                    'best_score' => [
5187
                        get_lang('Best score'),
5188
                    ],
5189
                    'last_connection' => [
5190
                        get_lang('Latest login'),
5191
                    ],
5192
                    'details' => [
5193
                        get_lang('Details'),
5194
                    ],
5195
                ];
5196
5197
                $html .= '<thead><tr>';
5198
                foreach ($columnHeaders as $key => $columnSetting) {
5199
                    if (isset($trackingColumns['course_session']) &&
5200
                        in_array($key, $trackingColumns['course_session']) &&
5201
                        $trackingColumns['course_session'][$key]
5202
                    ) {
5203
                        $settings = isset($columnSetting[1]) ? $columnSetting[1] : [];
5204
                        $html .= Display::tag(
5205
                             'th',
5206
                             $columnSetting[0],
5207
                             $settings
5208
                         );
5209
                    }
5210
                }
5211
5212
                $html .= '</tr>
5213
                    </thead>
5214
                    <tbody>';
5215
5216
                foreach ($course_list as $course_data) {
5217
                    $course_code = $course_data['code'];
5218
                    $course_title = $course_data['title'];
5219
                    $courseId = $course_data['real_id'];
5220
                    $course = api_get_course_entity($courseId);
5221
                    $session = api_get_session_entity($session_id_from_get);
5222
5223
                    $qb = Container::getQuizRepository()->findAllByCourse($course, $session, null, 2);
5224
5225
                    /** @var CQuiz[] $exercises */
5226
                    $exercises = $qb->getQuery()->getResult();
5227
                    $count_exercises = 0;
5228
                    if (!empty($exercises)) {
5229
                        $count_exercises = count($exercises);
5230
                    }
5231
5232
                    $answered_exercises = 0;
5233
                    foreach ($exercises as $exercise_item) {
5234
                        $attempts = Event::count_exercise_attempts_by_user(
5235
                            api_get_user_id(),
5236
                            $exercise_item->getIid(),
5237
                            $courseId,
5238
                            $session_id_from_get
5239
                        );
5240
                        if ($attempts > 1) {
5241
                            $answered_exercises++;
5242
                        }
5243
                    }
5244
5245
                    $unanswered_exercises = $count_exercises - $answered_exercises;
5246
5247
                    // Average
5248
                    $average = ExerciseLib::get_average_score_by_course(
5249
                        $courseId,
5250
                        $session_id_from_get
5251
                    );
5252
                    $my_average = ExerciseLib::get_average_score_by_course_by_user(
5253
                        api_get_user_id(),
5254
                        $courseId,
5255
                        $session_id_from_get
5256
                    );
5257
5258
                    $bestScore = self::get_avg_student_score(
5259
                        $user_id,
5260
                        $course,
5261
                        [],
5262
                        $session,
5263
                        false,
5264
                        false,
5265
                        true
5266
                    );
5267
5268
                    $stats_array[$course_code] = [
5269
                        'exercises' => $count_exercises,
5270
                        'unanswered_exercises_by_user' => $unanswered_exercises,
5271
                        'done_exercises' => $done_exercises,
5272
                        'average' => $average,
5273
                        'my_average' => $my_average,
5274
                        'best_score' => $bestScore,
5275
                    ];
5276
5277
                    $last_connection = self::get_last_connection_date_on_the_course(
5278
                        $user_id,
5279
                        $course_data,
5280
                        $session_id_from_get
5281
                    );
5282
5283
                    $progress = self::get_avg_student_progress(
5284
                        $user_id,
5285
                        $course,
5286
                        [],
5287
                        $session
5288
                    );
5289
5290
                    $total_time_login = self::get_time_spent_on_the_course(
5291
                        $user_id,
5292
                        $courseId,
5293
                        $session_id_from_get
5294
                    );
5295
                    $time = api_time_to_hms($total_time_login);
5296
5297
                    $percentage_score = self::get_avg_student_score(
5298
                        $user_id,
5299
                        $course,
5300
                        [],
5301
                        $session
5302
                    );
5303
                    $courseCodeFromGet = isset($_GET['course']) ? $_GET['course'] : null;
5304
5305
                    if ($course_code == $courseCodeFromGet && $_GET['session_id'] == $session_id_from_get) {
5306
                        $html .= '<tr class="row_odd" style="background-color:#FBF09D" >';
5307
                    } else {
5308
                        $html .= '<tr class="row_even">';
5309
                    }
5310
5311
                    $url = api_get_course_url($courseId, $session_id_from_get);
5312
                    $course_url = Display::url(
5313
                        $course_title,
5314
                        $url,
5315
                        ['target' => SESSION_LINK_TARGET]
5316
                    );
5317
5318
                    if (is_numeric($progress)) {
5319
                        $progress = $progress.'%';
5320
                    } else {
5321
                        $progress = '0%';
5322
                    }
5323
                    if (is_numeric($percentage_score)) {
5324
                        $percentage_score = $percentage_score.'%';
5325
                    } else {
5326
                        $percentage_score = '0%';
5327
                    }
5328
5329
                    if (is_numeric($stats_array[$course_code]['best_score'])) {
5330
                        $bestScore = $stats_array[$course_code]['best_score'].'%';
5331
                    } else {
5332
                        $bestScore = '-';
5333
                    }
5334
5335
                    if (empty($last_connection) || is_bool($last_connection)) {
5336
                        $last_connection = '';
5337
                    }
5338
5339
                    if ($course_code == $courseCodeFromGet &&
5340
                        $_GET['session_id'] == $session_id_from_get
5341
                    ) {
5342
                        $details = Display::url(
5343
                            Display::getMdiIcon('fast-forward-outline', 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Details')),
5344
                        '#course_session_data'
5345
                        );
5346
                    } else {
5347
                        $url = api_get_self().
5348
                            '?course='.$course_code.'&session_id='.$session_id_from_get.$extra_params.'#course_session_data';
5349
                        $details = Display::url(
5350
                            Display::getMdiIcon('fast-forward-outline', 'ch-tool-icon', null, ICON_SIZE_SMALL, get_lang('Details')
5351
                            ),
5352
                            $url
5353
                        );
5354
                    }
5355
5356
                    $data = [
5357
                        'course_title' => $course_url,
5358
                        'published_exercises' => $stats_array[$course_code]['exercises'], // exercise available
5359
                        'new_exercises' => $stats_array[$course_code]['unanswered_exercises_by_user'],
5360
                        'my_average' => ExerciseLib::convert_to_percentage($stats_array[$course_code]['my_average']),
5361
                        'average_exercise_result' => 0 == $stats_array[$course_code]['average'] ? '-' : '('.ExerciseLib::convert_to_percentage($stats_array[$course_code]['average']).')',
5362
                        'time_spent' => $time,
5363
                        'lp_progress' => $progress,
5364
                        'score' => $percentage_score,
5365
                        'best_score' => $bestScore,
5366
                        'last_connection' => $last_connection,
5367
                        'details' => $details,
5368
                    ];
5369
5370
                    foreach ($data as $key => $value) {
5371
                        if (in_array($key, $trackingColumns['course_session'])
5372
                            && $trackingColumns['course_session'][$key]
5373
                        ) {
5374
                            $html .= Display::tag('td', $value);
5375
                        }
5376
                    }
5377
                    $html .= '</tr>';
5378
                }
5379
                $html .= '</tbody></table></div>';
5380
            }
5381
        }
5382
5383
        $pluginCalendar = 'true' === api_get_plugin_setting('learning_calendar', 'enabled');
5384
        if ($pluginCalendar) {
5385
            $course_in_session[0] = $courseIdList;
5386
            $plugin = LearningCalendarPlugin::create();
5387
            $html .= $plugin->getUserStatsPanel($user_id, $course_in_session);
5388
        }
5389
5390
        return $html;
5391
    }
5392
5393
    /**
5394
     * Shows the user detail progress (when clicking in the details link).
5395
     *
5396
     * @param int  $userId
5397
     * @param int  $courseId
5398
     * @param int  $sessionId
5399
     * @param bool $showDiagram
5400
     *
5401
     * @return string html code
5402
     */
5403
    public static function show_course_detail($userId, $courseId, $sessionId = 0, $showDiagram = false)
5404
    {
5405
        $html = '';
5406
        $courseId = (int) $courseId;
5407
5408
        if (empty($courseId)) {
5409
            return '';
5410
        }
5411
        $userId = (int) $userId;
5412
        $sessionId = (int) $sessionId;
5413
        $course = api_get_course_entity($courseId);
5414
        if (null === $course) {
5415
            return '';
5416
        }
5417
        $courseCode = $course->getCode();
5418
5419
        $html .= '<a name="course_session_data"></a>';
5420
        $html .= Display::page_subheader($course->getTitle());
5421
5422
        if ($showDiagram && !empty($sessionId)) {
5423
            $visibility = api_get_session_visibility($sessionId);
5424
            if (SESSION_AVAILABLE === $visibility) {
5425
                $html .= Display::page_subheader2($course->getTitle());
5426
            }
5427
        }
5428
5429
        $html .= '<div class="table-responsive">';
5430
        $html .= '<table class="table table-striped table-hover">';
5431
5432
        // Course details
5433
        $html .= '
5434
            <thead>
5435
            <tr>
5436
            <th>'.get_lang('Tests').'</th>
5437
            <th>'.get_lang('Attempts').'</th>
5438
            <th>'.get_lang('Best attempt').'</th>
5439
            <th>'.get_lang('Ranking').'</th>
5440
            <th>'.get_lang('Best result in course').'</th>
5441
            <th>'.get_lang('Statistics').' '
5442
                .Display::getMdiIcon(
5443
                    ActionIcon::INFORMATION,
5444
                    'ch-tool-icon',
5445
                    null,
5446
                    ICON_SIZE_SMALL,
5447
                    get_lang('In case of multiple attempts')
5448
                    ).
5449
            '</th>
5450
            </tr>
5451
            </thead>
5452
            <tbody>';
5453
        $session = null;
5454
        if (empty($sessionId)) {
5455
            $user_list = CourseManager::get_user_list_from_course_code(
5456
                $courseCode,
5457
                $sessionId,
5458
                null,
5459
                null,
5460
                STUDENT
5461
            );
5462
        } else {
5463
            $session = api_get_session_entity($sessionId);
5464
            $user_list = CourseManager::get_user_list_from_course_code(
5465
                $courseCode,
5466
                $sessionId,
5467
                null,
5468
                null,
5469
                0
5470
            );
5471
        }
5472
5473
        // Show exercise results of invisible exercises? see BT#4091
5474
        /*$exercise_list = ExerciseLib::get_all_exercises(
5475
            $course_info,
5476
            $session_id,
5477
            false,
5478
            null,
5479
            false,
5480
            2
5481
        );*/
5482
        $qb = Container::getQuizRepository()->findAllByCourse($course, $session, null, 2, false);
5483
        /** @var CQuiz[] $exercises */
5484
        $exercises = $qb->getQuery()->getResult();
5485
5486
        $to_graph_exercise_result = [];
5487
        if (!empty($exercises)) {
5488
            $weighting = $exe_id = 0;
5489
            foreach ($exercises as $exercise) {
5490
                $exerciseId = $exercise->getIid();
5491
                $exercise_obj = new Exercise($courseId);
5492
                $exercise_obj->read($exerciseId);
5493
                $visible_return = $exercise_obj->is_visible();
5494
                $score = $weighting = $attempts = 0;
5495
5496
                // Getting count of attempts by user
5497
                $attempts = Event::count_exercise_attempts_by_user(
5498
                    api_get_user_id(),
5499
                    $exercise->getIid(),
5500
                    $courseId,
5501
                    $sessionId
5502
                );
5503
5504
                $html .= '<tr class="row_even">';
5505
                $url = api_get_path(WEB_CODE_PATH).
5506
                    "exercise/overview.php?cid={$courseId}&sid=$sessionId&exerciseId={$exerciseId}";
5507
5508
                if (true == $visible_return['value']) {
5509
                    $exerciseTitle = Display::url(
5510
                        $exercise->getTitle(),
5511
                        $url,
5512
                        ['target' => SESSION_LINK_TARGET]
5513
                    );
5514
                } elseif (-1 == $exercise->getActive()) {
5515
                    $exerciseTitle = sprintf(get_lang('%s (deleted)'), $exercise->getTitle());
5516
                }
5517
5518
                $html .= Display::tag('td', $exerciseTitle);
5519
                $resultsDisabled = $exercise->getResultsDisabled();
5520
5521
                // Exercise configuration show results or show only score
5522
                if (0 == $resultsDisabled || 2 == $resultsDisabled) {
5523
                    //For graphics
5524
                    $best_exercise_stats = Event::get_best_exercise_results_by_user(
5525
                        $exerciseId,
5526
                        $courseId,
5527
                        $sessionId
5528
                    );
5529
5530
                    $to_graph_exercise_result[$exerciseId] = [
5531
                        'title' => $exerciseTitle,
5532
                        'data' => $best_exercise_stats,
5533
                    ];
5534
5535
                    $latest_attempt_url = '';
5536
                    $best_score = $position = $percentage_score_result = '-';
5537
                    $graph = $normal_graph = null;
5538
5539
                    // Getting best results
5540
                    $best_score_data = ExerciseLib::get_best_attempt_in_course(
5541
                        $exerciseId,
5542
                        $courseId,
5543
                        $sessionId
5544
                    );
5545
5546
                    $best_score = '';
5547
                    if (!empty($best_score_data)) {
5548
                        $best_score = ExerciseLib::show_score(
5549
                            $best_score_data['score'],
5550
                            $best_score_data['max_score']
5551
                        );
5552
                    }
5553
5554
                    if ($attempts > 0) {
5555
                        $exercise_stat = ExerciseLib::get_best_attempt_by_user(
5556
                            api_get_user_id(),
5557
                            $exerciseId,
5558
                            $courseId,
5559
                            $sessionId
5560
                        );
5561
                        if (!empty($exercise_stat)) {
5562
                            // Always getting the BEST attempt
5563
                            $score = $exercise_stat['score'];
5564
                            $weighting = $exercise_stat['max_score'];
5565
                            $exe_id = $exercise_stat['exe_id'];
5566
5567
                            $latest_attempt_url .= api_get_path(WEB_CODE_PATH).
5568
                                'exercise/result.php?id='.$exe_id.'&cid='.$courseId.'&show_headers=1&sid='.$sessionId;
5569
                            $percentage_score_result = Display::url(
5570
                                ExerciseLib::show_score($score, $weighting),
5571
                                $latest_attempt_url
5572
                            );
5573
                            $my_score = 0;
5574
                            if (!empty($weighting) && 0 != intval($weighting)) {
5575
                                $my_score = $score / $weighting;
5576
                            }
5577
                            //@todo this function slows the page
5578
                            if (is_int($user_list)) {
5579
                                $user_list = [$user_list];
5580
                            }
5581
                            $position = ExerciseLib::get_exercise_result_ranking(
5582
                                $my_score,
5583
                                $exe_id,
5584
                                $exerciseId,
5585
                                $courseCode,
5586
                                $sessionId,
5587
                                $user_list
5588
                            );
5589
5590
                            $graph = self::generate_exercise_result_thumbnail_graph(
5591
                                $to_graph_exercise_result[$exerciseId]
5592
                            );
5593
                            $normal_graph = self::generate_exercise_result_graph(
5594
                                $to_graph_exercise_result[$exerciseId]
5595
                            );
5596
                        }
5597
                    }
5598
                    $html .= Display::div(
5599
                        $normal_graph,
5600
                        [
5601
                            'id' => 'main_graph_'.$exerciseId,
5602
                            'class' => 'dialog',
5603
                            'style' => 'display:none',
5604
                        ]
5605
                    );
5606
5607
                    if (empty($graph)) {
5608
                        $graph = '-';
5609
                    } else {
5610
                        $graph = Display::url(
5611
                            '<img src="'.$graph.'" >',
5612
                            $normal_graph,
5613
                            [
5614
                                'id' => $exerciseId,
5615
                                'class' => 'expand-image',
5616
                            ]
5617
                        );
5618
                    }
5619
5620
                    $html .= Display::tag('td', $attempts);
5621
                    $html .= Display::tag('td', $percentage_score_result);
5622
                    $html .= Display::tag('td', $position);
5623
                    $html .= Display::tag('td', $best_score);
5624
                    $html .= Display::tag('td', $graph);
5625
                } else {
5626
                    // Exercise configuration NO results
5627
                    $html .= Display::tag('td', $attempts);
5628
                    $html .= Display::tag('td', '-');
5629
                    $html .= Display::tag('td', '-');
5630
                    $html .= Display::tag('td', '-');
5631
                    $html .= Display::tag('td', '-');
5632
                }
5633
                $html .= '</tr>';
5634
            }
5635
        } else {
5636
            $html .= '<tr><td colspan="5">'.get_lang('There is no test for the moment').'</td></tr>';
5637
        }
5638
        $html .= '</tbody></table></div>';
5639
5640
        $columnHeaders = [
5641
            'lp' => get_lang('Learning paths'),
5642
            'time' => get_lang('Time spent'),
5643
            'progress' => get_lang('Progress'),
5644
            'score' => get_lang('Score'),
5645
            'best_score' => get_lang('Best score'),
5646
            'last_connection' => get_lang('Latest login'),
5647
        ];
5648
5649
        $headers = '';
5650
        $trackingColumns = api_get_setting('session.tracking_columns', true);
5651
        if (isset($trackingColumns['my_progress_lp'])) {
5652
            foreach ($columnHeaders as $key => $value) {
5653
                if (!isset($trackingColumns['my_progress_lp'][$key]) ||
5654
                    false == $trackingColumns['my_progress_lp'][$key]
5655
                ) {
5656
                    unset($columnHeaders[$key]);
5657
                }
5658
            }
5659
        }
5660
5661
        $columnHeadersKeys = array_keys($columnHeaders);
5662
        foreach ($columnHeaders as $columnName) {
5663
            $headers .= Display::tag(
5664
                'th',
5665
                $columnName
5666
            );
5667
        }
5668
5669
        // LP table results
5670
        $html .= '<div class="table-responsive">';
5671
        $html .= '<table class="table table-striped table-hover">';
5672
        $html .= '<thead><tr>';
5673
        $html .= $headers;
5674
        $html .= '</tr></thead><tbody>';
5675
5676
        $list = new LearnpathList(
5677
            api_get_user_id(),
5678
            ['real_id' => $courseId],
5679
            $sessionId,
5680
            'resource.publishedOn ASC',
5681
            true,
5682
            null,
5683
            true
5684
        );
5685
5686
        $lp_list = $list->get_flat_list();
5687
5688
        if (!empty($lp_list)) {
5689
            foreach ($lp_list as $lp_id => $learnpath) {
5690
                if (!$learnpath['lp_visibility']) {
5691
                    continue;
5692
                }
5693
5694
                $progress = self::get_avg_student_progress(
5695
                    $userId,
5696
                    $course,
5697
                    [$lp_id],
5698
                    $session
5699
                );
5700
                $last_connection_in_lp = self::get_last_connection_time_in_lp(
5701
                    $userId,
5702
                    $course->getCode(),
5703
                    $lp_id,
5704
                    $sessionId
5705
                );
5706
5707
                $time_spent_in_lp = self::get_time_spent_in_lp(
5708
                    $userId,
5709
                    $course,
5710
                    [$lp_id],
5711
                    $sessionId
5712
                );
5713
                $percentage_score = self::get_avg_student_score(
5714
                    $userId,
5715
                    $course,
5716
                    [$lp_id],
5717
                    $session
5718
                );
5719
5720
                $bestScore = self::get_avg_student_score(
5721
                    $userId,
5722
                    $course,
5723
                    [$lp_id],
5724
                    $session,
5725
                    false,
5726
                    false,
5727
                    true
5728
                );
5729
5730
                if (is_numeric($progress)) {
5731
                    $progress = $progress.'%';
5732
                }
5733
                if (is_numeric($percentage_score)) {
5734
                    $percentage_score = $percentage_score.'%';
5735
                } else {
5736
                    $percentage_score = '0%';
5737
                }
5738
5739
                if (is_numeric($bestScore)) {
5740
                    $bestScore = $bestScore.'%';
5741
                } else {
5742
                    $bestScore = '-';
5743
                }
5744
5745
                $time_spent_in_lp = api_time_to_hms($time_spent_in_lp);
5746
                $last_connection = '-';
5747
                if (!empty($last_connection_in_lp)) {
5748
                    $last_connection = api_convert_and_format_date(
5749
                        $last_connection_in_lp,
5750
                        DATE_TIME_FORMAT_LONG
5751
                    );
5752
                }
5753
5754
                $url = api_get_path(WEB_CODE_PATH).
5755
                    "lp/lp_controller.php?cid={$courseId}&sid=$sessionId&lp_id=$lp_id&action=view";
5756
                $html .= '<tr class="row_even">';
5757
5758
                if (in_array('lp', $columnHeadersKeys)) {
5759
                    if (0 == $learnpath['lp_visibility']) {
5760
                        $html .= Display::tag('td', $learnpath['lp_name']);
5761
                    } else {
5762
                        $html .= Display::tag(
5763
                            'td',
5764
                            Display::url(
5765
                                $learnpath['lp_name'],
5766
                                $url,
5767
                                ['target' => SESSION_LINK_TARGET]
5768
                            )
5769
                        );
5770
                    }
5771
                }
5772
5773
                if (in_array('time', $columnHeadersKeys)) {
5774
                    $html .= Display::tag(
5775
                        'td',
5776
                        $time_spent_in_lp
5777
                    );
5778
                }
5779
5780
                if (in_array('progress', $columnHeadersKeys)) {
5781
                    $html .= Display::tag(
5782
                        'td',
5783
                        $progress
5784
                    );
5785
                }
5786
5787
                if (in_array('score', $columnHeadersKeys)) {
5788
                    $html .= Display::tag('td', $percentage_score);
5789
                }
5790
                if (in_array('best_score', $columnHeadersKeys)) {
5791
                    $html .= Display::tag('td', $bestScore);
5792
                }
5793
5794
                if (in_array('last_connection', $columnHeadersKeys)) {
5795
                    $html .= Display::tag('td', $last_connection, ['width' => '180px']);
5796
                }
5797
                $html .= '</tr>';
5798
            }
5799
        } else {
5800
            $html .= '<tr>
5801
                    <td colspan="4" align="center">
5802
                        '.get_lang('No learning path').'
5803
                    </td>
5804
                  </tr>';
5805
        }
5806
        $html .= '</tbody></table></div>';
5807
5808
        $html .= self::displayUserSkills($userId, $courseId, $sessionId);
5809
5810
        return $html;
5811
    }
5812
5813
    /**
5814
     * Generates an histogram.
5815
     *
5816
     * @param array $names      list of exercise names
5817
     * @param array $my_results my results 0 to 100
5818
     * @param array $average    average scores 0-100
5819
     *
5820
     * @return string
5821
     */
5822
    public static function generate_session_exercise_graph($names, $my_results, $average)
5823
    {
5824
        //$html = api_get_js('chartjs/Chart.js');
5825
        $canvas = Display::tag('canvas', '', ['id' => 'session_graph_chart']);
5826
        $html = Display::tag('div', $canvas, ['style' => 'width:100%']);
5827
        $jsStr = " var data = {
5828
                       labels:".json_encode($names).",
5829
                       datasets: [
5830
                       {
5831
                         label: '".get_lang('My results')."',
5832
                         backgroundColor: 'rgb(255, 99, 132)',
5833
                         stack: 'Stack1',
5834
                         data: ".json_encode($my_results).",
5835
                        },
5836
                        {
5837
                         label: '".get_lang('Average score')."',
5838
                         backgroundColor: 'rgb(75, 192, 192)',
5839
                         stack: 'Stack2',
5840
                         data: ".json_encode($average).",
5841
                        },
5842
                        ],
5843
                    };
5844
                    var ctx = document.getElementById('session_graph_chart').getContext('2d');
5845
                    var myBarChart = new Chart(ctx, {
5846
                    type: 'bar',
5847
                    data: data,
5848
                    options: {
5849
                            title: {
5850
                                    display: true,
5851
                                    text: '".get_lang('TestsInTimeProgressChart')."'
5852
                            },
5853
                            tooltips: {
5854
                                    mode: 'index',
5855
                                    intersect: false
5856
                            },
5857
                            responsive: true,
5858
                            scales: {
5859
                                yAxes: [{
5860
                                    ticks: {
5861
                                        // Include a dollar sign in the ticks
5862
                                        callback: function(value, index, values) {
5863
                                            return value + '%';
5864
                                        }
5865
                                    }
5866
                                }]
5867
                            }
5868
                    }
5869
                });";
5870
        $html .= Display::tag('script', $jsStr);
5871
5872
        return $html;
5873
    }
5874
5875
    /**
5876
     * Returns a thumbnail of the function generate_exercise_result_graph.
5877
     *
5878
     * @param array $attempts
5879
     */
5880
    public static function generate_exercise_result_thumbnail_graph($attempts)
5881
    {
5882
        //$exercise_title = $attempts['title'];
5883
        $attempts = $attempts['data'];
5884
        $my_exercise_result_array = $exercise_result = [];
5885
        if (empty($attempts)) {
5886
            return null;
5887
        }
5888
5889
        foreach ($attempts as $attempt) {
5890
            if (api_get_user_id() == $attempt['exe_user_id']) {
5891
                if (0 != $attempt['max_score']) {
5892
                    $my_exercise_result_array[] = $attempt['score'] / $attempt['max_score'];
5893
                }
5894
            } else {
5895
                if (0 != $attempt['max_score']) {
5896
                    $exercise_result[] = $attempt['score'] / $attempt['max_score'];
5897
                }
5898
            }
5899
        }
5900
5901
        // Getting best result
5902
        rsort($my_exercise_result_array);
5903
        $my_exercise_result = 0;
5904
        if (isset($my_exercise_result_array[0])) {
5905
            $my_exercise_result = $my_exercise_result_array[0] * 100;
5906
        }
5907
5908
        $max = 100;
5909
        $pieces = 5;
5910
        $part = round($max / $pieces);
5911
        $x_axis = [];
5912
        $final_array = [];
5913
        $my_final_array = [];
5914
5915
        for ($i = 1; $i <= $pieces; $i++) {
5916
            $sum = 1;
5917
            if (1 == $i) {
5918
                $sum = 0;
5919
            }
5920
            $min = ($i - 1) * $part + $sum;
5921
            $max = ($i) * $part;
5922
            $x_axis[] = $min." - ".$max;
5923
            $count = 0;
5924
            foreach ($exercise_result as $result) {
5925
                $percentage = $result * 100;
5926
                if ($percentage >= $min && $percentage <= $max) {
5927
                    //echo ' is > ';
5928
                    $count++;
5929
                }
5930
            }
5931
            //echo '<br />';
5932
            $final_array[] = $count;
5933
5934
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
5935
                $my_final_array[] = 1;
5936
            } else {
5937
                $my_final_array[] = 0;
5938
            }
5939
        }
5940
5941
        // Fix to remove the data of the user with my data
5942
        for ($i = 0; $i <= count($my_final_array); $i++) {
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...
5943
            if (!empty($my_final_array[$i])) {
5944
                $my_final_array[$i] = $final_array[$i] + 1; //Add my result
5945
                $final_array[$i] = 0;
5946
            }
5947
        }
5948
5949
        // Dataset definition
5950
        $dataSet = new pData();
5951
        $dataSet->addPoints($final_array, 'Serie1');
5952
        $dataSet->addPoints($my_final_array, 'Serie2');
5953
        $dataSet->normalize(100, "%");
5954
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true);
5955
5956
        // Cache definition
5957
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
5958
        $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
5959
        $chartHash = $myCache->getHash($dataSet);
5960
        if ($myCache->isInCache($chartHash)) {
5961
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
5962
            $myCache->saveFromCache($chartHash, $imgPath);
5963
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
5964
        } else {
5965
            /* Create the pChart object */
5966
            $widthSize = 80;
5967
            $heightSize = 35;
5968
            $fontSize = 2;
5969
5970
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
5971
5972
            /* Turn of Antialiasing */
5973
            $myPicture->Antialias = false;
5974
5975
            /* Add a border to the picture */
5976
            $myPicture->drawRectangle(
5977
                0,
5978
                0,
5979
                $widthSize - 1,
5980
                $heightSize - 1,
5981
                ['R' => 0, 'G' => 0, 'B' => 0]
5982
            );
5983
5984
            /* Set the default font */
5985
            $myPicture->setFontProperties(
5986
                [
5987
                    'FontName' => api_get_path(
5988
                            SYS_FONTS_PATH
5989
                        ).'opensans/OpenSans-Regular.ttf',
5990
                    'FontSize' => $fontSize,
5991
                ]
5992
            );
5993
5994
            /* Do not write the chart title */
5995
            /* Define the chart area */
5996
            $myPicture->setGraphArea(5, 5, $widthSize - 5, $heightSize - 5);
5997
5998
            /* Draw the scale */
5999
            $scaleSettings = [
6000
                'GridR' => 200,
6001
                'GridG' => 200,
6002
                'GridB' => 200,
6003
                'DrawSubTicks' => true,
6004
                'CycleBackground' => true,
6005
                'Mode' => SCALE_MODE_MANUAL,
6006
                'ManualScale' => [
6007
                    '0' => [
6008
                        'Min' => 0,
6009
                        'Max' => 100,
6010
                    ],
6011
                ],
6012
            ];
6013
            $myPicture->drawScale($scaleSettings);
6014
6015
            /* Turn on shadow computing */
6016
            $myPicture->setShadow(
6017
                true,
6018
                [
6019
                    'X' => 1,
6020
                    'Y' => 1,
6021
                    'R' => 0,
6022
                    'G' => 0,
6023
                    'B' => 0,
6024
                    'Alpha' => 10,
6025
                ]
6026
            );
6027
6028
            /* Draw the chart */
6029
            $myPicture->setShadow(
6030
                true,
6031
                [
6032
                    'X' => 1,
6033
                    'Y' => 1,
6034
                    'R' => 0,
6035
                    'G' => 0,
6036
                    'B' => 0,
6037
                    'Alpha' => 10,
6038
                ]
6039
            );
6040
            $settings = [
6041
                'DisplayValues' => true,
6042
                'DisplaySize' => $fontSize,
6043
                'DisplayR' => 0,
6044
                'DisplayG' => 0,
6045
                'DisplayB' => 0,
6046
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
6047
                'Gradient' => false,
6048
                'Surrounding' => 5,
6049
                'InnerSurrounding' => 5,
6050
            ];
6051
            $myPicture->drawStackedBarChart($settings);
6052
6053
            /* Save and write in cache */
6054
            $myCache->writeToCache($chartHash, $myPicture);
6055
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6056
            $myCache->saveFromCache($chartHash, $imgPath);
6057
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6058
        }
6059
6060
        return $imgPath;
6061
    }
6062
6063
    /**
6064
     * Generates a big graph with the number of best results.
6065
     *
6066
     * @param	array
6067
     */
6068
    public static function generate_exercise_result_graph($attempts)
6069
    {
6070
        $exercise_title = strip_tags($attempts['title']);
6071
        $attempts = $attempts['data'];
6072
        $my_exercise_result_array = $exercise_result = [];
6073
        if (empty($attempts)) {
6074
            return null;
6075
        }
6076
        foreach ($attempts as $attempt) {
6077
            if (api_get_user_id() == $attempt['exe_user_id']) {
6078
                if (0 != $attempt['max_score']) {
6079
                    $my_exercise_result_array[] = $attempt['score'] / $attempt['max_score'];
6080
                }
6081
            } else {
6082
                if (0 != $attempt['max_score']) {
6083
                    $exercise_result[] = $attempt['score'] / $attempt['max_score'];
6084
                }
6085
            }
6086
        }
6087
6088
        //Getting best result
6089
        rsort($my_exercise_result_array);
6090
        $my_exercise_result = 0;
6091
        if (isset($my_exercise_result_array[0])) {
6092
            $my_exercise_result = $my_exercise_result_array[0] * 100;
6093
        }
6094
6095
        $max = 100;
6096
        $pieces = 5;
6097
        $part = round($max / $pieces);
6098
        $x_axis = [];
6099
        $final_array = [];
6100
        $my_final_array = [];
6101
6102
        for ($i = 1; $i <= $pieces; $i++) {
6103
            $sum = 1;
6104
            if (1 == $i) {
6105
                $sum = 0;
6106
            }
6107
            $min = ($i - 1) * $part + $sum;
6108
            $max = ($i) * $part;
6109
            $x_axis[] = $min." - ".$max;
6110
            $count = 0;
6111
            foreach ($exercise_result as $result) {
6112
                $percentage = $result * 100;
6113
                if ($percentage >= $min && $percentage <= $max) {
6114
                    $count++;
6115
                }
6116
            }
6117
            $final_array[] = $count;
6118
6119
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
6120
                $my_final_array[] = 1;
6121
            } else {
6122
                $my_final_array[] = 0;
6123
            }
6124
        }
6125
6126
        //Fix to remove the data of the user with my data
6127
6128
        for ($i = 0; $i <= count($my_final_array); $i++) {
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...
6129
            if (!empty($my_final_array[$i])) {
6130
                $my_final_array[$i] = $final_array[$i] + 1; //Add my result
6131
                $final_array[$i] = 0;
6132
            }
6133
        }
6134
6135
        // Dataset definition
6136
        $dataSet = new pData();
6137
        $dataSet->addPoints($final_array, 'Serie1');
6138
        $dataSet->addPoints($my_final_array, 'Serie2');
6139
        $dataSet->addPoints($x_axis, 'Serie3');
6140
6141
        $dataSet->setSerieDescription('Serie1', get_lang('Score'));
6142
        $dataSet->setSerieDescription('Serie2', get_lang('My results'));
6143
        $dataSet->setAbscissa('Serie3');
6144
6145
        $dataSet->setXAxisName(get_lang('Score'));
6146
        $dataSet->normalize(100, "%");
6147
6148
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true);
6149
6150
        // Cache definition
6151
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
6152
        $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
6153
        $chartHash = $myCache->getHash($dataSet);
6154
6155
        if ($myCache->isInCache($chartHash)) {
6156
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6157
            $myCache->saveFromCache($chartHash, $imgPath);
6158
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6159
        } else {
6160
            /* Create the pChart object */
6161
            $widthSize = 480;
6162
            $heightSize = 250;
6163
            $fontSize = 8;
6164
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
6165
6166
            /* Turn of Antialiasing */
6167
            $myPicture->Antialias = false;
6168
6169
            /* Add a border to the picture */
6170
            $myPicture->drawRectangle(0, 0, $widthSize - 1, $heightSize - 1, ['R' => 0, 'G' => 0, 'B' => 0]);
6171
6172
            /* Set the default font */
6173
            $myPicture->setFontProperties(
6174
                [
6175
                    'FontName' => api_get_path(
6176
                            SYS_FONTS_PATH
6177
                        ).'opensans/OpenSans-Regular.ttf',
6178
                    'FontSize' => 10,
6179
                ]
6180
            );
6181
6182
            /* Write the chart title */
6183
            $myPicture->drawText(
6184
                250,
6185
                20,
6186
                $exercise_title,
6187
                [
6188
                    'FontSize' => 12,
6189
                    'Align' => TEXT_ALIGN_BOTTOMMIDDLE,
6190
                ]
6191
            );
6192
6193
            /* Define the chart area */
6194
            $myPicture->setGraphArea(50, 50, $widthSize - 20, $heightSize - 30);
6195
6196
            /* Draw the scale */
6197
            $scaleSettings = [
6198
                'GridR' => 200,
6199
                'GridG' => 200,
6200
                'GridB' => 200,
6201
                'DrawSubTicks' => true,
6202
                'CycleBackground' => true,
6203
                'Mode' => SCALE_MODE_MANUAL,
6204
                'ManualScale' => [
6205
                    '0' => [
6206
                        'Min' => 0,
6207
                        'Max' => 100,
6208
                    ],
6209
                ],
6210
            ];
6211
            $myPicture->drawScale($scaleSettings);
6212
6213
            /* Turn on shadow computing */
6214
            $myPicture->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]);
6215
6216
            /* Draw the chart */
6217
            $myPicture->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]);
6218
            $settings = [
6219
                'DisplayValues' => true,
6220
                'DisplaySize' => $fontSize,
6221
                'DisplayR' => 0,
6222
                'DisplayG' => 0,
6223
                'DisplayB' => 0,
6224
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
6225
                'Gradient' => false,
6226
                'Surrounding' => 30,
6227
                'InnerSurrounding' => 25,
6228
            ];
6229
            $myPicture->drawStackedBarChart($settings);
6230
6231
            $legendSettings = [
6232
                'Mode' => LEGEND_HORIZONTAL,
6233
                'Style' => LEGEND_NOBORDER,
6234
            ];
6235
            $myPicture->drawLegend($widthSize / 2, 30, $legendSettings);
6236
6237
            /* Write and save into cache */
6238
            $myCache->writeToCache($chartHash, $myPicture);
6239
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6240
            $myCache->saveFromCache($chartHash, $imgPath);
6241
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6242
        }
6243
6244
        return $imgPath;
6245
    }
6246
6247
    /**
6248
     * @param FormValidator $form
6249
     *
6250
     * @return mixed
6251
     */
6252
    public static function setUserSearchForm($form)
6253
    {
6254
        $form->addElement('text', 'keyword', get_lang('Keyword'));
6255
        $form->addSelect(
6256
            'active',
6257
            get_lang('Status'),
6258
            [1 => get_lang('active'), 0 => get_lang('inactive')]
6259
        );
6260
6261
        $form->addSelect(
6262
            'sleeping_days',
6263
            get_lang('Inactive days'),
6264
            [
6265
                '',
6266
                1 => 1,
6267
                5 => 5,
6268
                15 => 15,
6269
                30 => 30,
6270
                60 => 60,
6271
                90 => 90,
6272
                120 => 120,
6273
            ]
6274
        );
6275
6276
        $form->addButtonSearch(get_lang('Search'));
6277
6278
        return $form;
6279
    }
6280
6281
    /**
6282
     * Get the progress of a exercise.
6283
     *
6284
     * @param int    $sessionId  The session ID (session.id)
6285
     * @param int    $courseId   The course ID (course.id)
6286
     * @param int    $exerciseId The quiz ID (c_quiz.id)
6287
     * @param string $date_from
6288
     * @param string $date_to
6289
     * @param array  $options    An array of options you can pass to the query (limit, where and order)
6290
     *
6291
     * @return array An array with the data of exercise(s) progress
6292
     */
6293
    public static function get_exercise_progress(
6294
        $sessionId = 0,
6295
        $courseId = 0,
6296
        $exerciseId = 0,
6297
        $date_from = null,
6298
        $date_to = null,
6299
        $options = []
6300
    ) {
6301
        $sessionId = intval($sessionId);
6302
        $courseId = intval($courseId);
6303
        $exerciseId = intval($exerciseId);
6304
        $date_from = Database::escape_string($date_from);
6305
        $date_to = Database::escape_string($date_to);
6306
        /*
6307
         * This method gets the data by blocks, as previous attempts at one single
6308
         * query made it take ages. The logic of query division is described below
6309
         */
6310
        // Get tables names
6311
        $tuser = Database::get_main_table(TABLE_MAIN_USER);
6312
        $tquiz = Database::get_course_table(TABLE_QUIZ_TEST);
6313
        $tquiz_answer = Database::get_course_table(TABLE_QUIZ_ANSWER);
6314
        $tquiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION);
6315
        $tquiz_rel_question = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
6316
        $ttrack_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
6317
        $ttrack_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
6318
6319
        $sessions = [];
6320
        $courses = [];
6321
        // if session ID is defined but course ID is empty, get all the courses
6322
        // from that session
6323
        if (!empty($sessionId) && empty($courseId)) {
6324
            // $courses is an array of course int id as index and course details hash as value
6325
            $courses = SessionManager::get_course_list_by_session_id($sessionId);
6326
            $sessions[$sessionId] = api_get_session_info($sessionId);
6327
        } elseif (empty($sessionId) && !empty($courseId)) {
6328
            // if, to the contrary, course is defined but not sessions, get the sessions that include this course
6329
            // $sessions is an array like: [0] => ('id' => 3, 'title' => 'Session 35'), [1] => () etc;
6330
            $course = api_get_course_info_by_id($courseId);
6331
            $sessionsTemp = SessionManager::get_session_by_course($courseId);
6332
            $courses[$courseId] = $course;
6333
            foreach ($sessionsTemp as $sessionItem) {
6334
                $sessions[$sessionItem['id']] = $sessionItem;
6335
            }
6336
        } elseif (!empty($courseId) && !empty($sessionId)) {
6337
            //none is empty
6338
            $course = api_get_course_info_by_id($courseId);
6339
            $courses[$courseId] = [$course['code']];
6340
            $courses[$courseId]['code'] = $course['code'];
6341
            $sessions[$sessionId] = api_get_session_info($sessionId);
6342
        } else {
6343
            //both are empty, not enough data, return an empty array
6344
            return [];
6345
        }
6346
        // Now we have two arrays of courses and sessions with enough data to proceed
6347
        // If no course could be found, we shouldn't return anything.
6348
        // Course sessions can be empty (then we only return the pure-course-context results)
6349
        if (count($courses) < 1) {
6350
            return [];
6351
        }
6352
6353
        $data = [];
6354
        // The following loop is less expensive than what it seems:
6355
        // - if a course was defined, then we only loop through sessions
6356
        // - if a session was defined, then we only loop through courses
6357
        // - if a session and a course were defined, then we only loop once
6358
        foreach ($courses as $courseIdx => $courseData) {
6359
            $where = '';
6360
            $whereParams = [];
6361
            $whereSessionParams = '';
6362
            if (count($sessions > 0)) {
6363
                foreach ($sessions as $sessionIdx => $sessionData) {
6364
                    if (!empty($sessionIdx)) {
6365
                        $whereSessionParams .= $sessionIdx.',';
6366
                    }
6367
                }
6368
                $whereSessionParams = substr($whereSessionParams, 0, -1);
6369
            }
6370
6371
            if (!empty($exerciseId)) {
6372
                $exerciseId = intval($exerciseId);
6373
                $where .= ' AND q.iid = %d ';
6374
                $whereParams[] = $exerciseId;
6375
            }
6376
6377
            /*
6378
             * This feature has been disabled for now, to avoid having to
6379
             * join two very large tables
6380
            //2 = show all questions (wrong and correct answered)
6381
            if ($answer != 2) {
6382
                $answer = intval($answer);
6383
                //$where .= ' AND qa.correct = %d';
6384
                //$whereParams[] = $answer;
6385
            }
6386
            */
6387
6388
            $limit = '';
6389
            if (!empty($options['limit'])) {
6390
                $limit = " LIMIT ".$options['limit'];
6391
            }
6392
6393
            if (!empty($options['where'])) {
6394
                $where .= ' AND '.Database::escape_string($options['where']);
6395
            }
6396
6397
            $order = '';
6398
            if (!empty($options['order'])) {
6399
                $order = " ORDER BY ".$options['order'];
6400
            }
6401
6402
            if (!empty($date_to) && !empty($date_from)) {
6403
                $where .= sprintf(" AND (te.start_date BETWEEN '%s 00:00:00' AND '%s 23:59:59')", $date_from, $date_to);
6404
            }
6405
6406
            $sql = "SELECT
6407
                te.session_id,
6408
                ta.id as attempt_id,
6409
                te.exe_user_id as user_id,
6410
                te.exe_id as exercise_attempt_id,
6411
                ta.question_id,
6412
                ta.answer as answer_id,
6413
                ta.tms as time,
6414
                te.exe_exo_id as quiz_id,
6415
                CONCAT ('c', q.c_id, '_e', q.iid) as exercise_id,
6416
                q.title as quiz_title,
6417
                qq.description as description
6418
                FROM $ttrack_exercises te
6419
                INNER JOIN $ttrack_attempt ta
6420
                ON ta.exe_id = te.exe_id
6421
                INNER JOIN $tquiz q
6422
                ON q.iid = te.exe_exo_id
6423
                INNER JOIN $tquiz_rel_question rq
6424
                ON rq.quiz_id = q.iid AND rq.c_id = q.c_id
6425
                INNER JOIN $tquiz_question qq
6426
                ON
6427
                    qq.iid = rq.question_id AND
6428
                    qq.c_id = rq.c_id AND
6429
                    qq.position = rq.question_order AND
6430
                    ta.question_id = rq.question_id
6431
                WHERE
6432
                    te.c_id = $courseIdx ".(empty($whereSessionParams) ? '' : "AND te.session_id IN ($whereSessionParams)")."
6433
                    AND q.c_id = $courseIdx
6434
                    $where $order $limit";
6435
            $sql_query = vsprintf($sql, $whereParams);
6436
6437
            // Now browse through the results and get the data
6438
            $rs = Database::query($sql_query);
6439
            $userIds = [];
6440
            $questionIds = [];
6441
            $answerIds = [];
6442
            while ($row = Database::fetch_array($rs)) {
6443
                //only show if exercise is visible
6444
                if (api_get_item_visibility($courseData, 'quiz', $row['exercise_id'])) {
6445
                    $userIds[$row['user_id']] = $row['user_id'];
6446
                    $questionIds[$row['question_id']] = $row['question_id'];
6447
                    $answerIds[$row['question_id']][$row['answer_id']] = $row['answer_id'];
6448
                    $row['session'] = $sessions[$row['session_id']];
6449
                    $data[] = $row;
6450
                }
6451
            }
6452
            // Now fill questions data. Query all questions and answers for this test to avoid
6453
            $sqlQuestions = "SELECT tq.c_id, tq.iid as question_id, tq.question, tqa.iid,
6454
                            tqa.answer, tqa.correct, tq.position, tqa.iid as answer_id
6455
                            FROM $tquiz_question tq, $tquiz_answer tqa
6456
                            WHERE
6457
                                tqa.question_id = tq.iid AND
6458
                                tqa.c_id = tq.c_id AND
6459
                                tq.c_id = $courseIdx AND
6460
                                tq.iid IN (".implode(',', $questionIds).")";
6461
6462
            $resQuestions = Database::query($sqlQuestions);
6463
            $answer = [];
6464
            $question = [];
6465
            while ($rowQuestion = Database::fetch_assoc($resQuestions)) {
6466
                $questionId = $rowQuestion['question_id'];
6467
                $answerId = $rowQuestion['answer_id'];
6468
                $answer[$questionId][$answerId] = [
6469
                    'position' => $rowQuestion['position'],
6470
                    'question' => $rowQuestion['question'],
6471
                    'answer' => $rowQuestion['answer'],
6472
                    'correct' => $rowQuestion['correct'],
6473
                ];
6474
                $question[$questionId]['question'] = $rowQuestion['question'];
6475
            }
6476
6477
            // Now fill users data
6478
            $sqlUsers = "SELECT id as user_id, username, lastname, firstname
6479
                         FROM $tuser
6480
                         WHERE active <> ".USER_SOFT_DELETED." AND id IN (".implode(',', $userIds).")";
6481
            $resUsers = Database::query($sqlUsers);
6482
            while ($rowUser = Database::fetch_assoc($resUsers)) {
6483
                $users[$rowUser['user_id']] = $rowUser;
6484
            }
6485
6486
            foreach ($data as $id => $row) {
6487
                $rowQuestId = $row['question_id'];
6488
                $rowAnsId = $row['answer_id'];
6489
                $data[$id]['session'] = $sessions[$row['session_id']]['title'];
6490
                $data[$id]['firstname'] = $users[$row['user_id']]['firstname'];
6491
                $data[$id]['lastname'] = $users[$row['user_id']]['lastname'];
6492
                $data[$id]['username'] = $users[$row['user_id']]['username'];
6493
                $data[$id]['answer'] = $answer[$rowQuestId][$rowAnsId]['answer'];
6494
                $data[$id]['correct'] = (0 == $answer[$rowQuestId][$rowAnsId]['correct'] ? get_lang('No') : get_lang('Yes'));
6495
                $data[$id]['question'] = $question[$rowQuestId]['question'];
6496
                $data[$id]['question_id'] = $rowQuestId;
6497
                $data[$id]['description'] = $row['description'];
6498
            }
6499
6500
            /*
6501
            The minimum expected array structure at the end is:
6502
            attempt_id,
6503
            session title,
6504
            exercise_id,
6505
            quiz_title,
6506
            username,
6507
            lastname,
6508
            firstname,
6509
            time,
6510
            question_id,
6511
            question,
6512
            answer,
6513
            */
6514
        }
6515
6516
        return $data;
6517
    }
6518
6519
    /**
6520
     * @param string              $tool
6521
     * @param SessionEntity |null $session
6522
     *
6523
     * @return CStudentPublication|null
6524
     */
6525
    public static function getLastStudentPublication(
6526
        User $user,
6527
        $tool,
6528
        Course $course,
6529
        SessionEntity $session = null
6530
    ) {
6531
        // @todo
6532
        return null;
6533
6534
        return Database::getManager()
0 ignored issues
show
Unused Code introduced by
return Database::getMana...)->getOneOrNullResult() is not reachable.

This check looks for unreachable code. It uses sophisticated control flow analysis techniques to find statements which will never be executed.

Unreachable code is most often the result of return, die or exit statements that have been added for debug purposes.

function fx() {
    try {
        doSomething();
        return true;
    }
    catch (\Exception $e) {
        return false;
    }

    return false;
}

In the above example, the last return false will never be executed, because a return statement has already been met in every possible execution path.

Loading history...
6535
            ->createQuery("
6536
                SELECT csp
6537
                FROM ChamiloCourseBundle:CStudentPublication csp
6538
                INNER JOIN ChamiloCourseBundle:CItemProperty cip
6539
                    WITH (
6540
                        csp.iid = cip.ref AND
6541
                        csp.session = cip.session AND
6542
                        csp.cId = cip.course AND
6543
                        csp.userId = cip.lasteditUserId
6544
                    )
6545
                WHERE
6546
                    cip.session = :session AND cip.course = :course AND cip.lasteditUserId = :user AND cip.tool = :tool
6547
                ORDER BY csp.iid DESC
6548
            ")
6549
            ->setMaxResults(1)
6550
            ->setParameters([
6551
                'tool' => $tool,
6552
                'session' => $session,
6553
                'course' => $course,
6554
                'user' => $user,
6555
            ])
6556
            ->getOneOrNullResult();
6557
    }
6558
6559
    /**
6560
     * Get the HTML code for show a block with the achieved user skill on course/session.
6561
     *
6562
     * @param int  $userId
6563
     * @param int  $courseId
6564
     * @param int  $sessionId
6565
     * @param bool $forceView forces the view of the skills, not checking for deeper access
6566
     *
6567
     * @return string
6568
     */
6569
    public static function displayUserSkills($userId, $courseId = 0, $sessionId = 0, $forceView = false)
6570
    {
6571
        if (false === SkillModel::isAllowed($userId, false) && false == $forceView) {
6572
            return '';
6573
        }
6574
        $skillManager = new SkillModel();
6575
6576
        return $skillManager->getUserSkillsTable($userId, $courseId, $sessionId)['table'];
6577
    }
6578
6579
    /**
6580
     * @param int $userId
6581
     * @param int $courseId
6582
     * @param int $sessionId
6583
     *
6584
     * @return array
6585
     */
6586
    public static function getCalculateTime($userId, $courseId, $sessionId)
6587
    {
6588
        $userId = (int) $userId;
6589
        $courseId = (int) $courseId;
6590
        $sessionId = (int) $sessionId;
6591
6592
        if (empty($userId) || empty($courseId)) {
6593
            return [];
6594
        }
6595
6596
        $sql = "SELECT MIN(date_reg) min, MAX(date_reg) max
6597
                FROM track_e_access_complete
6598
                WHERE
6599
                    user_id = $userId AND
6600
                    c_id = $courseId AND
6601
                    session_id = $sessionId AND
6602
                    login_as = 0
6603
                ORDER BY date_reg ASC
6604
                LIMIT 1";
6605
        $rs = Database::query($sql);
6606
6607
        $firstConnection = '';
6608
        $lastConnection = '';
6609
        if (Database::num_rows($rs) > 0) {
6610
            $value = Database::fetch_array($rs);
6611
            $firstConnection = $value['min'];
6612
            $lastConnection = $value['max'];
6613
        }
6614
6615
        $sql = "SELECT * FROM track_e_access_complete
6616
                WHERE
6617
                    user_id = $userId AND
6618
                    c_id = $courseId AND
6619
                    session_id = $sessionId AND
6620
                    login_as = 0 AND current_id <> 0";
6621
6622
        $res = Database::query($sql);
6623
        $reg = [];
6624
        while ($row = Database::fetch_assoc($res)) {
6625
            $reg[$row['id']] = $row;
6626
            $reg[$row['id']]['date_reg'] = strtotime($row['date_reg']);
6627
        }
6628
6629
        $sessions = [];
6630
        foreach ($reg as $key => $value) {
6631
            $sessions[$value['current_id']][$value['tool']][] = $value;
6632
        }
6633
6634
        $quizTime = 0;
6635
        $result = [];
6636
        $totalTime = 0;
6637
        $lpTime = [];
6638
        $lpDetailTime = [];
6639
        foreach ($sessions as $listPerTool) {
6640
            $min = 0;
6641
            $max = 0;
6642
            $sessionDiff = 0;
6643
            foreach ($listPerTool as $tool => $results) {
6644
                $beforeItem = [];
6645
                foreach ($results as $item) {
6646
                    if (empty($beforeItem)) {
6647
                        $beforeItem = $item;
6648
                        if (empty($min)) {
6649
                            $min = $item['date_reg'];
6650
                        }
6651
6652
                        if (empty($max)) {
6653
                            $max = $item['date_reg'];
6654
                        }
6655
                        continue;
6656
                    }
6657
6658
                    $partialTime = $item['date_reg'] - $beforeItem['date_reg'];
6659
                    if ($item['date_reg'] > $max) {
6660
                        $max = $item['date_reg'];
6661
                    }
6662
6663
                    if (empty($min)) {
6664
                        $min = $item['date_reg'];
6665
                    }
6666
6667
                    if ($item['date_reg'] < $min) {
6668
                        $min = $item['date_reg'];
6669
                    }
6670
6671
                    switch ($tool) {
6672
                        case TOOL_AGENDA:
6673
                        case TOOL_FORUM:
6674
                        case TOOL_ANNOUNCEMENT:
6675
                        case TOOL_COURSE_DESCRIPTION:
6676
                        case TOOL_SURVEY:
6677
                        case TOOL_NOTEBOOK:
6678
                        case TOOL_GRADEBOOK:
6679
                        case TOOL_DROPBOX:
6680
                        case 'Reports':
6681
                        case 'Videoconference':
6682
                        case TOOL_LINK:
6683
                        case TOOL_CHAT:
6684
                        case 'course-main':
6685
                            if (!isset($result[$tool])) {
6686
                                $result[$tool] = 0;
6687
                            }
6688
                            $result[$tool] += $partialTime;
6689
                            break;
6690
                        case TOOL_LEARNPATH:
6691
                            if ($item['tool_id'] != $beforeItem['tool_id']) {
6692
                                break;
6693
                            }
6694
                            if (!isset($lpTime[$item['tool_id']])) {
6695
                                $lpTime[$item['tool_id']] = 0;
6696
                            }
6697
6698
                            // Saving the attempt id "action_details"
6699
                            if (!empty($item['tool_id'])) {
6700
                                if (!empty($item['tool_id_detail'])) {
6701
                                    if (!isset($lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']])) {
6702
                                        $lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']] = 0;
6703
                                    }
6704
                                    $lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']] += $partialTime;
6705
                                }
6706
                                $lpTime[$item['tool_id']] += $partialTime;
6707
                            }
6708
                            break;
6709
                        case TOOL_QUIZ:
6710
                            if (!isset($lpTime[$item['action_details']])) {
6711
                                $lpTime[$item['action_details']] = 0;
6712
                            }
6713
                            if ('learnpath_id' === $beforeItem['action']) {
6714
                                $lpTime[$item['action_details']] += $partialTime;
6715
                            } else {
6716
                                $quizTime += $partialTime;
6717
                            }
6718
                            break;
6719
                    }
6720
                    $beforeItem = $item;
6721
                }
6722
            }
6723
6724
            $sessionDiff += $max - $min;
6725
            if ($sessionDiff > 0) {
6726
                $totalTime += $sessionDiff;
6727
            }
6728
        }
6729
6730
        $totalLp = 0;
6731
        foreach ($lpTime as $value) {
6732
            $totalLp += $value;
6733
        }
6734
6735
        $result['learnpath_detailed'] = $lpDetailTime;
6736
        $result[TOOL_LEARNPATH] = $lpTime;
6737
        $result[TOOL_QUIZ] = $quizTime;
6738
        $result['total_learnpath'] = $totalLp;
6739
        $result['total_time'] = $totalTime;
6740
        $result['number_connections'] = count($sessions);
6741
        $result['first'] = $firstConnection;
6742
        $result['last'] = $lastConnection;
6743
6744
        return $result;
6745
    }
6746
6747
    /**
6748
     * Gets the IP of a given user, using the last login before the given date.
6749
     *
6750
     * @param int User ID
6751
     * @param string Datetime
6752
     * @param bool Whether to return the IP as a link or just as an IP
6753
     * @param string If defined and return_as_link if true, will be used as the text to be shown as the link
6754
     *
6755
     * @return string IP address (or false on error)
6756
     * @assert (0,0) === false
6757
     */
6758
    public static function get_ip_from_user_event(
6759
        $user_id,
6760
        $event_date,
6761
        $return_as_link = false,
6762
        $body_replace = null
6763
    ) {
6764
        if (empty($user_id) || empty($event_date)) {
6765
            return false;
6766
        }
6767
        $user_id = intval($user_id);
6768
        $event_date = Database::escape_string($event_date);
6769
        $table_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
6770
        $sql_ip = "SELECT login_date, user_ip
6771
                   FROM $table_login
6772
                   WHERE login_user_id = $user_id AND login_date < '$event_date'
6773
                   ORDER BY login_date DESC LIMIT 1";
6774
        $ip = '';
6775
        $res_ip = Database::query($sql_ip);
6776
        if (false !== $res_ip && Database::num_rows($res_ip) > 0) {
6777
            $row_ip = Database::fetch_row($res_ip);
6778
            if ($return_as_link) {
6779
                $ip = Display::url(
6780
                    (empty($body_replace) ? $row_ip[1] : $body_replace),
6781
                    'http://www.whatsmyip.org/ip-geo-location/?ip='.$row_ip[1],
6782
                    ['title' => get_lang('Trace IP'), 'target' => '_blank']
6783
                );
6784
            } else {
6785
                $ip = $row_ip[1];
6786
            }
6787
        }
6788
6789
        return $ip;
6790
    }
6791
6792
    /**
6793
     * @param int   $userId
6794
     * @param array $courseInfo
6795
     * @param int   $sessionId
6796
     *
6797
     * @return array
6798
     */
6799
    public static function getToolInformation(
6800
        $userId,
6801
        $courseInfo,
6802
        $sessionId = 0
6803
    ) {
6804
        $csvContent = [];
6805
        $courseToolInformation = '';
6806
        $headerTool = [
6807
            [get_lang('Title')],
6808
            [get_lang('Created at')],
6809
            [get_lang('Updated at')],
6810
        ];
6811
6812
        $headerListForCSV = [];
6813
        foreach ($headerTool as $item) {
6814
            $headerListForCSV[] = $item[0];
6815
        }
6816
6817
        $courseForumInformationArray = getForumCreatedByUser(
6818
            $userId,
6819
            $courseInfo,
6820
            $sessionId
6821
        );
6822
6823
        if (!empty($courseForumInformationArray)) {
6824
            $csvContent[] = [];
6825
            $csvContent[] = [get_lang('Forums')];
6826
            $csvContent[] = $headerListForCSV;
6827
            foreach ($courseForumInformationArray as $row) {
6828
                $csvContent[] = $row;
6829
            }
6830
6831
            $courseToolInformation .= Display::page_subheader2(
6832
                get_lang('Forums')
6833
            );
6834
            $courseToolInformation .= Display::return_sortable_table(
6835
                $headerTool,
6836
                $courseForumInformationArray
6837
            );
6838
        }
6839
6840
        $courseWorkInformationArray = getWorkCreatedByUser(
6841
            $userId,
6842
            $courseInfo['real_id'],
6843
            $sessionId
6844
        );
6845
6846
        if (!empty($courseWorkInformationArray)) {
6847
            $csvContent[] = null;
6848
            $csvContent[] = [get_lang('Assignments')];
6849
            $csvContent[] = $headerListForCSV;
6850
6851
            foreach ($courseWorkInformationArray as $row) {
6852
                $csvContent[] = $row;
6853
            }
6854
            $csvContent[] = null;
6855
6856
            $courseToolInformation .= Display::page_subheader2(
6857
                get_lang('Assignments')
6858
            );
6859
            $courseToolInformation .= Display::return_sortable_table(
6860
                $headerTool,
6861
                $courseWorkInformationArray
6862
            );
6863
        }
6864
6865
        $courseToolInformationTotal = null;
6866
        if (!empty($courseToolInformation)) {
6867
            $sessionTitle = null;
6868
            if (!empty($sessionId)) {
6869
                $sessionTitle = ' ('.api_get_session_name($sessionId).')';
6870
            }
6871
6872
            $courseToolInformationTotal .= Display::page_subheader(
6873
                $courseInfo['title'].$sessionTitle
6874
            );
6875
            $courseToolInformationTotal .= $courseToolInformation;
6876
        }
6877
6878
        return [
6879
            'array' => $csvContent,
6880
            'html' => $courseToolInformationTotal,
6881
        ];
6882
    }
6883
6884
    /**
6885
     * @param int $sessionId
6886
     *
6887
     * @return bool
6888
     */
6889
    public static function isAllowToTrack($sessionId)
6890
    {
6891
        return
6892
            api_is_platform_admin(true, true) ||
6893
            (!empty($sessionId) && api_get_session_entity($sessionId)->hasUserAsGeneralCoach(api_get_user_entity())) ||
6894
            api_is_allowed_to_create_course() ||
6895
            api_is_course_tutor() ||
6896
            api_is_course_admin();
6897
    }
6898
6899
    public static function getCourseLpProgress($userId, $sessionId)
6900
    {
6901
        $controller = new IndexManager(get_lang('MyCourses'));
6902
        $data = $controller->returnCoursesAndSessions($userId);
6903
        $courseList = $data['courses'];
6904
        $result = [];
6905
        if ($courseList) {
6906
            //$counter = 1;
6907
            foreach ($courseList as $course) {
6908
                $courseId = $course['course_id'];
6909
                $courseInfo = api_get_course_info_by_id($courseId);
6910
                if (empty($courseInfo)) {
6911
                    continue;
6912
                }
6913
                $courseCode = $courseInfo['code'];
6914
                $lpTimeList = self::getCalculateTime($userId, $courseId, $sessionId);
6915
6916
                // total progress
6917
                $list = new LearnpathList(
6918
                    $userId,
6919
                     $courseInfo,
6920
                    0,
6921
                    'resource.publishedOn ASC',
6922
                    true,
6923
                    null,
6924
                    true
6925
                );
6926
6927
                $list = $list->get_flat_list();
6928
                $totalProgress = 0;
6929
                $totalTime = 0;
6930
                if (!empty($list)) {
6931
                    foreach ($list as $lp_id => $learnpath) {
6932
                        if (!$learnpath['lp_visibility']) {
6933
                            continue;
6934
                        }
6935
                        $lpProgress = self::get_avg_student_progress($userId, $courseCode, [$lp_id], $sessionId);
6936
                        $time = isset($lpTimeList[TOOL_LEARNPATH][$lp_id]) ? $lpTimeList[TOOL_LEARNPATH][$lp_id] : 0;
6937
                        if (100 == $lpProgress) {
6938
                            if (!empty($time)) {
6939
                                $timeInMinutes = $time / 60;
6940
                                $min = (int) learnpath::getAccumulateWorkTimePrerequisite($lp_id, $courseId);
6941
                                if ($timeInMinutes >= $min) {
6942
                                    $totalProgress++;
6943
                                }
6944
                            }
6945
                        }
6946
                        $totalTime += $time;
6947
                    }
6948
6949
                    if (!empty($totalProgress)) {
6950
                        $totalProgress = (float) api_number_format($totalProgress / count($list) * 100, 2);
6951
                    }
6952
                }
6953
6954
                $progress = self::get_avg_student_progress($userId, $courseCode, [], $sessionId);
6955
6956
                $result[] = [
6957
                    'module' => $courseInfo['name'],
6958
                    'progress' => $progress,
6959
                    'qualification' => $totalProgress,
6960
                    'activeTime' => $totalTime,
6961
                ];
6962
            }
6963
        }
6964
6965
        return $result;
6966
    }
6967
6968
    /**
6969
     * @param int $userId
6970
     * @param int $courseId
6971
     * @param int $sessionId
6972
     *
6973
     * @return int
6974
     */
6975
    public static function getNumberOfCourseAccessDates($userId, $courseId, $sessionId)
6976
    {
6977
        $tblTrackCourseAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
6978
        $sessionCondition = api_get_session_condition($sessionId);
6979
        $courseId = (int) $courseId;
6980
        $userId = (int) $userId;
6981
6982
        $sql = "SELECT COUNT(DISTINCT (DATE(login_course_date))) AS c
6983
            FROM $tblTrackCourseAccess
6984
            WHERE c_id = $courseId $sessionCondition AND user_id = $userId";
6985
6986
        $result = Database::fetch_assoc(Database::query($sql));
6987
6988
        return (int) $result['c'];
6989
    }
6990
6991
    public static function processUserDataMove(
6992
        $user_id,
6993
        $course_info,
6994
        $origin_session_id,
6995
        $new_session_id,
6996
        $update_database,
6997
        $debug = false
6998
    ) {
6999
        // Begin with the import process
7000
        $origin_course_code = $course_info['code'];
7001
        $course_id = $course_info['real_id'];
7002
        $user_id = (int) $user_id;
7003
        $origin_session_id = (int) $origin_session_id;
7004
        $new_session_id = (int) $new_session_id;
7005
        $session = api_get_session_entity($new_session_id);
7006
        $em = Database::getManager();
7007
7008
        $TABLETRACK_EXERCICES = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
7009
        $TBL_TRACK_ATTEMPT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
7010
        $TBL_TRACK_E_COURSE_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
7011
        $TBL_TRACK_E_LAST_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
7012
        $TBL_LP_VIEW = Database::get_course_table(TABLE_LP_VIEW);
7013
        $TBL_NOTEBOOK = Database::get_course_table(TABLE_NOTEBOOK);
7014
        $TBL_STUDENT_PUBLICATION = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
7015
        $TBL_STUDENT_PUBLICATION_ASSIGNMENT = Database::get_course_table(TABLE_STUDENT_PUBLICATION_ASSIGNMENT);
7016
        $TBL_ITEM_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
7017
7018
        $TBL_DROPBOX_FILE = Database::get_course_table(TABLE_DROPBOX_FILE);
7019
        $TBL_DROPBOX_POST = Database::get_course_table(TABLE_DROPBOX_POST);
7020
        $TBL_AGENDA = Database::get_course_table(TABLE_AGENDA);
7021
7022
        //1. track_e_exercises
7023
        //ORIGINAL COURSE
7024
        $sessionCondition = api_get_session_condition($origin_session_id);
7025
        $sql = "SELECT * FROM $TABLETRACK_EXERCICES
7026
                WHERE c_id = $course_id AND exe_user_id = $user_id  $sessionCondition";
7027
        $res = Database::query($sql);
7028
        $list = [];
7029
        while ($row = Database::fetch_assoc($res)) {
7030
            $list[$row['exe_id']] = $row;
7031
        }
7032
7033
        $result_message = [];
7034
        $result_message_compare = [];
7035
        if (!empty($list)) {
7036
            foreach ($list as $exe_id => $data) {
7037
                if ($update_database) {
7038
                    $sql = "UPDATE $TABLETRACK_EXERCICES SET session_id = '$new_session_id' WHERE exe_id = $exe_id";
7039
                    Database::query($sql);
7040
7041
                    //$sql = "UPDATE $TBL_TRACK_ATTEMPT SET session_id = '$new_session_id' WHERE exe_id = $exe_id";
7042
                    //Database::query($sql);
7043
7044
                    $repoTrackQualify = $em->getRepository(TrackEAttemptQualify::class);
7045
                    /** @var TrackEAttemptQualify $trackQualify */
7046
                    $trackQualify = $repoTrackQualify->findBy([
7047
                        'exeId' => $exe_id
7048
                    ]);
7049
                    if ($trackQualify) {
7050
                        $trackQualify->setSessionId($new_session_id);
7051
                        $em->persist($trackQualify);
7052
                        $em->flush();
7053
                    }
7054
7055
                    if (!isset($result_message[$TABLETRACK_EXERCICES])) {
7056
                        $result_message[$TABLETRACK_EXERCICES] = 0;
7057
                    }
7058
                    $result_message[$TABLETRACK_EXERCICES]++;
7059
                } else {
7060
                    if (!empty($data['orig_lp_id']) && !empty($data['orig_lp_item_id'])) {
7061
                        $result_message['TRACK_E_EXERCISES'][$exe_id] = $data;
7062
                    } else {
7063
                        $result_message['TRACK_E_EXERCISES_IN_LP'][$exe_id] = $data;
7064
                    }
7065
                }
7066
            }
7067
        }
7068
7069
        // DESTINY COURSE
7070
        if (!$update_database) {
7071
            $sql = "SELECT * FROM $TABLETRACK_EXERCICES
7072
                    WHERE
7073
                        c_id = $course_id AND
7074
                        session_id = $new_session_id AND
7075
                        exe_user_id = $user_id ";
7076
            $res = Database::query($sql);
7077
            $list = [];
7078
            while ($row = Database::fetch_assoc($res)) {
7079
                $list[$row['exe_id']] = $row;
7080
            }
7081
7082
            if (!empty($list)) {
7083
                foreach ($list as $exe_id => $data) {
7084
                    if ($update_database) {
7085
                        $sql = "UPDATE $TABLETRACK_EXERCICES
7086
                                SET session_id = '$new_session_id'
7087
                                WHERE exe_id = $exe_id";
7088
                        Database::query($sql);
7089
                        $result_message[$TABLETRACK_EXERCICES]++;
7090
                    } else {
7091
                        if (!empty($data['orig_lp_id']) && !empty($data['orig_lp_item_id'])) {
7092
                            $result_message_compare['TRACK_E_EXERCISES'][$exe_id] = $data;
7093
                        } else {
7094
                            $result_message_compare['TRACK_E_EXERCISES_IN_LP'][$exe_id] = $data;
7095
                        }
7096
                    }
7097
                }
7098
            }
7099
        }
7100
7101
        // 2.track_e_attempt, track_e_attempt_recording, track_e_downloads
7102
        // Nothing to do because there are not relationship with a session
7103
        // 3. track_e_course_access
7104
        $sql = "SELECT * FROM $TBL_TRACK_E_COURSE_ACCESS
7105
                WHERE c_id = $course_id AND session_id = $origin_session_id  AND user_id = $user_id ";
7106
        $res = Database::query($sql);
7107
        $list = [];
7108
        while ($row = Database::fetch_assoc($res)) {
7109
            $list[$row['course_access_id']] = $row;
7110
        }
7111
7112
        if (!empty($list)) {
7113
            foreach ($list as $id => $data) {
7114
                if ($update_database) {
7115
                    $sql = "UPDATE $TBL_TRACK_E_COURSE_ACCESS
7116
                            SET session_id = $new_session_id
7117
                            WHERE course_access_id = $id";
7118
                    if ($debug) {
7119
                        echo $sql;
7120
                    }
7121
                    Database::query($sql);
7122
                    if (!isset($result_message[$TBL_TRACK_E_COURSE_ACCESS])) {
7123
                        $result_message[$TBL_TRACK_E_COURSE_ACCESS] = 0;
7124
                    }
7125
                    $result_message[$TBL_TRACK_E_COURSE_ACCESS]++;
7126
                }
7127
            }
7128
        }
7129
7130
        // 4. track_e_lastaccess
7131
        $sql = "SELECT access_id FROM $TBL_TRACK_E_LAST_ACCESS
7132
                WHERE
7133
                    c_id = $course_id AND
7134
                    session_id = $origin_session_id AND
7135
                    access_user_id = $user_id ";
7136
        $res = Database::query($sql);
7137
        $list = [];
7138
        while ($row = Database::fetch_assoc($res)) {
7139
            $list[] = $row['access_id'];
7140
        }
7141
7142
        if (!empty($list)) {
7143
            foreach ($list as $id) {
7144
                if ($update_database) {
7145
                    $sql = "UPDATE $TBL_TRACK_E_LAST_ACCESS
7146
                            SET session_id = $new_session_id
7147
                            WHERE access_id = $id";
7148
                    if ($debug) {
7149
                        echo $sql;
7150
                    }
7151
                    Database::query($sql);
7152
                    if (!isset($result_message[$TBL_TRACK_E_LAST_ACCESS])) {
7153
                        $result_message[$TBL_TRACK_E_LAST_ACCESS] = 0;
7154
                    }
7155
                    $result_message[$TBL_TRACK_E_LAST_ACCESS]++;
7156
                }
7157
            }
7158
        }
7159
7160
        // 5. lp_item_view
7161
        // CHECK ORIGIN
7162
        $sql = "SELECT * FROM $TBL_LP_VIEW
7163
                WHERE user_id = $user_id AND session_id = $origin_session_id AND c_id = $course_id ";
7164
        $res = Database::query($sql);
7165
7166
        // Getting the list of LPs in the new session
7167
        $lp_list = new LearnpathList($user_id, $course_info, $new_session_id);
7168
        $flat_list = $lp_list->get_flat_list();
7169
        $list = [];
7170
        while ($row = Database::fetch_assoc($res)) {
7171
            // Checking if the LP exist in the new session
7172
            //if (in_array($row['lp_id'], array_keys($flat_list))) {
7173
            $list[$row['id']] = $row;
7174
            //}
7175
        }
7176
7177
        if (!empty($list)) {
7178
            foreach ($list as $id => $data) {
7179
                if ($update_database) {
7180
                    $sql = "UPDATE $TBL_LP_VIEW
7181
                            SET session_id = $new_session_id
7182
                            WHERE c_id = $course_id AND iid = $id ";
7183
                    if ($debug) {
7184
                        var_dump($sql);
0 ignored issues
show
Security Debugging Code introduced by
var_dump($sql) looks like debug code. Are you sure you do not want to remove it?
Loading history...
7185
                    }
7186
                    $res = Database::query($sql);
7187
                    if ($debug) {
7188
                        var_dump($res);
7189
                    }
7190
                    if (!isset($result_message[$TBL_LP_VIEW])) {
7191
                        $result_message[$TBL_LP_VIEW] = 0;
7192
                    }
7193
                    $result_message[$TBL_LP_VIEW]++;
7194
                } else {
7195
                    // Getting all information of that lp_item_id
7196
                    $score = self::get_avg_student_score(
7197
                        $user_id,
7198
                        $origin_course_code,
7199
                        [$data['lp_id']],
7200
                        $origin_session_id
7201
                    );
7202
                    $progress = self::get_avg_student_progress(
7203
                        $user_id,
7204
                        $origin_course_code,
7205
                        [$data['lp_id']],
7206
                        $origin_session_id
7207
                    );
7208
                    $result_message['LP_VIEW'][$data['lp_id']] = [
7209
                        'score' => $score,
7210
                        'progress' => $progress,
7211
                    ];
7212
                }
7213
            }
7214
        }
7215
7216
        // Check destination.
7217
        if (!$update_database) {
7218
            $sql = "SELECT * FROM $TBL_LP_VIEW
7219
                    WHERE user_id = $user_id AND session_id = $new_session_id AND c_id = $course_id";
7220
            $res = Database::query($sql);
7221
7222
            // Getting the list of LPs in the new session
7223
            $lp_list = new LearnpathList($user_id, $course_info, $new_session_id);
7224
            $flat_list = $lp_list->get_flat_list();
7225
7226
            $list = [];
7227
            while ($row = Database::fetch_assoc($res)) {
7228
                //Checking if the LP exist in the new session
7229
                //if (in_array($row['lp_id'], array_keys($flat_list))) {
7230
                $list[$row['id']] = $row;
7231
                //}
7232
            }
7233
7234
            if (!empty($list)) {
7235
                foreach ($list as $id => $data) {
7236
                    // Getting all information of that lp_item_id
7237
                    $score = self::get_avg_student_score(
7238
                        $user_id,
7239
                        $origin_course_code,
7240
                        [$data['lp_id']],
7241
                        $new_session_id
7242
                    );
7243
                    $progress = self::get_avg_student_progress(
7244
                        $user_id,
7245
                        $origin_course_code,
7246
                        [$data['lp_id']],
7247
                        $new_session_id
7248
                    );
7249
                    $result_message_compare['LP_VIEW'][$data['lp_id']] = [
7250
                        'score' => $score,
7251
                        'progress' => $progress,
7252
                    ];
7253
                }
7254
            }
7255
        }
7256
7257
        // 6. Agenda
7258
        // calendar_event_attachment no problems no session_id
7259
        $sql = "SELECT ref FROM $TBL_ITEM_PROPERTY
7260
                WHERE tool = 'calendar_event' AND insert_user_id = $user_id AND c_id = $course_id ";
7261
        $res = Database::query($sql);
7262
        while ($row = Database::fetch_assoc($res)) {
7263
            $id = $row['ref'];
7264
            if ($update_database) {
7265
                $sql = "UPDATE $TBL_AGENDA SET session_id = $new_session_id WHERE c_id = $course_id AND iid = $id ";
7266
                if ($debug) {
7267
                    var_dump($sql);
7268
                }
7269
                $res_update = Database::query($sql);
7270
                if ($debug) {
7271
                    var_dump($res_update);
7272
                }
7273
                if (!isset($result_message['agenda'])) {
7274
                    $result_message['agenda'] = 0;
7275
                }
7276
                $result_message['agenda']++;
7277
            }
7278
        }
7279
7280
        // 7. Forum ?? So much problems when trying to import data
7281
        // 8. Student publication - Works
7282
        $sql = "SELECT ref FROM $TBL_ITEM_PROPERTY
7283
                WHERE tool = 'work' AND insert_user_id = $user_id AND c_id = $course_id";
7284
        if ($debug) {
7285
            echo $sql;
7286
        }
7287
        $res = Database::query($sql);
7288
        while ($row = Database::fetch_assoc($res)) {
7289
            $id = $row['ref'];
7290
            $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION
7291
                    WHERE iid = $id AND session_id = $origin_session_id AND c_id = $course_id";
7292
            $sub_res = Database::query($sql);
7293
            if (Database::num_rows($sub_res) > 0) {
7294
                $data = Database::fetch_assoc($sub_res);
7295
                if ($debug) {
7296
                    var_dump($data);
7297
                }
7298
                $parent_id = $data['parent_id'];
7299
                if (isset($data['parent_id']) && !empty($data['parent_id'])) {
7300
                    $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION
7301
                            WHERE iid = $parent_id AND c_id = $course_id";
7302
                    $select_res = Database::query($sql);
7303
                    $parent_data = Database::fetch_assoc($select_res);
7304
                    if ($debug) {
7305
                        var_dump($parent_data);
7306
                    }
7307
7308
                    $sys_course_path = api_get_path(SYS_COURSE_PATH);
7309
                    $course_dir = $sys_course_path.$course_info['path'];
7310
                    $base_work_dir = $course_dir.'/work';
7311
7312
                    // Creating the parent folder in the session if does not exists already
7313
                    //@todo ugly fix
7314
                    $search_this = "folder_moved_from_session_id_$origin_session_id";
7315
                    $search_this2 = $parent_data['url'];
7316
                    $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION
7317
                            WHERE description like '%$search_this%' AND
7318
                                  url LIKE '%$search_this2%' AND
7319
                                  session_id = $new_session_id AND
7320
                                  c_id = $course_id
7321
                            ORDER BY id desc  LIMIT 1";
7322
                    if ($debug) {
7323
                        echo $sql;
7324
                    }
7325
                    $sub_res = Database::query($sql);
7326
                    $num_rows = Database::num_rows($sub_res);
7327
7328
                    $new_parent_id = 0;
7329
                    if ($num_rows > 0) {
7330
                        $new_result = Database::fetch_assoc($sub_res);
7331
                        $created_dir = $new_result['url'];
7332
                        $new_parent_id = $new_result['id'];
7333
                    } else {
7334
                        if ($update_database) {
7335
                            $dir_name = substr($parent_data['url'], 1);
7336
                            $created_dir = create_unexisting_work_directory($base_work_dir, $dir_name);
7337
                            $created_dir = '/'.$created_dir;
7338
                            $now = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
7339
                            // Creating directory
7340
                            $publication = (new CStudentPublication())
7341
                                ->setTitle($parent_data['title'])
7342
                                ->setDescription(
7343
                                    $parent_data['description']."folder_moved_from_session_id_$origin_session_id"
7344
                                )
7345
                                ->setActive(false)
7346
                                ->setAccepted(true)
7347
                                ->setPostGroupId(0)
7348
                                ->setHasProperties($parent_data['has_properties'])
7349
                                ->setWeight($parent_data['weight'])
7350
                                ->setContainsFile($parent_data['contains_file'])
7351
                                ->setFiletype('folder')
7352
                                ->setSentDate($now)
7353
                                ->setQualification($parent_data['qualification'])
7354
                                ->setParentId(0)
7355
                                ->setQualificatorId(0)
7356
                                ->setUserId($parent_data['user_id'])
7357
                                ->setAllowTextAssignment($parent_data['allow_text_assignment'])
7358
                                ->setSession($session);
7359
7360
                            $publication->setDocumentId($parent_data['document_id']);
7361
7362
                            Database::getManager()->persist($publication);
7363
                            Database::getManager()->flush();
7364
                            $id = $publication->getIid();
7365
                            //Folder created
7366
                            //api_item_property_update($course_info, 'work', $id, 'DirectoryCreated', api_get_user_id());
7367
                            $new_parent_id = $id;
7368
                            if (!isset($result_message[$TBL_STUDENT_PUBLICATION.' - new folder created called: '.$created_dir])) {
7369
                                $result_message[$TBL_STUDENT_PUBLICATION.' - new folder created called: '.$created_dir] = 0;
7370
                            }
7371
                            $result_message[$TBL_STUDENT_PUBLICATION.' - new folder created called: '.$created_dir]++;
7372
                        }
7373
                    }
7374
7375
                    //Creating student_publication_assignment if exists
7376
                    $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION_ASSIGNMENT
7377
                            WHERE publication_id = $parent_id AND c_id = $course_id";
7378
                    if ($debug) {
7379
                        var_dump($sql);
7380
                    }
7381
                    $rest_select = Database::query($sql);
7382
                    if (Database::num_rows($rest_select) > 0) {
7383
                        if ($update_database && $new_parent_id) {
7384
                            $assignment_data = Database::fetch_assoc($rest_select);
7385
                            $sql_add_publication = "INSERT INTO ".$TBL_STUDENT_PUBLICATION_ASSIGNMENT." SET
7386
                                    	c_id = '$course_id',
7387
                                       expires_on          = '".$assignment_data['expires_on']."',
7388
                                       ends_on              = '".$assignment_data['ends_on']."',
7389
                                       add_to_calendar      = '".$assignment_data['add_to_calendar']."',
7390
                                       enable_qualification = '".$assignment_data['enable_qualification']."',
7391
                                       publication_id       = '".$new_parent_id."'";
7392
                            if ($debug) {
7393
                                echo $sql_add_publication;
7394
                            }
7395
                            Database::query($sql_add_publication);
7396
                            $id = (int) Database::insert_id();
7397
                            if ($id) {
7398
                                $sql_update = "UPDATE $TBL_STUDENT_PUBLICATION
7399
                                           SET  has_properties = '".$id."',
7400
                                                view_properties = '1'
7401
                                           WHERE iid = ".$new_parent_id;
7402
                                if ($debug) {
7403
                                    echo $sql_update;
7404
                                }
7405
                                Database::query($sql_update);
7406
                                if (!isset($result_message[$TBL_STUDENT_PUBLICATION_ASSIGNMENT])) {
7407
                                    $result_message[$TBL_STUDENT_PUBLICATION_ASSIGNMENT] = 0;
7408
                                }
7409
                                $result_message[$TBL_STUDENT_PUBLICATION_ASSIGNMENT]++;
7410
                            }
7411
                        }
7412
                    }
7413
7414
                    $doc_url = $data['url'];
7415
                    $new_url = str_replace($parent_data['url'], $created_dir, $doc_url);
7416
7417
                    if ($update_database) {
7418
                        // Creating a new work
7419
                        $data['sent_date'] = new DateTime($data['sent_date'], new DateTimeZone('UTC'));
7420
7421
                        $data['post_group_id'] = (int) $data['post_group_id'];
7422
                        $publication = (new CStudentPublication())
7423
                            ->setTitle($data['title'])
7424
                            ->setDescription($data['description'].' file moved')
7425
                            ->setActive($data['active'])
7426
                            ->setAccepted($data['accepted'])
7427
                            ->setPostGroupId($data['post_group_id'])
7428
                            ->setSentDate($data['sent_date'])
7429
                            ->setParentId($new_parent_id)
7430
                            ->setWeight($data['weight'])
7431
                            ->setHasProperties(0)
7432
                            ->setWeight($data['weight'])
7433
                            ->setContainsFile($data['contains_file'])
7434
                            ->setSession($session)
7435
                            ->setUserId($data['user_id'])
7436
                            ->setFiletype('file')
7437
                            ->setDocumentId(0)
7438
                        ;
7439
7440
                        $em->persist($publication);
7441
                        $em->flush();
7442
7443
                        $id = $publication->getIid();
7444
                        /*api_item_property_update(
7445
                            $course_info,
7446
                            'work',
7447
                            $id,
7448
                            'DocumentAdded',
7449
                            $user_id,
7450
                            null,
7451
                            null,
7452
                            null,
7453
                            null,
7454
                            $new_session_id
7455
                        );*/
7456
                        if (!isset($result_message[$TBL_STUDENT_PUBLICATION])) {
7457
                            $result_message[$TBL_STUDENT_PUBLICATION] = 0;
7458
                        }
7459
                        $result_message[$TBL_STUDENT_PUBLICATION]++;
7460
                        $full_file_name = $course_dir.'/'.$doc_url;
7461
                        $new_file = $course_dir.'/'.$new_url;
7462
7463
                        if (file_exists($full_file_name)) {
7464
                            // deleting old assignment
7465
                            $result = copy($full_file_name, $new_file);
7466
                            if ($result) {
7467
                                unlink($full_file_name);
7468
                                if (isset($data['id'])) {
7469
                                    $sql = "DELETE FROM $TBL_STUDENT_PUBLICATION WHERE id= ".$data['id'];
7470
                                    if ($debug) {
7471
                                        var_dump($sql);
7472
                                    }
7473
                                    Database::query($sql);
7474
                                }
7475
                                api_item_property_update(
7476
                                    $course_info,
7477
                                    'work',
7478
                                    $data['id'],
7479
                                    'DocumentDeleted',
7480
                                    api_get_user_id()
7481
                                );
7482
                            }
7483
                        }
7484
                    }
7485
                }
7486
            }
7487
        }
7488
7489
        //9. Survey   Pending
7490
        //10. Dropbox - not neccesary to move categories (no presence of session_id)
7491
        $sql = "SELECT id FROM $TBL_DROPBOX_FILE
7492
                WHERE uploader_id = $user_id AND session_id = $origin_session_id AND c_id = $course_id";
7493
        if ($debug) {
7494
            var_dump($sql);
7495
        }
7496
        $res = Database::query($sql);
7497
        while ($row = Database::fetch_assoc($res)) {
7498
            $id = (int) $row['id'];
7499
            if ($update_database) {
7500
                $sql = "UPDATE $TBL_DROPBOX_FILE SET session_id = $new_session_id WHERE c_id = $course_id AND iid = $id";
7501
                if ($debug) {
7502
                    var_dump($sql);
7503
                }
7504
                Database::query($sql);
7505
                if ($debug) {
7506
                    var_dump($res);
7507
                }
7508
7509
                $sql = "UPDATE $TBL_DROPBOX_POST SET session_id = $new_session_id WHERE file_id = $id";
7510
                if ($debug) {
7511
                    var_dump($sql);
7512
                }
7513
                Database::query($sql);
7514
                if ($debug) {
7515
                    var_dump($res);
7516
                }
7517
                if (!isset($result_message[$TBL_DROPBOX_FILE])) {
7518
                    $result_message[$TBL_DROPBOX_FILE] = 0;
7519
                }
7520
                $result_message[$TBL_DROPBOX_FILE]++;
7521
            }
7522
        }
7523
7524
        // 11. Notebook
7525
        /*$sql = "SELECT notebook_id FROM $TBL_NOTEBOOK
7526
                WHERE
7527
                    user_id = $user_id AND
7528
                    session_id = $origin_session_id AND
7529
                    course = '$origin_course_code' AND
7530
                    c_id = $course_id";
7531
        if ($debug) {
7532
            var_dump($sql);
7533
        }
7534
        $res = Database::query($sql);
7535
        while ($row = Database::fetch_assoc($res)) {
7536
            $id = $row['notebook_id'];
7537
            if ($update_database) {
7538
                $sql = "UPDATE $TBL_NOTEBOOK
7539
                        SET session_id = $new_session_id
7540
                        WHERE c_id = $course_id AND notebook_id = $id";
7541
                if ($debug) {
7542
                    var_dump($sql);
7543
                }
7544
                $res = Database::query($sql);
7545
                if ($debug) {
7546
                    var_dump($res);
7547
                }
7548
            }
7549
        }*/
7550
7551
        if ($update_database) {
7552
            echo Display::return_message(get_lang('StatsMoved'));
7553
            if (is_array($result_message)) {
7554
                foreach ($result_message as $table => $times) {
7555
                    echo 'Table '.$table.' - '.$times.' records updated <br />';
7556
                }
7557
            }
7558
        } else {
7559
            echo '<h4>'.get_lang('UserInformationOfThisCourse').'</h4>';
7560
            echo '<br />';
7561
            echo '<table class="table" width="100%">';
7562
            echo '<tr>';
7563
            echo '<td width="50%" valign="top">';
7564
7565
            if (0 == $origin_session_id) {
7566
                echo '<h5>'.get_lang('OriginCourse').'</h5>';
7567
            } else {
7568
                echo '<h5>'.get_lang('OriginSession').' #'.$origin_session_id.'</h5>';
7569
            }
7570
            self::compareUserData($result_message);
7571
            echo '</td>';
7572
            echo '<td width="50%" valign="top">';
7573
            if (0 == $new_session_id) {
7574
                echo '<h5>'.get_lang('DestinyCourse').'</h5>';
7575
            } else {
7576
                echo '<h5>'.get_lang('DestinySession').' #'.$new_session_id.'</h5>';
7577
            }
7578
            self::compareUserData($result_message_compare);
7579
            echo '</td>';
7580
            echo '</tr>';
7581
            echo '</table>';
7582
        }
7583
    }
7584
7585
    public static function compareUserData($result_message)
7586
    {
7587
        foreach ($result_message as $table => $data) {
7588
            $title = $table;
7589
            if ('TRACK_E_EXERCISES' === $table) {
7590
                $title = get_lang('Exercises');
7591
            } elseif ('TRACK_E_EXERCISES_IN_LP' === $table) {
7592
                $title = get_lang('ExercisesInLp');
7593
            } elseif ('LP_VIEW' === $table) {
7594
                $title = get_lang('LearningPaths');
7595
            }
7596
            echo '<br / ><h3>'.get_lang($title).' </h3><hr />';
7597
7598
            if (is_array($data)) {
7599
                foreach ($data as $id => $item) {
7600
                    if ('TRACK_E_EXERCISES' === $table || 'TRACK_E_EXERCISES_IN_LP' === $table) {
7601
                        echo "<br /><h3>".get_lang('Attempt')." #$id</h3>";
7602
                        echo '<h3>';
7603
                        echo get_lang('Exercise').' #'.$item['exe_exo_id'];
7604
                        echo '</h3>';
7605
                        if (!empty($item['orig_lp_id'])) {
7606
                            echo '<h3>';
7607
                            echo get_lang('LearningPath').' #'.$item['orig_lp_id'];
7608
                            echo '</h3>';
7609
                        }
7610
                        // Process data.
7611
                        $array = [
7612
                            'exe_date' => get_lang('Date'),
7613
                            'score' => get_lang('Score'),
7614
                            'max_score' => get_lang('Weighting'),
7615
                        ];
7616
                        foreach ($item as $key => $value) {
7617
                            if (in_array($key, array_keys($array))) {
7618
                                $key = $array[$key];
7619
                                echo "$key =  $value <br />";
7620
                            }
7621
                        }
7622
                    } else {
7623
                        echo "<br /><h3>".get_lang('Id')." #$id</h3>";
7624
                        // process data
7625
                        foreach ($item as $key => $value) {
7626
                            echo "$key =  $value <br />";
7627
                        }
7628
                    }
7629
                }
7630
            } else {
7631
                echo get_lang('NoResults');
7632
            }
7633
        }
7634
    }
7635
7636
    private static function generateQuizzesTable(array $courseInfo, int $sessionId = 0): string
7637
    {
7638
        if (empty($sessionId)) {
7639
            $userList = CourseManager::get_user_list_from_course_code(
7640
                $courseInfo['code'],
7641
                $sessionId,
7642
                null,
7643
                null,
7644
                STUDENT
7645
            );
7646
        } else {
7647
            $userList = CourseManager::get_user_list_from_course_code($courseInfo['code'], $sessionId, null, null, 0);
7648
        }
7649
7650
        $exerciseList = ExerciseLib::get_all_exercises($courseInfo, $sessionId, false, null);
7651
7652
        if (empty($exerciseList)) {
7653
            return Display::return_message(get_lang('NoEx'));
7654
        }
7655
7656
        $toGraphExerciseResult = [];
7657
7658
        $quizzesTable = new SortableTableFromArray([], 0, 0, 'quizzes');
7659
        $quizzesTable->setHeaders(
7660
            [
7661
                get_lang('Exercises'),
7662
                get_lang('Attempts'),
7663
                get_lang('BestAttempt'),
7664
                get_lang('Ranking'),
7665
                get_lang('BestResultInCourse'),
7666
                get_lang('Statistics').Display::getMdiIcon(
7667
                    ActionIcon::INFORMATION,
7668
                    'ch-tool-icon',
7669
                    null,
7670
                    ICON_SIZE_SMALL,
7671
                    get_lang('OnlyBestResultsPerStudent')
7672
                ),
7673
            ]
7674
        );
7675
7676
        $webCodePath = api_get_path(WEB_CODE_PATH);
7677
7678
        foreach ($exerciseList as $exercices) {
7679
            $objExercise = new Exercise($courseInfo['real_id']);
7680
            $objExercise->read($exercices['id']);
7681
            $visibleReturn = $objExercise->is_visible();
7682
7683
            // Getting count of attempts by user
7684
            $attempts = Event::count_exercise_attempts_by_user(
7685
                api_get_user_id(),
7686
                $exercices['id'],
7687
                $courseInfo['real_id'],
7688
                $sessionId
7689
            );
7690
7691
            $url = $webCodePath.'exercise/overview.php?'
7692
                .http_build_query(
7693
                    ['cidReq' => $courseInfo['code'], 'id_session' => $sessionId, 'exerciseId' => $exercices['id']]
7694
                );
7695
7696
            if (true == $visibleReturn['value']) {
7697
                $exercices['title'] = Display::url(
7698
                    $exercices['title'],
7699
                    $url,
7700
                    ['target' => SESSION_LINK_TARGET]
7701
                );
7702
            } elseif (-1 == $exercices['active']) {
7703
                $exercices['title'] = sprintf(get_lang('XParenthesisDeleted'), $exercices['title']);
7704
            }
7705
7706
            $quizData = [
7707
                $exercices['title'],
7708
                $attempts,
7709
                '-',
7710
                '-',
7711
                '-',
7712
                '-',
7713
            ];
7714
7715
            // Exercise configuration show results or show only score
7716
            if (!in_array($exercices['results_disabled'], [0, 2])
7717
                || empty($attempts)
7718
            ) {
7719
                $quizzesTable->addRow($quizData);
7720
7721
                continue;
7722
            }
7723
7724
            //For graphics
7725
            $bestExerciseAttempts = Event::get_best_exercise_results_by_user(
7726
                $exercices['id'],
7727
                $courseInfo['real_id'],
7728
                $sessionId
7729
            );
7730
7731
            $toGraphExerciseResult[$exercices['id']] = [
7732
                'title' => $exercices['title'],
7733
                'data' => $bestExerciseAttempts,
7734
            ];
7735
7736
            // Getting best results
7737
            $bestScoreData = ExerciseLib::get_best_attempt_in_course(
7738
                $exercices['id'],
7739
                $courseInfo['real_id'],
7740
                $sessionId
7741
            );
7742
7743
            if (!empty($bestScoreData)) {
7744
                $quizData[5] = ExerciseLib::show_score(
7745
                    $bestScoreData['score'],
7746
                    $bestScoreData['max_score']
7747
                );
7748
            }
7749
7750
            $exerciseAttempt = ExerciseLib::get_best_attempt_by_user(
7751
                api_get_user_id(),
7752
                $exercices['id'],
7753
                $courseInfo['real_id'],
7754
                $sessionId
7755
            );
7756
7757
            if (!empty($exerciseAttempt)) {
7758
                // Always getting the BEST attempt
7759
                $score = $exerciseAttempt['score'];
7760
                $weighting = $exerciseAttempt['max_score'];
7761
                $exeId = $exerciseAttempt['exe_id'];
7762
7763
                $latestAttemptUrl = $webCodePath.'exercise/result.php?'
7764
                    .http_build_query(
7765
                        [
7766
                            'id' => $exeId,
7767
                            'cidReq' => $courseInfo['code'],
7768
                            'show_headers' => 1,
7769
                            'id_session' => $sessionId,
7770
                        ]
7771
                    );
7772
7773
                $quizData[3] = Display::url(
7774
                    ExerciseLib::show_score($score, $weighting),
7775
                    $latestAttemptUrl
7776
                );
7777
7778
                $myScore = !empty($weighting) && 0 != intval($weighting) ? $score / $weighting : 0;
7779
7780
                //@todo this function slows the page
7781
                if (is_int($userList)) {
7782
                    $userList = [$userList];
7783
                }
7784
7785
                $quizData[4] = ExerciseLib::get_exercise_result_ranking(
7786
                    $myScore,
7787
                    $exeId,
7788
                    $exercices['id'],
7789
                    $courseInfo['code'],
7790
                    $sessionId,
7791
                    $userList
7792
                );
7793
                $graph = self::generate_exercise_result_thumbnail_graph($toGraphExerciseResult[$exercices['id']]);
7794
                $normalGraph = self::generate_exercise_result_graph($toGraphExerciseResult[$exercices['id']]);
7795
7796
                $quizData[6] = Display::url(
7797
                    Display::img($graph, '', [], false),
7798
                    $normalGraph,
7799
                    ['id' => $exercices['id'], 'class' => 'expand-image']
7800
                );
7801
            }
7802
7803
            $quizzesTable->addRow($quizData);
7804
        }
7805
7806
        return Display::div(
7807
            $quizzesTable->toHtml(),
7808
            ['class' => 'table-responsive']
7809
        );
7810
    }
7811
7812
    private static function generateLearningPathsTable(int $userId, array $courseInfo, int $sessionId = 0): string
7813
    {
7814
        $columnHeaders = [
7815
            'lp' => get_lang('LearningPath'),
7816
            'time' => get_lang('LatencyTimeSpent'),
7817
            'progress' => get_lang('Progress'),
7818
            'score' => get_lang('Score'),
7819
            'best_score' => get_lang('BestScore'),
7820
            'last_connection' => get_lang('LastConnexion'),
7821
        ];
7822
7823
        $trackingColumns = api_get_setting('session.tracking_columns', true);
7824
7825
        if (isset($trackingColumns['my_progress_lp'])) {
7826
            $columnHeaders = array_filter(
7827
                $columnHeaders,
7828
                function ($columHeader, $key) use ($trackingColumns) {
7829
                    if (!isset($trackingColumns['my_progress_lp'][$key])
7830
                        || false == $trackingColumns['my_progress_lp'][$key]
7831
                    ) {
7832
                        return false;
7833
                    }
7834
7835
                    return true;
7836
                },
7837
                ARRAY_FILTER_USE_BOTH
7838
            );
7839
        }
7840
7841
        if (true === api_get_configuration_value('student_follow_page_add_LP_subscription_info')) {
7842
            $columnHeaders['student_follow_page_add_LP_subscription_info'] = get_lang('Unlock');
7843
        }
7844
7845
        if ('true' === api_get_setting('lp.student_follow_page_add_lp_acquisition_info')) {
7846
            $columnHeaders['student_follow_page_add_lp_acquisition_info'] = get_lang('Acquisition');
7847
        }
7848
7849
        $addLpInvisibleCheckbox = api_get_setting('lp.student_follow_page_add_lp_invisible_checkbox');
7850
7851
        $columnHeadersKeys = array_keys($columnHeaders);
7852
7853
        $learningpathsTable = new SortableTableFromArray([], 0, 0, 'learningpaths');
7854
        $learningpathsTable->setHeaders($columnHeaders);
7855
7856
        // LP table results
7857
        $list = new LearnpathList(
7858
            api_get_user_id(),
7859
            $courseInfo,
7860
            $sessionId,
7861
            'resource.publishedOn ASC',
7862
            true,
7863
            null,
7864
            true
7865
        );
7866
7867
        $lpList = $list->get_flat_list();
7868
7869
        if (empty($lpList)) {
7870
            return Display::return_message(get_lang('NoLearnpath'));
7871
        }
7872
7873
        $webCodePath = api_get_path(WEB_CODE_PATH);
7874
7875
        foreach ($lpList as $lpId => $learnpath) {
7876
            $learningpathData = [];
7877
7878
            if (!$learnpath['lp_visibility']) {
7879
                continue;
7880
            }
7881
7882
            if ($addLpInvisibleCheckbox) {
7883
                if (!StudentFollowPage::isViewVisible($lpId, $userId, $courseInfo['real_id'], $sessionId)) {
7884
                    continue;
7885
                }
7886
            }
7887
7888
            $url = $webCodePath.'lp/lp_controller.php?'
7889
                .http_build_query(
7890
                    ['cidReq' => $courseInfo['code'], 'id_session' => $sessionId, 'lp_id' => $lpId, 'action' => 'view']
7891
                );
7892
7893
            if (in_array('lp', $columnHeadersKeys)) {
7894
                if (0 == $learnpath['lp_visibility']) {
7895
                    $learningpathData[] = $learnpath['lp_name'];
7896
                } else {
7897
                    $learningpathData[] = Display::url(
7898
                        $learnpath['lp_name'],
7899
                        $url,
7900
                        ['target' => SESSION_LINK_TARGET]
7901
                    );
7902
                }
7903
            }
7904
7905
            if (in_array('time', $columnHeadersKeys)) {
7906
                $time_spent_in_lp = self::get_time_spent_in_lp(
7907
                    $userId,
7908
                    $courseInfo['code'],
7909
                    [$lpId],
7910
                    $sessionId
7911
                );
7912
7913
                $learningpathData[] = api_time_to_hms($time_spent_in_lp);
7914
            }
7915
7916
            if (in_array('progress', $columnHeadersKeys)) {
7917
                $progress = self::get_avg_student_progress(
7918
                    $userId,
7919
                    $courseInfo['code'],
7920
                    [$lpId],
7921
                    $sessionId
7922
                );
7923
7924
                if (is_numeric($progress)) {
7925
                    $progress = sprintf(get_lang('XPercent'), $progress);
7926
                }
7927
7928
                $learningpathData[] = $progress;
7929
            }
7930
7931
            if (in_array('score', $columnHeadersKeys)) {
7932
                $percentage_score = self::get_avg_student_score(
7933
                    $userId,
7934
                    $courseInfo['code'],
7935
                    [$lpId],
7936
                    $sessionId
7937
                );
7938
7939
                if (is_numeric($percentage_score)) {
7940
                    $percentage_score = sprintf(get_lang('XPercent'), $percentage_score);
7941
                } else {
7942
                    $percentage_score = sprintf(get_lang('XPercent'), 0);
7943
                }
7944
7945
                $learningpathData[] = $percentage_score;
7946
            }
7947
7948
            if (in_array('best_score', $columnHeadersKeys)) {
7949
                $bestScore = self::get_avg_student_score(
7950
                    $userId,
7951
                    $courseInfo['code'],
7952
                    [$lpId],
7953
                    $sessionId,
7954
                    false,
7955
                    false,
7956
                    true
7957
                );
7958
7959
                if (is_numeric($bestScore)) {
7960
                    $bestScore = sprintf(get_lang('XPercent'), $bestScore);
7961
                } else {
7962
                    $bestScore = '-';
7963
                }
7964
7965
                $learningpathData[] = $bestScore;
7966
            }
7967
7968
            if (in_array('last_connection', $columnHeadersKeys)) {
7969
                $lastConnectionInLp = self::get_last_connection_time_in_lp(
7970
                    $userId,
7971
                    $courseInfo['code'],
7972
                    $lpId,
7973
                    $sessionId
7974
                );
7975
7976
                $lastConnection = '-';
7977
7978
                if (!empty($lastConnectionInLp)) {
7979
                    $lastConnection = api_convert_and_format_date($lastConnectionInLp, DATE_TIME_FORMAT_LONG);
7980
                }
7981
7982
                $learningpathData[] = $lastConnection;
7983
            }
7984
7985
            if (in_array('student_follow_page_add_LP_subscription_info', $columnHeadersKeys)) {
7986
                $learningpathData[] = StudentFollowPage::getLpSubscription(
7987
                    $learnpath,
7988
                    $userId,
7989
                    $courseInfo['real_id'],
7990
                    $sessionId
7991
                );
7992
            }
7993
7994
            if (in_array('student_follow_page_add_lp_acquisition_info', $columnHeadersKeys)) {
7995
                $learningpathData[] = StudentFollowPage::getLpAcquisition(
7996
                    $learnpath,
7997
                    $userId,
7998
                    $courseInfo['real_id'],
7999
                    $sessionId
8000
                );
8001
            }
8002
8003
            $learningpathsTable->addRow($learningpathData);
8004
        }
8005
8006
        return Display::div(
8007
            $learningpathsTable->toHtml(),
8008
            ['class' => 'table-responsive']
8009
        );
8010
    }
8011
8012
    /**
8013
     * Counts the number of student publications for a given course, session, and group.
8014
     *
8015
     * @param int $courseId
8016
     * @param int|null $sessionId
8017
     * @param int|null $groupId
8018
     *
8019
     * @return int The number of student publications.
8020
     */
8021
    public static function countStudentPublications(int $courseId, ?int $sessionId = null, ?int $groupId = null): int
8022
    {
8023
        $repo = Container::getStudentPublicationRepository();
8024
8025
        $course = api_get_course_entity($courseId);
8026
        $session = api_get_session_entity($sessionId);
8027
        $group = api_get_group_entity($groupId);
8028
8029
        $qb = $repo->getResourcesByCourse($course, $session, $group);
8030
        $qb->select('COUNT(resource.iid)');
8031
8032
        return (int) $qb->getQuery()->getSingleScalarResult();
8033
    }
8034
8035
    /**
8036
     * Counts the number of forum posts for a given course, session, and group.
8037
     *
8038
     * @param int $courseId
8039
     * @param int|null $sessionId
8040
     * @param int|null $groupId
8041
     *
8042
     * @return int The number of forum posts.
8043
     */
8044
    public static function countStudentMessages(int $courseId, ?int $sessionId = null, ?int $groupId = null): int
8045
    {
8046
        $repo = Container::getForumPostRepository();
8047
8048
        $course = api_get_course_entity($courseId);
8049
        $session = api_get_session_entity($sessionId);
8050
        $group = api_get_group_entity($groupId);
8051
8052
        $qb = $repo->getResourcesByCourse($course, $session, $group);
8053
        $qb->select('COUNT(resource.iid)');
8054
8055
        return (int) $qb->getQuery()->getSingleScalarResult();
8056
    }
8057
8058
    /**
8059
     * It gets the last finalization date of learnpaths in a course.
8060
     *
8061
     * @return string finalization date formatted or false if it is empty.
8062
     */
8063
    public static function getCourseLpFinalizationDate(
8064
        int $userId,
8065
        int $courseId,
8066
        int $sessionId,
8067
        bool $convertDate = true
8068
    ) {
8069
        $tblLpView = Database::get_course_table(TABLE_LP_VIEW);
8070
        $tblLpItem = Database::get_course_table(TABLE_LP_ITEM);
8071
        $tblLpItemView = Database::get_course_table(TABLE_LP_ITEM_VIEW);
8072
8073
        $sql = "SELECT FROM_UNIXTIME(liv.start_time) as start_date
8074
                FROM $tblLpItemView liv
8075
                INNER JOIN
8076
                    $tblLpView lv ON lv.iid = liv.lp_view_id
8077
                INNER JOIN
8078
                    $tblLpItem li ON li.iid = liv.lp_item_id
8079
                WHERE
8080
                    lv.user_id = $userId AND
8081
                    lv.c_id = $courseId AND
8082
                    lv.session_id = $sessionId AND
8083
                    li.item_type = '".TOOL_LP_FINAL_ITEM."' AND
8084
                    liv.status = 'completed'
8085
                ORDER BY start_date DESC
8086
                LIMIT 1";
8087
8088
        $rs = Database::query($sql);
8089
        $lpFinalDate = Database::result($rs, 0, 0);
8090
8091
        if (empty($lpFinalDate)) {
8092
            return false;
8093
        }
8094
8095
        if ($convertDate) {
8096
            return api_convert_and_format_date($lpFinalDate, DATE_FORMAT_SHORT);
8097
        }
8098
8099
        return $lpFinalDate;
8100
    }
8101
8102
    /**
8103
     * It gets the last finalization date of exercises in a course.
8104
     *
8105
     * @return string finalization date formatted or false if it is empty.
8106
     */
8107
    public static function getCourseQuizLastFinalizationDate(
8108
        int $userId,
8109
        int $courseId,
8110
        int $sessionId,
8111
        bool $convertDate = true
8112
    ) {
8113
        $tblTrackExercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
8114
8115
        $sql = "SELECT ex.exe_date
8116
                FROM $tblTrackExercise AS ex
8117
                WHERE
8118
                    ex.c_id = $courseId AND
8119
                    ex.session_id  = $sessionId AND
8120
                    ex.exe_user_id = $userId AND
8121
                    ex.status = ''
8122
                ORDER BY ex.exe_date DESC
8123
                LIMIT 1";
8124
        $rs = Database::query($sql);
8125
        $exeDate = Database::result($rs, 0, 0);
8126
8127
        if (empty($exeDate)) {
8128
            return false;
8129
        }
8130
8131
        if ($convertDate) {
8132
            return api_convert_and_format_date($exeDate, DATE_FORMAT_SHORT);
8133
        }
8134
8135
        return $exeDate;
8136
    }
8137
8138
    /**
8139
     * Return the total time spent in courses (no the total in platform).
8140
     *
8141
     * @return int
8142
     * @throws \Doctrine\DBAL\Exception
8143
     */
8144
    public static function getTotalTimeSpentInCourses(
8145
        string $dateFrom = '',
8146
        string $dateUntil = ''
8147
    ): int {
8148
        $tableTrackLogin = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
8149
        $tableUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
8150
        $tableUrl = null;
8151
        $urlCondition = null;
8152
        $conditionTime = null;
8153
        if (AccessUrlHelper::isMultiple()) {
8154
            $accessUrlId = api_get_current_access_url_id();
8155
            $tableUrl = ", ".$tableUrlRelUser." as url_users";
8156
            $urlCondition = " AND u.user_id = url_users.user_id AND access_url_id = $accessUrlId";
8157
        }
8158
        if (!empty($dateFrom) && !empty($dateUntil)) {
8159
            $dateFrom = Database::escape_string($dateFrom);
8160
            $dateUntil = Database::escape_string($dateUntil);
8161
            $conditionTime = " (login_course_date >= '$dateFrom' AND logout_course_date <= '$dateUntil' ) ";
8162
        }
8163
        $sql = "SELECT SUM(TIMESTAMPDIFF(HOUR, login_course_date, logout_course_date)) diff
8164
    	        FROM $tableTrackLogin u $tableUrl
8165
                WHERE $conditionTime $urlCondition";
8166
        $rs = Database::query($sql);
8167
        $row = Database::fetch_array($rs, 'ASSOC');
8168
        $diff = $row['diff'];
8169
        if ($diff >= 0 and !empty($diff)) {
8170
            return $diff;
8171
        }
8172
        return 0;
8173
    }
8174
}
8175