Tracking::setUserSearchForm()   A
last analyzed

Complexity

Conditions 2
Paths 2

Size

Total Lines 35
Code Lines 24

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
eloc 24
c 0
b 0
f 0
dl 0
loc 35
rs 9.536
cc 2
nc 2
nop 2
1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\Course;
6
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
7
use Chamilo\CourseBundle\Entity\CLpCategory;
8
use Chamilo\UserBundle\Entity\User;
9
use ChamiloSession as Session;
10
use CpChart\Cache as pCache;
11
use CpChart\Data as pData;
12
use CpChart\Image as pImage;
13
use ExtraField as ExtraFieldModel;
14
15
/**
16
 *  Class Tracking.
17
 *
18
 *  @author  Julio Montoya <[email protected]>
19
 */
20
class Tracking
21
{
22
    /**
23
     * Get group reporting.
24
     *
25
     * @param int    $course_id
26
     * @param int    $sessionId
27
     * @param int    $group_id
28
     * @param string $type
29
     * @param int    $start
30
     * @param int    $limit
31
     * @param int    $sidx
32
     * @param string $sord
33
     * @param array  $where_condition
34
     *
35
     * @return array|null
36
     */
37
    public static function get_group_reporting(
38
        $course_id,
39
        $sessionId = 0,
40
        $group_id = 0,
41
        $type = 'all',
42
        $start = 0,
43
        $limit = 1000,
44
        $sidx = 1,
45
        $sord = 'desc',
46
        $where_condition = []
47
    ) {
48
        $course_id = (int) $course_id;
49
        $sessionId = (int) $sessionId;
50
51
        if (empty($course_id)) {
52
            return null;
53
        }
54
        $courseInfo = api_get_course_info_by_id($course_id);
55
        if ('count' == $type) {
56
            return GroupManager::get_group_list(null, $courseInfo, null, $sessionId, true);
57
        }
58
59
        $groupList = GroupManager::get_group_list(null, $courseInfo, null, $sessionId);
60
        $parsedResult = [];
61
        if (!empty($groupList)) {
62
            foreach ($groupList as $group) {
63
                $users = GroupManager::get_users($group['id'], true, null, null, false, $courseInfo['real_id']);
64
                $time = 0;
65
                $avg_student_score = 0;
66
                $avg_student_progress = 0;
67
                $work = 0;
68
                $messages = 0;
69
70
                foreach ($users as $user_data) {
71
                    $time += self::get_time_spent_on_the_course(
72
                        $user_data['user_id'],
73
                        $courseInfo['real_id'],
74
                        $sessionId
75
                    );
76
                    $average = self::get_avg_student_score(
77
                        $user_data['user_id'],
78
                        $courseInfo['code'],
79
                        [],
80
                        $sessionId
81
                    );
82
                    if (is_numeric($average)) {
83
                        $avg_student_score += $average;
84
                    }
85
                    $avg_student_progress += self::get_avg_student_progress(
86
                        $user_data['user_id'],
87
                        $courseInfo['code'],
88
                        [],
89
                        $sessionId
90
                    );
91
                    $work += self::count_student_assignments(
92
                        $user_data['user_id'],
93
                        $courseInfo['code'],
94
                        $sessionId
95
                    );
96
                    $messages += self::count_student_messages(
97
                        $user_data['user_id'],
98
                        $courseInfo['code'],
99
                        $sessionId
100
                    );
101
                }
102
103
                $countUsers = count($users);
104
                $averageProgress = empty($countUsers) ? 0 : round($avg_student_progress / $countUsers, 2);
105
                $averageScore = empty($countUsers) ? 0 : round($avg_student_score / $countUsers, 2);
106
107
                $groupItem = [
108
                    'id' => $group['id'],
109
                    'name' => $group['name'],
110
                    'time' => api_time_to_hms($time),
111
                    'progress' => $averageProgress,
112
                    'score' => $averageScore,
113
                    'works' => $work,
114
                    'messages' => $messages,
115
                ];
116
                $parsedResult[] = $groupItem;
117
            }
118
        }
119
120
        return $parsedResult;
121
    }
122
123
    /**
124
     * Get the lp quizzes result as content for pdf export.
125
     *
126
     * @param $userId
127
     * @param $sessionId
128
     *
129
     * @return string
130
     */
131
    public static function getLpQuizContentToPdf(
132
        $userId,
133
        $sessionId
134
    ) {
135
        $tblLp = Database::get_course_table(TABLE_LP_MAIN);
136
        $tblLpItem = Database::get_course_table(TABLE_LP_ITEM);
137
        $tblLpItemView = Database::get_course_table(TABLE_LP_ITEM_VIEW);
138
        $tblLpView = Database::get_course_table(TABLE_LP_VIEW);
139
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
140
        $tblCourse = Database::get_main_table(TABLE_MAIN_COURSE);
141
142
        $courses = Tracking::get_courses_list_from_session($sessionId);
143
144
        // It creates lp quiz table html
145
        $lpQuizTable = '';
146
        if (!empty($courses)) {
147
            $lpQuizTable .= '<table class="data_table">';
148
            $lpQuizTable .= '<tbody>';
149
            $lpTitle = '';
150
            foreach ($courses as $course) {
151
                $courseId = $course['c_id'];
152
                $sql = 'SELECT DISTINCT c_lp_item.title AS quiz_name,
153
				c_lp.name AS lp_name,
154
				CASE WHEN c_lp_item_view.max_score>0 THEN CONCAT(ROUND((SUM(c_lp_item_view.score)/c_lp_item_view.max_score)*100),\'%\') ELSE 0 END AS score,
155
				SUM(c_lp_view.view_count) AS attempts
156
				FROM '.$tblLpView.' AS c_lp_view
157
				JOIN '.$tblUser.' AS user ON (user.user_id = c_lp_view.user_id)
158
				JOIN '.$tblCourse.' AS course ON (course.id = c_lp_view.c_id)
159
				JOIN '.$tblLp.' AS c_lp ON (c_lp.id = c_lp_view.lp_id AND c_lp.c_id = c_lp_view.c_id)
160
				JOIN '.$tblLpItem.' AS c_lp_item ON (c_lp_item.c_id = c_lp_view.c_id AND c_lp_item.lp_id = c_lp_view.lp_id)
161
				LEFT JOIN '.$tblLpItemView.' AS c_lp_item_view ON (c_lp_item_view.lp_item_id = c_lp_item.id AND c_lp_item_view.lp_view_id = c_lp_view.id AND c_lp_item_view.c_id = c_lp_view.c_id)
162
				WHERE
163
				    user.id = '.$userId.' AND
164
				    course.id = '.$courseId.' AND
165
				    c_lp_item.item_type = \'quiz\' AND
166
				    c_lp_view.session_id = '.$sessionId.'
167
				GROUP BY quiz_name, lp_name
168
				ORDER BY c_lp_item.display_order';
169
                $result = Database::query($sql);
170
                while ($row = Database::fetch_array($result)) {
171
                    if ($lpTitle != $row['lp_name']) {
172
                        $lpTitle = $row['lp_name'];
173
                        $lpQuizTable .= '<tr>
174
                            <th>'.stripslashes($lpTitle).'</th>
175
                            <th>'.get_lang('Score').'</th>
176
                            <th>'.get_lang('Attempts').'</th>
177
                        </tr>';
178
                    }
179
                    $lpQuizTable .= '<tr>
180
                        <td>'.$row['quiz_name'].'</td>
181
                        <td>'.$row['score'].'</td>
182
                        <td>'.$row['attempts'].'</td>
183
                    </tr>';
184
                }
185
            }
186
            $lpQuizTable .= '</tbody></table>';
187
        }
188
189
        return $lpQuizTable;
190
    }
191
192
    /**
193
     * Get the tables html as contents to export lp certificate pdf .
194
     *
195
     * @return array
196
     */
197
    public static function getLpCertificateTablesToPdf(
198
        $userId,
199
        $sessionId
200
    ) {
201
        $tblTrackCourseAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
202
        $tblLp = Database::get_course_table(TABLE_LP_MAIN);
203
        $tblLpItem = Database::get_course_table(TABLE_LP_ITEM);
204
        $tblLpItemView = Database::get_course_table(TABLE_LP_ITEM_VIEW);
205
        $tblLpView = Database::get_course_table(TABLE_LP_VIEW);
206
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
207
        $tblCourse = Database::get_main_table(TABLE_MAIN_COURSE);
208
209
        $courses = Tracking::get_courses_list_from_session($sessionId);
210
211
        // It creates the progress table html
212
        $progressTable = '';
213
        if (!empty($courses)) {
214
            $timeSpent = 0;
215
            $numberVisits = 0;
216
            $progress = 0;
217
            foreach ($courses as $course) {
218
                $courseId = $course['c_id'];
219
                $timeSpent += Tracking::get_time_spent_on_the_course($userId, $courseId, $sessionId);
220
                $sql = 'SELECT DISTINCT count(course_access_id) as count
221
                    FROM '.$tblTrackCourseAccess.'
222
                    WHERE
223
                        user_id = '.$userId.' AND
224
                        c_id = '.$courseId.' AND
225
                        session_id = '.$sessionId.'
226
                    ORDER BY login_course_date ASC';
227
                $result = Database::query($sql);
228
                $row = Database::fetch_array($result);
229
                $numberVisits += $row['count'];
230
                $progress += Tracking::get_avg_student_progress($userId, $course['code'], [], $sessionId);
231
            }
232
            $average = round($progress / count($courses), 1);
233
            $average = empty($average) ? '0%' : $average.'%';
234
            $first = Tracking::get_first_connection_date($userId);
235
            $last = Tracking::get_last_connection_date($userId);
236
237
            $table = new HTML_Table(['class' => 'data_table']);
238
            $column = 0;
239
            $row = 0;
240
            $headers = [
241
                get_lang('TimeSpent'),
242
                get_lang('NumberOfVisits'),
243
                get_lang('GlobalProgress'),
244
                get_lang('FirstLogin'),
245
                get_lang('LastConnexionDate'),
246
            ];
247
248
            foreach ($headers as $header) {
249
                $table->setHeaderContents($row, $column, $header);
250
                $column++;
251
            }
252
            $table->setCellContents(1, 0, api_time_to_hms($timeSpent));
253
            $table->setCellContents(1, 1, $numberVisits);
254
            $table->setCellContents(1, 2, $average);
255
            $table->setCellContents(1, 3, $first);
256
            $table->setCellContents(1, 4, $last);
257
            $progressTable = $table->toHtml();
258
        }
259
260
        // It creates the course table html
261
        $courseTable = '';
262
        if (!empty($courses)) {
263
            $totalCourseTime = 0;
264
            $totalAttendance = [0, 0];
265
            $totalScore = 0;
266
            $totalProgress = 0;
267
            $gradeBookTotal = [0, 0];
268
            $totalEvaluations = '0/0 (0%)';
269
            $totalCourses = count($courses);
270
            $scoreDisplay = ScoreDisplay::instance();
271
272
            $courseTable .= '<table class="data_table">';
273
            $courseTable .= '<thead>';
274
            $courseTable .= '<tr>
275
                    <th>'.get_lang('ToolLearnpath').'</th>
276
                    <th>'.get_lang('ConnectionTime').'</th>
277
					<th>'.get_lang('NumberOfVisits').'</th>
278
                    <th>'.get_lang('Progress').'</th>
279
                    <th>'.get_lang('FirstLogin').'</th>
280
                    <th>'.get_lang('LastConnexion').'</th>
281
                </tr>';
282
            $courseTable .= '</thead>';
283
            $courseTable .= '<tbody>';
284
            foreach ($courses as $course) {
285
                $courseId = $course['c_id'];
286
                $courseInfoItem = api_get_course_info_by_id($courseId);
287
                $courseId = $courseInfoItem['real_id'];
288
                $courseCodeItem = $courseInfoItem['code'];
289
290
                $isSubscribed = CourseManager::is_user_subscribed_in_course(
291
                    $userId,
292
                    $courseCodeItem,
293
                    true,
294
                    $sessionId
295
                );
296
297
                if ($isSubscribed) {
298
                    $timeInSeconds = Tracking::get_time_spent_on_the_course(
299
                        $userId,
300
                        $courseId,
301
                        $sessionId
302
                    );
303
                    $totalCourseTime += $timeInSeconds;
304
                    $time_spent_on_course = api_time_to_hms($timeInSeconds);
305
                    $progress = Tracking::get_avg_student_progress(
306
                        $userId,
307
                        $courseCodeItem,
308
                        [],
309
                        $sessionId
310
                    );
311
                    $totalProgress += $progress;
312
                    $bestScore = Tracking::get_avg_student_score(
313
                        $userId,
314
                        $courseCodeItem,
315
                        [],
316
                        $sessionId,
317
                        false,
318
                        false,
319
                        true
320
                    );
321
                    if (is_numeric($bestScore)) {
322
                        $totalScore += $bestScore;
323
                    }
324
                    if ($progress > 0) {
325
                        $progress = empty($progress) ? '0%' : $progress.'%';
326
                        $score = empty($bestScore) ? '0%' : $bestScore.'%';
327
                        $sql = 'SELECT
328
                                    DISTINCT count(course_access_id) as count,
329
   							        date_format(min(login_course_date),\'%d/%m/%Y\') as first,
330
							        date_format(max(logout_course_date),\'%d/%m/%Y\') as last
331
                                FROM
332
                                    '.$tblTrackCourseAccess.'
333
                                WHERE
334
                                    user_id = '.$userId.' AND
335
                                    c_id = '.$courseId.' AND
336
                                    session_id = '.$sessionId;
337
                        $result = Database::query($sql);
338
                        $row = Database::fetch_array($result);
339
                        $numberVisitsByCourse = $row['count'];
340
                        $firstByCourse = $row['first'];
341
                        $lastByCourse = $row['last'];
342
                        $courseTable .= '<tr>
343
                        <td ><a href="'.$courseInfoItem['course_public_url'].'?id_session='.$sessionId.'">'.$courseInfoItem['title'].'</a></td>
344
                        <td >'.$time_spent_on_course.'</td>
345
						<td >'.$numberVisitsByCourse.'</td>
346
                        <td >'.$progress.'</td>
347
                        <td >'.$firstByCourse.'</td>
348
                        <td >'.$lastByCourse.'</td>';
349
                        $courseTable .= '</tr>';
350
                    }
351
                }
352
            }
353
            $totalAttendanceFormatted = $scoreDisplay->display_score($totalAttendance);
354
            $totalScoreFormatted = $scoreDisplay->display_score([$totalScore / $totalCourses, 100], SCORE_AVERAGE);
355
            $totalProgressFormatted = $scoreDisplay->display_score(
356
                [$totalProgress / $totalCourses, 100],
357
                SCORE_AVERAGE
358
            );
359
            $totalEvaluations = $scoreDisplay->display_score($gradeBookTotal);
360
            $totalTimeFormatted = api_time_to_hms($totalCourseTime);
361
            $courseTable .= '<tr>
362
                <th>'.get_lang('Total').'</th>
363
                <th>'.$totalTimeFormatted.'</th>
364
				<th>'.$numberVisits.'</th>
365
                <th>'.$totalProgressFormatted.'</th>
366
                <th>'.$first.'</th>
367
                <th>'.$last.'</th>
368
            </tr>';
369
            $courseTable .= '</tbody></table>';
370
        }
371
372
        // It creates the lps table html
373
        $lpTable = '';
374
        if (!empty($courses)) {
375
            $lpTable = '<table class="data_table">';
376
            $lpTable .= '<tbody>';
377
            $lpTitle = '';
378
            foreach ($courses as $course) {
379
                $courseId = $course['c_id'];
380
                $sql = 'SELECT DISTINCT c_lp.name AS lp_name,
381
				c_lp_item.title AS chapter,
382
				SEC_TO_TIME(c_lp_item_view.total_time) AS temps,
383
				c_lp_item_view.status AS etat,
384
				SUM(c_lp_view.view_count) AS tentatives,
385
				c_lp_item_2.title AS chapter2,
386
				SEC_TO_TIME(c_lp_item_view_2.total_time) AS temps2,
387
				c_lp_item_view_2.status AS etat2
388
				FROM '.$tblLpView.' AS c_lp_view
389
				JOIN '.$tblUser.' AS user ON user.user_id = c_lp_view.user_id
390
				JOIN '.$tblCourse.' AS course ON course.id = c_lp_view.c_id
391
				JOIN '.$tblLp.' AS c_lp ON (c_lp.id = c_lp_view.lp_id AND c_lp.c_id = c_lp_view.c_id)
392
				JOIN '.$tblLpItem.' AS c_lp_item ON (c_lp_item.c_id = c_lp_view.c_id AND c_lp_item.lp_id = c_lp_view.lp_id)
393
				LEFT JOIN '.$tblLpItemView.' AS c_lp_item_view ON (c_lp_item_view.lp_item_id = c_lp_item.id AND c_lp_item_view.lp_view_id = c_lp_view.id AND c_lp_item_view.c_id = c_lp_view.c_id)
394
                LEFT JOIN '.$tblLpItem.' AS c_lp_item_2 ON (c_lp_item_2.parent_item_id > 0 AND c_lp_item_2.parent_item_id = c_lp_item.id)
395
				LEFT JOIN '.$tblLpItemView.' AS c_lp_item_view_2 ON (c_lp_item_view_2.lp_item_id = c_lp_item_2.id AND c_lp_item_view_2.lp_view_id = c_lp_view.id AND c_lp_item_view_2.c_id = c_lp_view.c_id)
396
				WHERE
397
				    user.id = '.$userId.' AND
398
				    course.id = '.$courseId.' AND
399
                    c_lp_view.session_id = '.$sessionId.' AND
400
				    c_lp_item.parent_item_id = 0
401
				GROUP BY
402
				    lp_name, chapter, chapter2
403
				ORDER BY
404
				    lp_name, c_lp_item.display_order';
405
                $result = Database::query($sql);
406
                $chapterTitle = false;
407
                while ($row = Database::fetch_array($result)) {
408
                    if ($lpTitle != $row['lp_name']) {
409
                        $lpTitle = $row['lp_name'];
410
                        $lpTable .= '<tr>
411
                        <th>'.stripslashes($lpTitle).'</th>
412
                        <th>'.get_lang('Duration').'</th>
413
                        <th>'.get_lang('Progress').'</th>
414
                    </tr>';
415
                    }
416
                    if ('' == $row['chapter2']) {
417
                        $progression = $row['etat'];
418
                        if (('completed' === $progression) || ('passed' === $progression)) {
419
                            $progression = get_lang('Done');
420
                        } else {
421
                            $progression = get_lang('Incomplete');
422
                        }
423
                        $lpTable .= '<tr>
424
                        <td>'.$row['chapter'].'</td>
425
                        <td>'.$row['temps'].'</td>
426
                        <td>'.$progression.'</td>
427
                    </tr>';
428
                        $chapterTitle = false;
429
                    } else {
430
                        if (false == $chapterTitle) {
431
                            $lpTable .= '<tr>
432
                            <td>'.$row['chapter'].'</td>
433
                            <td></td>
434
                            <td></td>
435
                        </tr>';
436
                            $chapterTitle = true;
437
                        }
438
                        $progression = $row['etat2'];
439
                        if ('completed' == $progression) {
440
                            $progression = get_lang('Done');
441
                        } else {
442
                            $progression = get_lang('Incomplete');
443
                        }
444
                        $lpTable .= '<tr>
445
                        <td>&nbsp;-&nbsp;'.$row['chapter2'].'</td>
446
                        <td>'.$row['temps2'].'</td>
447
                        <td>'.$progression.'</td>
448
                    </tr>';
449
                    }
450
                }
451
            }
452
            $lpTable .= '</tbody></table>';
453
        }
454
455
        return ['progress_table' => $progressTable, 'course_table' => $courseTable, 'lp_table' => $lpTable];
456
    }
457
458
    /**
459
     * It gets table html of Lp stats used to export in pdf.
460
     *
461
     * @return string
462
     */
463
    public static function getLpStatsContentToPdf(
464
        $userId,
465
        $courseInfo,
466
        $sessionId,
467
        $lpId,
468
        $lpName
469
    ) {
470
        $hideTime = api_get_configuration_value('hide_lp_time');
471
        $lpId = (int) $lpId;
472
        $userId = (int) $userId;
473
        $sessionId = (int) $sessionId;
474
        $courseId = $courseInfo['real_id'];
475
        $isAllowedToEdit = api_is_allowed_to_edit(null, true);
476
        $sessionCondition = api_get_session_condition($sessionId);
477
        $counter = 0;
478
        $totalTime = 0;
479
        $h = get_lang('h');
480
        $resultDisabledExtAll = true;
481
        $timeHeader = '<th>'.get_lang('ScormTime').'</th>';
482
        if ($hideTime) {
483
            $timeHeader = '';
484
        }
485
        $output = '<h2 class="clearfix text-center">'.$lpName.'</h2>';
486
        $output .= '<table class="table table-hover table-striped data_table">
487
            <thead>
488
                <tr>
489
                <th>'.get_lang('ScormLessonTitle').'</th>
490
                <th>'.get_lang('ScormStatus').'</th>
491
                <th>'.get_lang('ScormScore').'</th>
492
                '.$timeHeader.'
493
                </tr>
494
            </thead>
495
            <tbody>
496
        ';
497
498
        $tblLpItem = Database::get_course_table(TABLE_LP_ITEM);
499
        $tblLpItemView = Database::get_course_table(TABLE_LP_ITEM_VIEW);
500
        $tblLpView = Database::get_course_table(TABLE_LP_VIEW);
501
        $tblQuizQuestions = Database::get_course_table(TABLE_QUIZ_QUESTION);
502
        $tblQuiz = Database::get_course_table(TABLE_QUIZ_TEST);
503
        $tblStatsExercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
504
        $tblStatsAttempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
505
506
        // it gets the max view
507
        $sql = "SELECT max(view_count) FROM $tblLpView WHERE c_id = $courseId AND lp_id = $lpId AND user_id = $userId $sessionCondition";
508
        $res = Database::query($sql);
509
        $view = 0;
510
        $viewCondition = "";
511
        if (Database::num_rows($res) > 0) {
512
            $view = Database::result($res, 0, 0);
513
            $viewCondition = " AND v.view_count = ".(int) $view;
514
        }
515
516
        $chapterTypes = learnpath::getChapterTypes();
517
        $minimumAvailable = self::minimumTimeAvailable($sessionId, $courseId);
518
        $list = learnpath::get_flat_ordered_items_list($lpId, 0, $courseId);
519
        if (is_array($list) && count($list) > 0) {
520
            foreach ($list as $myItemId) {
521
                $sql = "SELECT
522
                    iv.status as mystatus,
523
                    v.view_count as mycount,
524
                    iv.score as myscore,
525
                    iv.total_time as mytime,
526
                    i.iid as myid,
527
                    i.lp_id as mylpid,
528
                    iv.lp_view_id as mylpviewid,
529
                    i.title as mytitle,
530
                    i.max_score as mymaxscore,
531
                    iv.max_score as myviewmaxscore,
532
                    i.item_type as item_type,
533
                    iv.view_count as iv_view_count,
534
                    iv.id as iv_id,
535
                    path
536
                FROM $tblLpItem as i
537
                INNER JOIN $tblLpItemView as iv
538
                ON (i.iid = iv.lp_item_id AND i.c_id = iv.c_id)
539
                INNER JOIN $tblLpView as v
540
                ON (iv.lp_view_id = v.id AND v.c_id = iv.c_id)
541
                WHERE
542
                    v.c_id = $courseId AND
543
                    i.iid = $myItemId AND
544
                    i.lp_id = $lpId  AND
545
                    v.user_id = $userId AND
546
                    v.session_id = $sessionId
547
                    $viewCondition
548
                ORDER BY iv.view_count";
549
                $result = Database::query($sql);
550
                $num = Database::num_rows($result);
551
                $timeForTotal = 0;
552
553
                if ($num > 0) {
554
                    // Not extended.
555
                    $row = Database::fetch_array($result, 'ASSOC');
556
                    $myId = $row['myid'];
557
                    $myLpId = $row['mylpid'];
558
                    $myLpViewId = $row['mylpviewid'];
559
                    $lpItemPath = (int) $row['path'];
560
                    $resultDisabledExtAll = false;
561
                    if ($row['item_type'] === 'quiz') {
562
                        // Check results_disabled in quiz table.
563
                        $sql = "SELECT results_disabled
564
                                FROM $tblQuiz
565
                                WHERE iid = $lpItemPath";
566
                        $resResultDisabled = Database::query($sql);
567
                        $rowResultDisabled = Database::fetch_row($resResultDisabled);
568
                        if (Database::num_rows($resResultDisabled) > 0 && 1 === (int) $rowResultDisabled[0]) {
569
                            $resultDisabledExtAll = true;
570
                        }
571
                    }
572
573
                    // Check if there are interactions below
574
                    $extendThisAttempt = 0;
575
                    $interNum = learnpath::get_interactions_count_from_db($row['iv_id'], $courseId);
576
                    $objecNum = learnpath::get_objectives_count_from_db($row['iv_id'], $courseId);
577
                    if ($interNum > 0 || $objecNum > 0) {
578
                        $extendThisAttempt = 1;
579
                    }
580
                    $lesson_status = $row['mystatus'];
581
                    $score = $row['myscore'];
582
                    $subtotalTime = $row['mytime'];
583
                    while ($tmp_row = Database::fetch_array($result)) {
584
                        $subtotalTime += $tmp_row['mytime'];
585
                    }
586
587
                    $title = $row['mytitle'];
588
                    // Selecting the exe_id from stats attempts tables in order to look the max score value.
589
                    $sql = 'SELECT * FROM '.$tblStatsExercises.'
590
                            WHERE
591
                                exe_exo_id="'.$row['path'].'" AND
592
                                exe_user_id="'.$userId.'" AND
593
                                orig_lp_id = "'.$lpId.'" AND
594
                                orig_lp_item_id = "'.$row['myid'].'" AND
595
                                c_id = '.$courseId.' AND
596
                                status <> "incomplete" AND
597
                                session_id = '.$sessionId.'
598
                             ORDER BY exe_date DESC
599
                             LIMIT 1';
600
601
                    $resultLastAttempt = Database::query($sql);
602
                    $num = Database::num_rows($resultLastAttempt);
603
                    $idLastAttempt = null;
604
                    if ($num > 0) {
605
                        while ($rowLA = Database::fetch_array($resultLastAttempt)) {
606
                            $idLastAttempt = $rowLA['exe_id'];
607
                        }
608
                    }
609
610
                    switch ($row['item_type']) {
611
                        case 'sco':
612
                            if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
613
                                $maxscore = $row['myviewmaxscore'];
614
                            } elseif ($row['myviewmaxscore'] === '') {
615
                                $maxscore = 0;
616
                            } else {
617
                                $maxscore = $row['mymaxscore'];
618
                            }
619
                            break;
620
                        case 'quiz':
621
                            // Get score and total time from last attempt of an exercise en lp.
622
                            $sql = "SELECT iid, score
623
                                    FROM $tblLpItemView
624
                                    WHERE
625
                                        c_id = $courseId AND
626
                                        lp_item_id = '".(int) $myId."' AND
627
                                        lp_view_id = '".(int) $myLpViewId."'
628
                                    ORDER BY view_count DESC
629
                                    LIMIT 1";
630
                            $resScore = Database::query($sql);
631
                            $rowScore = Database::fetch_array($resScore);
632
633
                            $sql = "SELECT SUM(total_time) as total_time
634
                                    FROM $tblLpItemView
635
                                    WHERE
636
                                        c_id = $courseId AND
637
                                        lp_item_id = '".(int) $myId."' AND
638
                                        lp_view_id = '".(int) $myLpViewId."'";
639
                            $resTime = Database::query($sql);
640
                            $rowTime = Database::fetch_array($resTime);
641
642
                            $score = 0;
643
                            $subtotalTime = 0;
644
                            if (Database::num_rows($resScore) > 0 && Database::num_rows($resTime) > 0) {
645
                                $score = (float) $rowScore['score'];
646
                                $subtotalTime = (int) $rowTime['total_time'];
647
                            }
648
                            // Selecting the max score from an attempt.
649
                            $sql = "SELECT SUM(t.ponderation) as maxscore
650
                                    FROM (
651
                                        SELECT DISTINCT
652
                                            question_id, marks, ponderation
653
                                        FROM $tblStatsAttempts as at
654
                                        INNER JOIN $tblQuizQuestions as q
655
                                        ON q.iid = at.question_id
656
                                        WHERE exe_id ='$idLastAttempt'
657
                                    ) as t";
658
659
                            $result = Database::query($sql);
660
                            $rowMaxScore = Database::fetch_array($result);
661
                            $maxscore = $rowMaxScore['maxscore'];
662
663
                            // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
664
                            $sql = 'SELECT SUM(exe_duration) exe_duration
665
                                    FROM '.$tblStatsExercises.'
666
                                    WHERE
667
                                        exe_exo_id="'.$row['path'].'" AND
668
                                        exe_user_id="'.$userId.'" AND
669
                                        orig_lp_id = "'.$lpId.'" AND
670
                                        orig_lp_item_id = "'.$row['myid'].'" AND
671
                                        c_id = '.$courseId.' AND
672
                                        status <> "incomplete" AND
673
                                        session_id = '.$sessionId.'
674
                                     ORDER BY exe_date DESC ';
675
                            $sumScoreResult = Database::query($sql);
676
                            $durationRow = Database::fetch_array($sumScoreResult, 'ASSOC');
677
                            if (!empty($durationRow['exe_duration'])) {
678
                                $exeDuration = $durationRow['exe_duration'];
679
                                if ($exeDuration != $subtotalTime && !empty($rowScore['iid']) && !empty($exeDuration)) {
680
                                    $subtotalTime = $exeDuration;
681
                                    // Update c_lp_item_view.total_time
682
                                    $sqlUpdate = "UPDATE $tblLpItemView SET total_time = '$exeDuration' WHERE iid = ".$rowScore['iid'];
683
                                    Database::query($sqlUpdate);
684
                                }
685
                            }
686
                            break;
687
                        default:
688
                            $maxscore = $row['mymaxscore'];
689
                            break;
690
                    }
691
692
                    $timeForTotal = $subtotalTime;
693
                    $time = learnpathItem::getScormTimeFromParameter('js', $subtotalTime);
694
                    if (empty($title)) {
695
                        $title = learnpath::rl_get_resource_name(
696
                            $courseInfo['code'],
697
                            $lpId,
698
                            $row['myid']
699
                        );
700
                    }
701
702
                    if (in_array($row['item_type'], $chapterTypes)) {
703
                        $title = Security::remove_XSS($title);
704
                        $output .= '<tr>
705
                                <td colspan="4"><h4>'.$title.'</h4></td>
706
                        </tr>';
707
                    } else {
708
                        if ('quiz' === $row['item_type']) {
709
                            $sql = 'SELECT * FROM '.$tblStatsExercises.'
710
                                     WHERE
711
                                        exe_exo_id="'.$row['path'].'" AND
712
                                        exe_user_id="'.$userId.'" AND
713
                                        orig_lp_id = "'.$lpId.'" AND
714
                                        orig_lp_item_id = "'.$row['myid'].'" AND
715
                                        c_id = '.$courseId.' AND
716
                                        status <> "incomplete" AND
717
                                        session_id = '.$sessionId.'
718
                                     ORDER BY exe_date DESC ';
719
                            $resultLastAttempt = Database::query($sql);
720
                            $num = Database::num_rows($resultLastAttempt);
721
                        }
722
723
                        $title = Security::remove_XSS($title);
724
                        if ($lpId == $myLpId && false) {
725
                            $output .= '<tr>
726
                                    <td>'.$title.'</td>
727
                                    <td>&nbsp;</td>
728
                                    <td>&nbsp;</td>
729
                                    <td>&nbsp;</td>
730
                            </tr>';
731
                        } else {
732
                            $output .= "<tr>";
733
                            $scoreItem = null;
734
                            if ($row['item_type'] === 'quiz') {
735
                                $scoreItem .= ExerciseLib::show_score($score, $maxscore, false);
736
                            } else {
737
                                $scoreItem .= $score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.$maxscore);
738
                            }
739
                            $timeRow = '<td style="width:20%;text-align:center;">'.$time.'</td>';
740
                            if ($hideTime) {
741
                                $timeRow = '';
742
                            }
743
                            $output .= '
744
                                <td style="width:35%;font-weight:bold;">'.$title.'</td>
745
                                <td style="width:25%;text-align:center;"><div class="btn btn-primary boldTitle">'.learnpathitem::humanize_status($lesson_status, false).'</div></td>
746
                                <td style="width:20%;text-align:center;">'.$scoreItem.'</td>
747
                                '.$timeRow.'
748
                             ';
749
                            $output .= '</tr>';
750
                        }
751
                    }
752
753
                    $counter++;
754
                    if ($extendThisAttempt) {
755
                        $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $courseId);
756
                        foreach ($list1 as $id => $interaction) {
757
                            $timeRow = '<td>'.$interaction['time'].'</td>';
758
                            if ($hideTime) {
759
                                $timeRow = '';
760
                            }
761
762
                            $output .= '<tr>
763
                                    <td>'.$interaction['order_id'].'</td>
764
                                    <td>'.$interaction['id'].'</td>
765
                                    <td>'.$interaction['type'].'</td>
766
                                    <td>'.urldecode($interaction['student_response']).'</td>
767
                                    <td>'.$interaction['result'].'</td>
768
                                    <td>'.$interaction['latency'].'</td>
769
                                    '.$timeRow.'
770
                               </tr>';
771
                            $counter++;
772
                        }
773
774
                        $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $courseId);
775
                        foreach ($list2 as $id => $interaction) {
776
                            $output .= '<tr>
777
                                    <td>'.$interaction['order_id'].'</td>
778
                                    <td>'.$interaction['objective_id'].'</td>
779
                                    <td>'.$interaction['status'].'</td>
780
                                    <td>'.$interaction['score_raw'].'</td>
781
                                    <td>'.$interaction['score_max'].'</td>
782
                                    <td>'.$interaction['score_min'].'</td>
783
                               </tr>';
784
                            $counter++;
785
                        }
786
                    }
787
788
                    // Attempts listing by exercise.
789
                    if ($lpId == $myLpId) {
790
                        // Get attempts of a exercise.
791
                        if (!empty($lpId) && 'quiz' === $row['item_type']) {
792
                            $sql = "SELECT path FROM $tblLpItem
793
                                    WHERE
794
                                        c_id = $courseId AND
795
                                        lp_id = '$lpId'";
796
                            $resPath = Database::query($sql);
797
                            if (Database::num_rows($resPath) > 0) {
798
                                while ($rowPath = Database::fetch_array($resPath)) {
799
                                    $sql = 'SELECT * FROM '.$tblStatsExercises.'
800
                                            WHERE
801
                                                exe_exo_id="'.(int) $rowPath['path'].'" AND
802
                                                status <> "incomplete" AND
803
                                                exe_user_id="'.$userId.'" AND
804
                                                orig_lp_id = "'.$lpId.'" AND
805
                                                orig_lp_item_id = "'.$myItemId.'" AND
806
                                                c_id = '.$courseId.'  AND
807
                                                session_id = '.$sessionId.'
808
                                            ORDER BY exe_date';
809
                                    $resAttempts = Database::query($sql);
810
                                    if (Database::num_rows($resAttempts) > 0) {
811
                                        $n = 1;
812
                                        while ($rowAttempts = Database::fetch_array($resAttempts)) {
813
                                            $myScore = $rowAttempts['exe_result'];
814
                                            $myMaxscore = $rowAttempts['exe_weighting'];
815
                                            $mktimeStartDate = api_strtotime($rowAttempts['start_date'], 'UTC');
816
                                            $mktimeExeDate = api_strtotime($rowAttempts['exe_date'], 'UTC');
817
                                            $timeAttempt = ' - ';
818
                                            if ($mktimeStartDate && $mktimeExeDate) {
819
                                                $timeAttempt = api_format_time($rowAttempts['exe_duration'], 'js');
820
                                            }
821
                                            // Show only float when need it
822
                                            if ($myScore == 0) {
823
                                                $viewScore = ExerciseLib::show_score(
824
                                                    0,
825
                                                    $myMaxscore,
826
                                                    false
827
                                                );
828
                                            } else {
829
                                                if ($myMaxscore == 0) {
830
                                                    $viewScore = $myScore;
831
                                                } else {
832
                                                    $viewScore = ExerciseLib::show_score(
833
                                                        $myScore,
834
                                                        $myMaxscore,
835
                                                        false
836
                                                    );
837
                                                }
838
                                            }
839
                                            $myLessonStatus = $rowAttempts['status'];
840
                                            if ($myLessonStatus == '') {
841
                                                $myLessonStatus = learnpathitem::humanize_status('completed', false);
842
                                            } elseif ($myLessonStatus == 'incomplete') {
843
                                                $myLessonStatus = learnpathitem::humanize_status('incomplete', false);
844
                                            }
845
                                            $timeRow = '<td style="text-align:center;">'.$timeAttempt.'</td>';
846
                                            if ($hideTime) {
847
                                                $timeRow = '';
848
                                            }
849
                                            $output .= '<tr>
850
                                            <td>&mdash; <em>'.get_lang('Attempt').' '.$n.'</em></td>
851
                                            <td style="text-align:center;"><div class="btn btn-primary boldTitle">'.$myLessonStatus.'</div></td>
852
                                            <td style="text-align:center;">'.$viewScore.'</td>
853
                                            '.$timeRow;
854
                                            $output .= '</tr>';
855
856
                                            // It displays categories questions by attempts
857
                                            $categoriesQuestions = TestCategory::getQuestionsByCat($rowAttempts['exe_exo_id'], [], [], false, $courseId);
858
                                            $categories = TestCategory::getListOfCategoriesIDForTest($rowAttempts['exe_exo_id'], $courseId);
859
                                            $objExercise = new Exercise($courseId);
860
                                            $objExercise->read($rowAttempts['exe_exo_id']);
861
                                            if (!empty($categoriesQuestions)) {
862
                                                foreach ($categoriesQuestions as $catId => $questions) {
863
                                                    $catScore = 0;
864
                                                    $catWeight = 0;
865
                                                    if (!empty($questions)) {
866
                                                        foreach ($questions as $questionId) {
867
                                                            $questionResult = $objExercise->manage_answer(
868
                                                                $rowAttempts['exe_id'],
869
                                                                $questionId,
870
                                                                '',
871
                                                                'exercise_show',
872
                                                                [],
873
                                                                false,
874
                                                                true,
875
                                                                false,
876
                                                                $objExercise->selectPropagateNeg()
877
                                                            );
878
                                                            $catScore += $questionResult['score'];
879
                                                            $catWeight += $questionResult['weight'];
880
                                                        }
881
                                                    }
882
                                                    $timeCatRow = '';
883
                                                    if (!$hideTime) {
884
                                                        $timeCatRow = '<td></td>';
885
                                                    }
886
                                                    $catTitle = $categories[$catId]['title'];
887
                                                    $catScoreDisplay = ($catWeight > 0) ? round(($catScore * 100) / $catWeight).'% ('.$catScore.'/'.$catWeight.')' : '';
888
                                                    $output .= "<tr>";
889
                                                    $output .= '
890
                                                        <td style="width:35%">&mdash;&mdash; '.$catTitle.'</td>
891
                                                        <td style="width:25%;"></td>
892
                                                        <td style="width:20%;text-align:center;">'.$catScoreDisplay.'</td>
893
                                                        '.$timeCatRow.'
894
                                                     ';
895
                                                    $output .= '</tr>';
896
                                                }
897
                                            }
898
                                            $n++;
899
                                        }
900
                                    }
901
                                }
902
                            }
903
                        }
904
                    }
905
                }
906
                $totalTime += $timeForTotal;
907
            }
908
        }
909
910
        $totalScore = self::get_avg_student_score(
911
            $userId,
912
            $courseInfo['code'],
913
            [$lpId],
914
            $sessionId,
915
            false,
916
            false
917
        );
918
919
        $totalTime = learnpathItem::getScormTimeFromParameter('js', $totalTime);
920
        $totalTime = str_replace('NaN', '00'.$h.'00\'00"', $totalTime);
921
922
        if (!$isAllowedToEdit && $resultDisabledExtAll) {
923
            $finalScore = Display::return_icon('invisible.png', get_lang('ResultsHiddenByExerciseSetting'));
924
        } else {
925
            if (is_numeric($totalScore)) {
926
                $finalScore = $totalScore.'%';
927
            } else {
928
                $finalScore = $totalScore;
929
            }
930
        }
931
        $progress = learnpath::getProgress($lpId, $userId, $courseId, $sessionId);
932
        $timeTotal = '<th style="text-align:center;">'.$totalTime.'</th>';
933
        if ($hideTime) {
934
            $timeTotal = '';
935
        }
936
937
        $output .= '<tr>
938
                <th><i>'.get_lang('AccomplishedStepsTotal').'</i></th>
939
                <th style="text-align:center;">'.$progress.'%</th>
940
                <th style="text-align:center;">'.$finalScore.'</th>
941
                '.$timeTotal.'
942
           </tr></tbody></table>';
943
944
        return $output;
945
    }
946
947
    /**
948
     * @param int    $user_id
949
     * @param array  $courseInfo
950
     * @param int    $session_id
951
     * @param string $origin
952
     * @param bool   $export_csv
953
     * @param int    $lp_id
954
     * @param int    $lp_item_id
955
     * @param int    $extendId
956
     * @param int    $extendAttemptId
957
     * @param string $extendedAttempt
958
     * @param string $extendedAll
959
     * @param string $type            classic or simple
960
     * @param bool   $allowExtend     Optional. Allow or not extend te results
961
     *
962
     * @return string
963
     */
964
    public static function getLpStats(
965
        $user_id,
966
        $courseInfo,
967
        $session_id,
968
        $origin,
969
        $export_csv,
970
        $lp_id,
971
        $lp_item_id = null,
972
        $extendId = null,
973
        $extendAttemptId = null,
974
        $extendedAttempt = null,
975
        $extendedAll = null,
976
        $type = 'classic',
977
        $allowExtend = true
978
    ) {
979
        if (empty($courseInfo) || empty($lp_id)) {
980
            return '';
981
        }
982
983
        $hideTime = api_get_configuration_value('hide_lp_time');
984
        $lp_id = (int) $lp_id;
985
        $lp_item_id = (int) $lp_item_id;
986
        $user_id = (int) $user_id;
987
        $session_id = (int) $session_id;
988
        $origin = Security::remove_XSS($origin);
989
        $list = learnpath::get_flat_ordered_items_list($lp_id, 0, $courseInfo['real_id']);
990
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
991
        $course_id = $courseInfo['real_id'];
992
        $courseCode = $courseInfo['code'];
993
        $session_condition = api_get_session_condition($session_id);
994
995
        // Extend all button
996
        $output = '';
997
998
        $extra = '<script>
999
        $(function() {
1000
            $( "#dialog:ui-dialog" ).dialog( "destroy" );
1001
            $( "#dialog-confirm" ).dialog({
1002
                autoOpen: false,
1003
                show: "blind",
1004
                resizable: false,
1005
                height:300,
1006
                modal: true
1007
            });
1008
1009
            $(".export").click(function() {
1010
                var targetUrl = $(this).attr("href");
1011
                $( "#dialog-confirm" ).dialog({
1012
                    width:400,
1013
                    height:300,
1014
                    buttons: {
1015
                        "'.addslashes(get_lang('Download')).'": function() {
1016
                            var option = $("input[name=add_logo]:checked").val();
1017
                            location.href = targetUrl+"&add_logo="+option;
1018
                            $(this).dialog("close");
1019
                        }
1020
                    }
1021
                });
1022
                $("#dialog-confirm").dialog("open");
1023
1024
                return false;
1025
            });
1026
        });
1027
        </script>';
1028
1029
        $extra .= '<div id="dialog-confirm" title="'.get_lang('ConfirmYourChoice').'">';
1030
        $form = new FormValidator('report', 'post', null, null, ['class' => 'form-vertical']);
1031
        $form->addCheckBox('add_logo', '', get_lang('AddRightLogo'), ['id' => 'export_format_csv_label']);
1032
        $extra .= $form->returnForm();
1033
        $extra .= '</div>';
1034
1035
        $output .= $extra;
1036
1037
        $url_suffix = '&lp_id='.$lp_id;
1038
        if ('tracking' === $origin) {
1039
            $url_suffix = '&session_id='.$session_id.'&course='.$courseCode.'&student_id='.$user_id.'&lp_id='.$lp_id.'&origin='.$origin;
1040
        }
1041
1042
        $extend_all = 0;
1043
        if (!empty($extendedAll)) {
1044
            $extend_all_link = Display::url(
1045
                Display::return_icon('view_less_stats.gif', get_lang('HideAllAttempts')),
1046
                api_get_self().'?action=stats'.$url_suffix
1047
            );
1048
            $extend_all = 1;
1049
        } else {
1050
            $extend_all_link = Display::url(
1051
                Display::return_icon('view_more_stats.gif', get_lang('ShowAllAttempts')),
1052
                api_get_self().'?action=stats&extend_all=1'.$url_suffix
1053
            );
1054
        }
1055
1056
        if ($origin != 'tracking') {
1057
            $output .= '<div class="section-status">';
1058
            $output .= Display::page_header(get_lang('ScormMystatus'));
1059
            $output .= '</div>';
1060
        }
1061
1062
        $actionColumn = null;
1063
        if ($type === 'classic') {
1064
            $actionColumn = ' <th>'.get_lang('Actions').'</th>';
1065
        }
1066
1067
        $timeHeader = '<th class="lp_time" colspan="2">'.get_lang('ScormTime').'</th>';
1068
        if ($hideTime) {
1069
            $timeHeader = '';
1070
        }
1071
        $output .= '<div class="table-responsive">';
1072
        $output .= '<table id="lp_tracking" class="table tracking">
1073
            <thead>
1074
            <tr class="table-header">
1075
                <th width="16">'.($allowExtend == true ? $extend_all_link : '&nbsp;').'</th>
1076
                <th colspan="4">
1077
                    '.get_lang('ScormLessonTitle').'
1078
                </th>
1079
                <th colspan="2">
1080
                    '.get_lang('ScormStatus').'
1081
                </th>
1082
                <th colspan="2">
1083
                    '.get_lang('ScormScore').'
1084
                </th>
1085
                '.$timeHeader.'
1086
                '.$actionColumn.'
1087
                </tr>
1088
            </thead>
1089
            <tbody>
1090
        ';
1091
1092
        // Going through the items using the $items[] array instead of the database order ensures
1093
        // we get them in the same order as in the imsmanifest file, which is rather random when using
1094
        // the database table.
1095
        $TBL_LP_ITEM = Database::get_course_table(TABLE_LP_ITEM);
1096
        $TBL_LP_ITEM_VIEW = Database::get_course_table(TABLE_LP_ITEM_VIEW);
1097
        $TBL_LP_VIEW = Database::get_course_table(TABLE_LP_VIEW);
1098
        $tbl_quiz_questions = Database::get_course_table(TABLE_QUIZ_QUESTION);
1099
        $TBL_QUIZ = Database::get_course_table(TABLE_QUIZ_TEST);
1100
        $tbl_stats_exercices = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1101
        $tbl_stats_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1102
1103
        $sql = "SELECT max(view_count)
1104
                FROM $TBL_LP_VIEW
1105
                WHERE
1106
                    c_id = $course_id AND
1107
                    lp_id = $lp_id AND
1108
                    user_id = $user_id
1109
                    $session_condition";
1110
        $res = Database::query($sql);
1111
        $view = 0;
1112
        if (Database::num_rows($res) > 0) {
1113
            $myrow = Database::fetch_array($res);
1114
            $view = (int) $myrow[0];
1115
        }
1116
1117
        $counter = 0;
1118
        $total_time = 0;
1119
        $h = get_lang('h');
1120
1121
        if (!empty($export_csv)) {
1122
            $csvHeaders = [
1123
                get_lang('ScormLessonTitle'),
1124
                get_lang('ScormStatus'),
1125
                get_lang('ScormScore'),
1126
            ];
1127
1128
            if ($hideTime === false) {
1129
                $csvHeaders[] = get_lang('ScormTime');
1130
            }
1131
1132
            $csv_content[] = $csvHeaders;
1133
        }
1134
1135
        $result_disabled_ext_all = true;
1136
        $chapterTypes = learnpath::getChapterTypes();
1137
        $accessToPdfExport = api_is_allowed_to_edit(false, false, true);
1138
1139
        $minimumAvailable = self::minimumTimeAvailable($session_id, $course_id);
1140
        $timeCourse = [];
1141
        if ($minimumAvailable) {
1142
            $timeCourse = self::getCalculateTime($user_id, $course_id, $session_id);
1143
            Session::write('trackTimeCourse', $timeCourse);
1144
        }
1145
1146
        // Show lp items
1147
        if (is_array($list) && count($list) > 0) {
1148
            foreach ($list as $my_item_id) {
1149
                $extend_this = 0;
1150
                $order = 'DESC';
1151
                if ((!empty($extendId) && $extendId == $my_item_id) || $extend_all) {
1152
                    $extend_this = 1;
1153
                    $order = 'ASC';
1154
                }
1155
1156
                // Prepare statement to go through each attempt.
1157
                $viewCondition = null;
1158
                if (!empty($view)) {
1159
                    $viewCondition = " AND v.view_count = $view  ";
1160
                }
1161
1162
                $sql = "SELECT
1163
                    iv.status as mystatus,
1164
                    v.view_count as mycount,
1165
                    iv.score as myscore,
1166
                    iv.total_time as mytime,
1167
                    i.iid as myid,
1168
                    i.lp_id as mylpid,
1169
                    iv.lp_view_id as mylpviewid,
1170
                    i.title as mytitle,
1171
                    i.max_score as mymaxscore,
1172
                    iv.max_score as myviewmaxscore,
1173
                    i.item_type as item_type,
1174
                    iv.view_count as iv_view_count,
1175
                    iv.id as iv_id,
1176
                    path
1177
                FROM $TBL_LP_ITEM as i
1178
                INNER JOIN $TBL_LP_ITEM_VIEW as iv
1179
                ON (i.iid = iv.lp_item_id AND i.c_id = iv.c_id)
1180
                INNER JOIN $TBL_LP_VIEW as v
1181
                ON (iv.lp_view_id = v.id AND v.c_id = iv.c_id)
1182
                WHERE
1183
                    v.c_id = $course_id AND
1184
                    i.iid = $my_item_id AND
1185
                    i.lp_id = $lp_id  AND
1186
                    v.user_id = $user_id AND
1187
                    v.session_id = $session_id
1188
                    $viewCondition
1189
                ORDER BY iv.view_count $order ";
1190
1191
                $result = Database::query($sql);
1192
                $num = Database::num_rows($result);
1193
                $time_for_total = 0;
1194
1195
                // Extend all
1196
                if (($extend_this || $extend_all) && $num > 0) {
1197
                    $row = Database::fetch_array($result);
1198
                    $result_disabled_ext_all = false;
1199
                    if ('quiz' === $row['item_type']) {
1200
                        // Check results_disabled in quiz table.
1201
                        $lpItemPath = (int) $row['path'];
1202
                        $sql = "SELECT results_disabled
1203
                                FROM $TBL_QUIZ
1204
                                WHERE
1205
                                    iid = $lpItemPath";
1206
                        $res_result_disabled = Database::query($sql);
1207
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
1208
1209
                        if (Database::num_rows($res_result_disabled) > 0
1210
                            && 1 === (int) $row_result_disabled[0]
1211
                        ) {
1212
                            $result_disabled_ext_all = true;
1213
                        }
1214
                    }
1215
1216
                    // If there are several attempts, and the link to extend has been clicked, show each attempt...
1217
                    $oddclass = 'row_even';
1218
                    if (($counter % 2) === 0) {
1219
                        $oddclass = 'row_odd';
1220
                    }
1221
                    $extend_link = '';
1222
                    if (!empty($inter_num)) {
1223
                        $extend_link = Display::url(
1224
                            Display::return_icon(
1225
                                'visible.png',
1226
                                get_lang('HideAttemptView')
1227
                            ),
1228
                            api_get_self().'?action=stats&fold_id='.$my_item_id.$url_suffix
1229
                        );
1230
                    }
1231
                    $title = $row['mytitle'];
1232
1233
                    if (empty($title)) {
1234
                        $title = learnpath::rl_get_resource_name($courseInfo['code'], $lp_id, $row['myid']);
1235
                    }
1236
1237
                    if (in_array($row['item_type'], $chapterTypes)) {
1238
                        $title = "<h4> $title </h4>";
1239
                    }
1240
                    $lesson_status = $row['mystatus'];
1241
                    $title = Security::remove_XSS($title);
1242
                    $counter++;
1243
1244
                    $action = null;
1245
                    if ('classic' === $type) {
1246
                        $action = '<td></td>';
1247
                    }
1248
1249
                    if (in_array($row['item_type'], $chapterTypes)) {
1250
                        $output .= '<tr class="'.$oddclass.'">
1251
                                <td>'.$extend_link.'</td>
1252
                                <td colspan="4">
1253
                                   '.$title.'
1254
                                </td>
1255
                                <td colspan="2">'.learnpathItem::humanize_status($lesson_status, true, $type).'</td>
1256
                                <td colspan="2"></td>
1257
                                <td colspan="2"></td>
1258
                                '.$action.'
1259
                            </tr>';
1260
                        continue;
1261
                    } else {
1262
                        $output .= '<tr class="'.$oddclass.'">
1263
                                <td>'.$extend_link.'</td>
1264
                                <td colspan="4">'.$title.'</td>
1265
                                <td colspan="2"></td>
1266
                                <td colspan="2"></td>
1267
                                <td colspan="2"></td>
1268
                                '.$action.'
1269
                            </tr>';
1270
                    }
1271
1272
                    $attemptCount = 1;
1273
                    do {
1274
                        // Check if there are interactions below.
1275
                        $extend_attempt_link = '';
1276
                        $extend_this_attempt = 0;
1277
1278
                        if ($timeCourse) {
1279
                            // $attemptResult = 0;
1280
                            if (isset($timeCourse['learnpath_detailed'])
1281
                                && isset($timeCourse['learnpath_detailed'][$lp_id])
1282
                                && isset($timeCourse['learnpath_detailed'][$lp_id][$my_item_id])
1283
                            ) {
1284
                                $attemptResult = $timeCourse['learnpath_detailed'][$lp_id][$my_item_id][$row['iv_view_count']];
1285
                            }
1286
                        }
1287
                        if ((
1288
                            learnpath::get_interactions_count_from_db($row['iv_id'], $course_id) > 0
1289
                            || learnpath::get_objectives_count_from_db($row['iv_id'], $course_id) > 0
1290
                        )
1291
                        && !$extend_all
1292
                        ) {
1293
                            if ($extendAttemptId == $row['iv_id']) {
1294
                                // The extend button for this attempt has been clicked.
1295
                                $extend_this_attempt = 1;
1296
                                $extend_attempt_link = Display::url(
1297
                                    Display::return_icon('visible.png', get_lang('HideAttemptView')),
1298
                                    api_get_self().'?action=stats&extend_id='.$my_item_id.'&fold_attempt_id='.$row['iv_id'].$url_suffix
1299
                                );
1300
                                if ($accessToPdfExport) {
1301
                                    $extend_attempt_link .= '&nbsp;'.
1302
                                        Display::url(
1303
                                            Display::return_icon('pdf.png', get_lang('ExportToPdf')),
1304
                                            api_get_self().'?action=export_stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix,
1305
                                            ['class' => 'export']
1306
                                        );
1307
                                }
1308
                            } else { // Same case if fold_attempt_id is set, so not implemented explicitly.
1309
                                // The extend button for this attempt has not been clicked.
1310
                                $extend_attempt_link = Display::url(
1311
                                    Display::return_icon('invisible.png', get_lang('ExtendAttemptView')),
1312
                                    api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
1313
                                );
1314
                                if ($accessToPdfExport) {
1315
                                    $extend_attempt_link .= '&nbsp;'.
1316
                                        Display::url(
1317
                                            Display::return_icon('pdf.png', get_lang('ExportToPdf')),
1318
                                            api_get_self().'?action=export_stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix,
1319
                                            ['class' => 'export']
1320
                                        );
1321
                                }
1322
                            }
1323
                        }
1324
1325
                        $oddclass = 'row_even';
1326
                        if (($counter % 2) == 0) {
1327
                            $oddclass = 'row_odd';
1328
                        }
1329
1330
                        $lesson_status = $row['mystatus'];
1331
                        $score = $row['myscore'];
1332
                        $time_for_total += $row['mytime'];
1333
                        $attemptTime = $row['mytime'];
1334
1335
                        if ($minimumAvailable) {
1336
                            /*$lp_time = $timeCourse[TOOL_LEARNPATH];
1337
                            $lpTime = null;
1338
                            if (isset($lp_time[$lp_id])) {
1339
                                $lpTime = (int) $lp_time[$lp_id];
1340
                            }
1341
                            $time_for_total = $lpTime;*/
1342
                            $time_for_total = (int) $attemptResult;
1343
                            $attemptTime = (int) $attemptResult;
1344
                        }
1345
1346
                        $time = learnpathItem::getScormTimeFromParameter('js', $attemptTime);
1347
1348
                        if ($score == 0) {
1349
                            $maxscore = $row['mymaxscore'];
1350
                        } else {
1351
                            if ($row['item_type'] === 'sco') {
1352
                                if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
1353
                                    $maxscore = $row['myviewmaxscore'];
1354
                                } elseif ($row['myviewmaxscore'] === '') {
1355
                                    $maxscore = 0;
1356
                                } else {
1357
                                    $maxscore = $row['mymaxscore'];
1358
                                }
1359
                            } else {
1360
                                $maxscore = $row['mymaxscore'];
1361
                            }
1362
                        }
1363
1364
                        // Remove "NaN" if any (@todo: locate the source of these NaN)
1365
                        $time = str_replace('NaN', '00'.$h.'00\'00"', $time);
1366
                        if ($row['item_type'] !== 'dir') {
1367
                            if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1368
                                $view_score = Display::return_icon(
1369
                                    'invisible.png',
1370
                                    get_lang('ResultsHiddenByExerciseSetting')
1371
                                );
1372
                            } else {
1373
                                switch ($row['item_type']) {
1374
                                    case 'sco':
1375
                                        if ($maxscore == 0) {
1376
                                            $view_score = $score;
1377
                                        } else {
1378
                                            $view_score = ExerciseLib::show_score(
1379
                                                $score,
1380
                                                $maxscore,
1381
                                                false
1382
                                            );
1383
                                        }
1384
                                        break;
1385
                                    case 'document':
1386
                                        $view_score = ($score == 0 ? '/' : ExerciseLib::show_score($score, $maxscore, false));
1387
                                        break;
1388
                                    default:
1389
                                        $view_score = ExerciseLib::show_score(
1390
                                            $score,
1391
                                            $maxscore,
1392
                                            false
1393
                                        );
1394
                                        break;
1395
                                }
1396
                            }
1397
1398
                            $action = null;
1399
                            if ($type === 'classic') {
1400
                                $action = '<td></td>';
1401
                            }
1402
                            $timeRow = '<td class="lp_time" colspan="2">'.$time.'</td>';
1403
                            if ($hideTime) {
1404
                                $timeRow = '';
1405
                            }
1406
                            $output .= '<tr class="'.$oddclass.'">
1407
                                    <td></td>
1408
                                    <td style="width:70px;float:left;">'.$extend_attempt_link.'</td>
1409
                                    <td colspan="3">'.get_lang('Attempt').' '.$attemptCount.'</td>
1410
                                    <td colspan="2">'.learnpathItem::humanize_status($lesson_status, true, $type).'</td>
1411
                                    <td colspan="2">'.$view_score.'</td>
1412
                                    '.$timeRow.'
1413
                                    '.$action.'
1414
                                </tr>';
1415
                            $attemptCount++;
1416
                            if (!empty($export_csv)) {
1417
                                $temp = [];
1418
                                $temp[] = $title = Security::remove_XSS($title);
1419
                                $temp[] = Security::remove_XSS(
1420
                                    learnpathItem::humanize_status($lesson_status, false, $type)
1421
                                );
1422
1423
                                if ($row['item_type'] === 'quiz') {
1424
                                    if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1425
                                        $temp[] = '/';
1426
                                    } else {
1427
                                        $temp[] = ($score == 0 ? '0/'.$maxscore : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
1428
                                    }
1429
                                } else {
1430
                                    $temp[] = ($score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
1431
                                }
1432
1433
                                if ($hideTime === false) {
1434
                                    $temp[] = $time;
1435
                                }
1436
                                $csv_content[] = $temp;
1437
                            }
1438
                        }
1439
1440
                        $counter++;
1441
                        $action = null;
1442
                        if ($type === 'classic') {
1443
                            $action = '<td></td>';
1444
                        }
1445
1446
                        if ($extend_this_attempt || $extend_all) {
1447
                            $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $course_id);
1448
                            foreach ($list1 as $id => $interaction) {
1449
                                $oddclass = 'row_even';
1450
                                if (($counter % 2) == 0) {
1451
                                    $oddclass = 'row_odd';
1452
                                }
1453
                                $timeRow = '<td class="lp_time">'.$interaction['time'].'</td>';
1454
                                if ($hideTime) {
1455
                                    $timeRow = '';
1456
                                }
1457
1458
                                $output .= '<tr class="'.$oddclass.'">
1459
                                        <td></td>
1460
                                        <td></td>
1461
                                        <td></td>
1462
                                        <td>'.$interaction['order_id'].'</td>
1463
                                        <td>'.$interaction['id'].'</td>';
1464
1465
                                $output .= '
1466
                                        <td colspan="2">'.$interaction['type'].'</td>
1467
                                        <td>'.$interaction['student_response_formatted'].'</td>
1468
                                        <td>'.$interaction['result'].'</td>
1469
                                        <td>'.$interaction['latency'].'</td>
1470
                                        '.$timeRow.'
1471
                                        '.$action.'
1472
                                    </tr>';
1473
                                $counter++;
1474
                            }
1475
                            $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $course_id);
1476
                            foreach ($list2 as $id => $interaction) {
1477
                                $oddclass = 'row_even';
1478
                                if (($counter % 2) === 0) {
1479
                                    $oddclass = 'row_odd';
1480
                                }
1481
                                $output .= '<tr class="'.$oddclass.'">
1482
                                        <td></td>
1483
                                        <td></td>
1484
                                        <td></td>
1485
                                        <td>'.$interaction['order_id'].'</td>
1486
                                        <td colspan="2">'.$interaction['objective_id'].'</td>
1487
                                        <td colspan="2">'.$interaction['status'].'</td>
1488
                                        <td>'.$interaction['score_raw'].'</td>
1489
                                        <td>'.$interaction['score_max'].'</td>
1490
                                        <td>'.$interaction['score_min'].'</td>
1491
                                        '.$action.'
1492
                                     </tr>';
1493
                                $counter++;
1494
                            }
1495
                        }
1496
                    } while ($row = Database::fetch_array($result));
1497
                } elseif ($num > 0) {
1498
                    // Not extended.
1499
                    $row = Database::fetch_array($result, 'ASSOC');
1500
                    $my_id = $row['myid'];
1501
                    $my_lp_id = $row['mylpid'];
1502
                    $my_lp_view_id = $row['mylpviewid'];
1503
                    $lpItemPath = (int) $row['path'];
1504
                    $result_disabled_ext_all = false;
1505
                    if ($row['item_type'] === 'quiz') {
1506
                        // Check results_disabled in quiz table.
1507
                        $sql = "SELECT results_disabled
1508
                                FROM $TBL_QUIZ
1509
                                WHERE iid = $lpItemPath";
1510
                        $res_result_disabled = Database::query($sql);
1511
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
1512
1513
                        if (Database::num_rows($res_result_disabled) > 0
1514
                            && (int) $row_result_disabled[0] === 1
1515
                        ) {
1516
                            $result_disabled_ext_all = true;
1517
                        }
1518
                    }
1519
1520
                    // Check if there are interactions below
1521
                    $extend_this_attempt = 0;
1522
                    $inter_num = learnpath::get_interactions_count_from_db($row['iv_id'], $course_id);
1523
                    $objec_num = learnpath::get_objectives_count_from_db($row['iv_id'], $course_id);
1524
                    $extend_attempt_link = '';
1525
                    if ($inter_num > 0 || $objec_num > 0) {
1526
                        if (!empty($extendAttemptId) && $extendAttemptId == $row['iv_id']) {
1527
                            // The extend button for this attempt has been clicked.
1528
                            $extend_this_attempt = 1;
1529
                            $extend_attempt_link = Display::url(
1530
                                Display::return_icon('visible.png', get_lang('HideAttemptView')),
1531
                                api_get_self().'?action=stats&extend_id='.$my_item_id.'&fold_attempt_id='.$row['iv_id'].$url_suffix
1532
                            );
1533
                        } else {
1534
                            // Same case if fold_attempt_id is set, so not implemented explicitly.
1535
                            // The extend button for this attempt has not been clicked.
1536
                            $extend_attempt_link = Display::url(
1537
                                Display::return_icon('invisible.png', get_lang('ExtendAttemptView')),
1538
                                api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
1539
                            );
1540
                        }
1541
                    }
1542
1543
                    $oddclass = 'row_even';
1544
                    if (($counter % 2) == 0) {
1545
                        $oddclass = 'row_odd';
1546
                    }
1547
1548
                    $extend_link = '';
1549
                    if ($inter_num > 1) {
1550
                        $extend_link = Display::url(
1551
                            Display::return_icon('invisible.png', get_lang('ExtendAttemptView')),
1552
                            api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
1553
                        );
1554
                    }
1555
                    $lesson_status = $row['mystatus'];
1556
                    $score = $row['myscore'];
1557
                    $subtotal_time = $row['mytime'];
1558
1559
                    // This is necessary for fix the total time
1560
                    $h5pImportEnable = api_get_plugin_setting('h5pimport', 'tool_enable');
1561
                    if ($row['item_type'] === 'h5p' && 'true' === $h5pImportEnable) {
1562
                        $subtotal_time = H5pImportPlugin::fixTotalTimeInLpItemView(
1563
                            $row['iv_id'],
1564
                            $user_id
1565
                        );
1566
                    }
1567
                    while ($tmp_row = Database::fetch_array($result)) {
1568
                        $subtotal_time += $tmp_row['mytime'];
1569
                    }
1570
1571
                    $title = $row['mytitle'];
1572
                    // Selecting the exe_id from stats attempts tables in order to look the max score value.
1573
                    $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
1574
                            WHERE
1575
                                exe_exo_id="'.$row['path'].'" AND
1576
                                exe_user_id="'.$user_id.'" AND
1577
                                orig_lp_id = "'.$lp_id.'" AND
1578
                                orig_lp_item_id = "'.$row['myid'].'" AND
1579
                                c_id = '.$course_id.' AND
1580
                                status <> "incomplete" AND
1581
                                session_id = '.$session_id.'
1582
                             ORDER BY exe_date DESC
1583
                             LIMIT 1';
1584
1585
                    $resultLastAttempt = Database::query($sql);
1586
                    $num = Database::num_rows($resultLastAttempt);
1587
                    $id_last_attempt = null;
1588
                    if ($num > 0) {
1589
                        while ($rowLA = Database::fetch_array($resultLastAttempt)) {
1590
                            $id_last_attempt = $rowLA['exe_id'];
1591
                        }
1592
                    }
1593
1594
                    switch ($row['item_type']) {
1595
                        case 'sco':
1596
                            if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
1597
                                $maxscore = $row['myviewmaxscore'];
1598
                            } elseif ($row['myviewmaxscore'] === '') {
1599
                                $maxscore = 0;
1600
                            } else {
1601
                                $maxscore = $row['mymaxscore'];
1602
                            }
1603
                            break;
1604
                        case 'quiz':
1605
                            // Get score and total time from last attempt of a exercise en lp.
1606
                            $sql = "SELECT iid, score
1607
                                FROM $TBL_LP_ITEM_VIEW
1608
                                WHERE
1609
                                    c_id = ".(int) $course_id." AND
1610
                                    lp_item_id = ".(int) $my_id." AND
1611
                                    lp_view_id = ".(int) $my_lp_view_id."
1612
                                ORDER BY view_count DESC
1613
                                LIMIT 1";
1614
1615
                            $res_score = Database::query($sql);
1616
                            $row_score = Database::fetch_array($res_score);
1617
1618
                            $sql = "SELECT SUM(total_time) AS total_time
1619
                                    FROM $TBL_LP_ITEM_VIEW
1620
                                    WHERE
1621
                                        c_id = ".(int) $course_id." AND
1622
                                        lp_item_id = ".(int) $my_id." AND
1623
                                        lp_view_id = ".(int) $my_lp_view_id;
1624
                            $res_time = Database::query($sql);
1625
                            $row_time = Database::fetch_array($res_time);
1626
1627
                            $score = 0;
1628
                            $subtotal_time = 0;
1629
                            if (Database::num_rows($res_score) > 0
1630
                                && Database::num_rows($res_time) > 0
1631
                            ) {
1632
                                $score = (float) $row_score['score'];
1633
                                $subtotal_time = (int) $row_time['total_time'];
1634
                            }
1635
                            // Selecting the max score from an attempt.
1636
                            $sql = "SELECT SUM(t.ponderation) as maxscore
1637
                                    FROM (
1638
                                        SELECT DISTINCT
1639
                                            question_id, marks, ponderation
1640
                                        FROM $tbl_stats_attempts as at
1641
                                        INNER JOIN $tbl_quiz_questions as q
1642
                                        ON q.iid = at.question_id
1643
                                        WHERE exe_id ='$id_last_attempt'
1644
                                    ) as t";
1645
1646
                            $result = Database::query($sql);
1647
                            $row_max_score = Database::fetch_array($result);
1648
                            $maxscore = $row_max_score['maxscore'];
1649
1650
                            // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
1651
                            $sql = 'SELECT SUM(exe_duration) exe_duration
1652
                                    FROM '.$tbl_stats_exercices.'
1653
                                    WHERE
1654
                                        exe_exo_id="'.$row['path'].'" AND
1655
                                        exe_user_id="'.$user_id.'" AND
1656
                                        orig_lp_id = "'.$lp_id.'" AND
1657
                                        orig_lp_item_id = "'.$row['myid'].'" AND
1658
                                        c_id = '.$course_id.' AND
1659
                                        status <> "incomplete" AND
1660
                                        session_id = '.$session_id.'
1661
                                     ORDER BY exe_date DESC ';
1662
                            $sumScoreResult = Database::query($sql);
1663
                            $durationRow = Database::fetch_array($sumScoreResult, 'ASSOC');
1664
                            if (!empty($durationRow['exe_duration'])) {
1665
                                $exeDuration = $durationRow['exe_duration'];
1666
                                if ($exeDuration != $subtotal_time
1667
                                    && !empty($row_score['iid'])
1668
                                    && !empty($exeDuration)
1669
                                ) {
1670
                                    $subtotal_time = $exeDuration;
1671
                                    // Update c_lp_item_view.total_time
1672
                                    $sqlUpdate = "UPDATE $TBL_LP_ITEM_VIEW SET total_time = '$exeDuration'
1673
                                                  WHERE iid = ".$row_score['iid'];
1674
                                    Database::query($sqlUpdate);
1675
                                }
1676
                            }
1677
                            break;
1678
                        case 'h5p':
1679
                            if ($row['item_type'] === 'h5p' && 'true' === $h5pImportEnable) {
1680
                                $sql = "SELECT iid, score
1681
                                        FROM $TBL_LP_ITEM_VIEW
1682
                                        WHERE
1683
                                            c_id = ".(int) $course_id." AND
1684
                                            lp_item_id = ".(int) $my_id." AND
1685
                                            lp_view_id = ".(int) $my_lp_view_id."
1686
                                        ORDER BY view_count DESC
1687
                                        LIMIT 1";
1688
                                $res_score = Database::query($sql);
1689
                                $row_score = Database::fetch_array($res_score);
1690
1691
                                H5pImportPlugin::fixTotalTimeInLpItemView($row_score['iid'], $user_id);
1692
                            }
1693
                            break;
1694
                        default:
1695
                            $maxscore = $row['mymaxscore'];
1696
                            break;
1697
                    }
1698
1699
                    $time_for_total = $subtotal_time;
1700
                    $time = learnpathItem::getScormTimeFromParameter('js', $subtotal_time);
1701
                    if (empty($title)) {
1702
                        $title = learnpath::rl_get_resource_name(
1703
                            $courseInfo['code'],
1704
                            $lp_id,
1705
                            $row['myid']
1706
                        );
1707
                    }
1708
1709
                    $action = null;
1710
                    if ($type === 'classic') {
1711
                        $action = '<td></td>';
1712
                    }
1713
1714
                    if (in_array($row['item_type'], $chapterTypes)) {
1715
                        $title = Security::remove_XSS($title);
1716
                        $output .= '<tr class="'.$oddclass.'">
1717
                                <td>'.$extend_link.'</td>
1718
                                <td colspan="10">
1719
                                <h4>'.$title.'</h4>
1720
                                </td>
1721
                                '.$action.'
1722
                            </tr>';
1723
                    } else {
1724
                        $correct_test_link = '-';
1725
                        $showRowspan = false;
1726
                        $my_url_suffix = '&course='.$courseCode.'&student_id='.$user_id.'&lp_id='.intval($row['mylpid']).'&origin='.$origin;
1727
                        if ($row['item_type'] === 'quiz') {
1728
                            $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
1729
                                     WHERE
1730
                                        exe_exo_id="'.$row['path'].'" AND
1731
                                        exe_user_id="'.$user_id.'" AND
1732
                                        orig_lp_id = "'.$lp_id.'" AND
1733
                                        orig_lp_item_id = "'.$row['myid'].'" AND
1734
                                        c_id = '.$course_id.' AND
1735
                                        status <> "incomplete" AND
1736
                                        session_id = '.$session_id.'
1737
                                     ORDER BY exe_date DESC ';
1738
1739
                            $resultLastAttempt = Database::query($sql);
1740
                            $num = Database::num_rows($resultLastAttempt);
1741
                            $showRowspan = false;
1742
                            if ($num > 0) {
1743
                                $linkId = 'link_'.$my_id;
1744
                                if ($extendedAttempt == 1
1745
                                    && $lp_id == $my_lp_id
1746
                                    && $lp_item_id == $my_id
1747
                                ) {
1748
                                    $showRowspan = true;
1749
                                    $correct_test_link = Display::url(
1750
                                        Display::return_icon(
1751
                                            'view_less_stats.gif',
1752
                                            get_lang('HideAllAttempts')
1753
                                        ),
1754
                                        api_get_self().'?action=stats'.$my_url_suffix.'&session_id='.$session_id.'&lp_item_id='.$my_id.'#'.$linkId,
1755
                                        ['id' => $linkId]
1756
                                    );
1757
                                } else {
1758
                                    $correct_test_link = Display::url(
1759
                                        Display::return_icon(
1760
                                            'view_more_stats.gif',
1761
                                            get_lang(
1762
                                                'ShowAllAttemptsByExercise'
1763
                                            )
1764
                                        ),
1765
                                        api_get_self().'?action=stats&extend_attempt=1'.$my_url_suffix.'&session_id='.$session_id.'&lp_item_id='.$my_id.'#'.$linkId,
1766
                                        ['id' => $linkId]
1767
                                    );
1768
                                }
1769
                            }
1770
                        }
1771
                        if ($row['item_type'] === 'h5p' && 'true' === $h5pImportEnable) {
1772
                            $em = Database::getManager();
1773
                            $cLpItemViewRepo = $em->getRepository('ChamiloPluginBundle:H5pImport\H5pImportResults');
1774
                            $count = $cLpItemViewRepo->count(['user' => $user_id, 'cLpItemView' => $row['iv_id']]);
1775
1776
                            $showRowspan = false;
1777
                            if ($count > 0) {
1778
                                $linkId = 'link_'.$my_id;
1779
                                if ($extendedAttempt == 1
1780
                                    && $lp_id == $my_lp_id
1781
                                    && $lp_item_id == $my_id
1782
                                ) {
1783
                                    $showRowspan = true;
1784
                                    $correct_test_link = Display::url(
1785
                                        Display::return_icon(
1786
                                            'view_less_stats.gif',
1787
                                            get_lang('HideAllAttempts')
1788
                                        ),
1789
                                        api_get_self().'?action=stats'.$my_url_suffix.'&session_id='.$session_id.'&lp_item_id='.$my_id.'#'.$linkId,
1790
                                        ['id' => $linkId]
1791
                                    );
1792
                                } else {
1793
                                    $correct_test_link = Display::url(
1794
                                        Display::return_icon(
1795
                                            'view_more_stats.gif',
1796
                                            get_lang(
1797
                                                'ShowAllAttemptsByExercise'
1798
                                            )
1799
                                        ),
1800
                                        api_get_self().'?action=stats&extend_attempt=1'.$my_url_suffix.'&session_id='.$session_id.'&lp_item_id='.$my_id.'#'.$linkId,
1801
                                        ['id' => $linkId]
1802
                                    );
1803
                                }
1804
                            }
1805
                        }
1806
                        $title = Security::remove_XSS($title);
1807
                        $action = null;
1808
                        if ($type === 'classic') {
1809
                            $action = '<td '.($showRowspan ? 'rowspan="2"' : '').'>'.$correct_test_link.'</td>';
1810
                        }
1811
1812
                        if ($lp_id == $my_lp_id && false) {
1813
                            $output .= '<tr class ='.$oddclass.'>
1814
                                    <td>'.$extend_link.'</td>
1815
                                    <td colspan="4">'.$title.'</td>
1816
                                    <td colspan="2">&nbsp;</td>
1817
                                    <td colspan="2">&nbsp;</td>
1818
                                    <td colspan="2">&nbsp;</td>
1819
                                    '.$action.'
1820
                                </tr>';
1821
                            $output .= '</tr>';
1822
                        } else {
1823
                            if ($lp_id == $my_lp_id && $lp_item_id == $my_id) {
1824
                                $output .= "<tr class='$oddclass'>";
1825
                            } else {
1826
                                $output .= "<tr class='$oddclass'>";
1827
                            }
1828
1829
                            $scoreItem = null;
1830
                            if ($row['item_type'] === 'quiz') {
1831
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1832
                                    $scoreItem .= Display::return_icon(
1833
                                        'invisible.gif',
1834
                                        get_lang('ResultsHiddenByExerciseSetting')
1835
                                    );
1836
                                } else {
1837
                                    $scoreItem .= ExerciseLib::show_score($score, $maxscore, false);
1838
                                }
1839
                            } else {
1840
                                $scoreItem .= $score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.$maxscore);
1841
                            }
1842
1843
                            $timeRow = '<td class="lp_time" colspan="2">'.$time.'</td>';
1844
                            if ($hideTime) {
1845
                                $timeRow = '';
1846
                            }
1847
1848
                            $output .= '
1849
                                <td>'.$extend_link.'</td>
1850
                                <td colspan="4">'.$title.'</td>
1851
                                <td colspan="2">'.learnpathitem::humanize_status($lesson_status).'</td>
1852
                                <td colspan="2">'.$scoreItem.'</td>
1853
                                '.$timeRow.'
1854
                                '.$action.'
1855
                             ';
1856
                            $output .= '</tr>';
1857
                        }
1858
1859
                        if (!empty($export_csv)) {
1860
                            $temp = [];
1861
                            $temp[] = api_html_entity_decode($title, ENT_QUOTES);
1862
                            $temp[] = api_html_entity_decode($lesson_status, ENT_QUOTES);
1863
                            if ($row['item_type'] === 'quiz') {
1864
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1865
                                    $temp[] = '/';
1866
                                } else {
1867
                                    $temp[] = ($score == 0 ? '0/'.$maxscore : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
1868
                                }
1869
                            } else {
1870
                                $temp[] = ($score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
1871
                            }
1872
1873
                            if ($hideTime === false) {
1874
                                $temp[] = $time;
1875
                            }
1876
                            $csv_content[] = $temp;
1877
                        }
1878
                    }
1879
1880
                    $counter++;
1881
                    $action = null;
1882
                    if ($type === 'classic') {
1883
                        $action = '<td></td>';
1884
                    }
1885
1886
                    if ($extend_this_attempt || $extend_all) {
1887
                        $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $course_id);
1888
                        foreach ($list1 as $id => $interaction) {
1889
                            $oddclass = 'row_even';
1890
                            if (($counter % 2) == 0) {
1891
                                $oddclass = 'row_odd';
1892
                            }
1893
                            $timeRow = '<td class="lp_time">'.$interaction['time'].'</td>';
1894
                            if ($hideTime) {
1895
                                $timeRow = '';
1896
                            }
1897
1898
                            $output .= '<tr class="'.$oddclass.'">
1899
                                    <td></td>
1900
                                    <td></td>
1901
                                    <td></td>
1902
                                    <td>'.$interaction['order_id'].'</td>
1903
                                    <td>'.$interaction['id'].'</td>
1904
                                    <td colspan="2">'.$interaction['type'].'</td>
1905
                                    <td>'.urldecode($interaction['student_response']).'</td>
1906
                                    <td>'.$interaction['result'].'</td>
1907
                                    <td>'.$interaction['latency'].'</td>
1908
                                    '.$timeRow.'
1909
                                    '.$action.'
1910
                               </tr>';
1911
                            $counter++;
1912
                        }
1913
1914
                        $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $course_id);
1915
                        foreach ($list2 as $id => $interaction) {
1916
                            $oddclass = 'row_even';
1917
                            if (($counter % 2) == 0) {
1918
                                $oddclass = 'row_odd';
1919
                            }
1920
                            $output .= '<tr class="'.$oddclass.'">
1921
                                    <td></td>
1922
                                    <td></td>
1923
                                    <td></td>
1924
                                    <td>'.$interaction['order_id'].'</td>
1925
                                    <td colspan="2">'.$interaction['objective_id'].'</td>
1926
                                    <td colspan="2">'.$interaction['status'].'</td>
1927
                                    <td>'.$interaction['score_raw'].'</td>
1928
                                    <td>'.$interaction['score_max'].'</td>
1929
                                    <td>'.$interaction['score_min'].'</td>
1930
                                    '.$action.'
1931
                               </tr>';
1932
                            $counter++;
1933
                        }
1934
                    }
1935
1936
                    // Attempts listing by exercise.
1937
                    if ($lp_id == $my_lp_id && $lp_item_id == $my_id && $extendedAttempt) {
1938
                        // Get attempts of an exercise.
1939
                        if (!empty($lp_id)
1940
                            && !empty($lp_item_id)
1941
                        ) {
1942
                            $sql = "SELECT path FROM $TBL_LP_ITEM
1943
                                    WHERE
1944
                                        c_id = $course_id AND
1945
                                        iid = '$lp_item_id' AND
1946
                                        lp_id = '$lp_id'";
1947
                            $res_path = Database::query($sql);
1948
                            $row_path = Database::fetch_array($res_path);
1949
1950
                            if (Database::num_rows($res_path) > 0) {
1951
                                if ('quiz' === $row['item_type']) {
1952
                                    $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
1953
                                    WHERE
1954
                                        exe_exo_id = '.(int) $row_path['path'].' AND
1955
                                        status <> "incomplete" AND
1956
                                        exe_user_id='.$user_id.' AND
1957
                                        orig_lp_id = '.(int) $lp_id.' AND
1958
                                        orig_lp_item_id = '.(int) $lp_item_id.' AND
1959
                                        c_id = '.$course_id.' AND
1960
                                        session_id = '.$session_id.'
1961
                                    ORDER BY exe_date';
1962
                                    $res_attempts = Database::query($sql);
1963
                                    $num_attempts = Database::num_rows($res_attempts);
1964
                                    if ($num_attempts > 0) {
1965
                                        $n = 1;
1966
                                        while ($row_attempts = Database::fetch_array($res_attempts)) {
1967
                                            $my_score = $row_attempts['exe_result'];
1968
                                            $my_maxscore = $row_attempts['exe_weighting'];
1969
                                            $my_exe_id = $row_attempts['exe_id'];
1970
                                            $mktime_start_date = api_strtotime($row_attempts['start_date'], 'UTC');
1971
                                            $mktime_exe_date = api_strtotime($row_attempts['exe_date'], 'UTC');
1972
                                            $time_attemp = ' - ';
1973
                                            if ($mktime_start_date && $mktime_exe_date) {
1974
                                                $time_attemp = api_format_time($row_attempts['exe_duration'], 'js');
1975
                                            }
1976
                                            if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1977
                                                $view_score = Display::return_icon(
1978
                                                    'invisible.png',
1979
                                                    get_lang(
1980
                                                        'ResultsHiddenByExerciseSetting'
1981
                                                    )
1982
                                                );
1983
                                            } else {
1984
                                                // Show only float when need it
1985
                                                if ($my_score == 0) {
1986
                                                    $view_score = ExerciseLib::show_score(
1987
                                                        0,
1988
                                                        $my_maxscore,
1989
                                                        false
1990
                                                    );
1991
                                                } else {
1992
                                                    if ($my_maxscore == 0) {
1993
                                                        $view_score = $my_score;
1994
                                                    } else {
1995
                                                        $view_score = ExerciseLib::show_score(
1996
                                                            $my_score,
1997
                                                            $my_maxscore,
1998
                                                            false
1999
                                                        );
2000
                                                    }
2001
                                                }
2002
                                            }
2003
                                            $my_lesson_status = $row_attempts['status'];
2004
                                            if ($my_lesson_status == '') {
2005
                                                $my_lesson_status = learnpathitem::humanize_status('completed');
2006
                                            } elseif ($my_lesson_status == 'incomplete') {
2007
                                                $my_lesson_status = learnpathitem::humanize_status('incomplete');
2008
                                            }
2009
                                            $timeRow = '<td class="lp_time" colspan="2">'.$time_attemp.'</td>';
2010
                                            if ($hideTime) {
2011
                                                $timeRow = '';
2012
                                            }
2013
2014
                                            $output .= '<tr class="'.$oddclass.'" >
2015
                                    <td></td>
2016
                                    <td>'.$extend_attempt_link.'</td>
2017
                                    <td colspan="3">'.get_lang('Attempt').' '.$n.'</td>
2018
                                    <td colspan="2">'.$my_lesson_status.'</td>
2019
                                    <td colspan="2">'.$view_score.'</td>
2020
                                    '.$timeRow;
2021
2022
                                            if ($action == 'classic') {
2023
                                                if ($origin != 'tracking') {
2024
                                                    if (!$is_allowed_to_edit && $result_disabled_ext_all) {
2025
                                                        $output .= '<td>
2026
                                                        <img src="'.Display::returnIconPath('quiz_na.gif').'" alt="'.get_lang('ShowAttempt').'" title="'.get_lang('ShowAttempt').'">
2027
                                                        </td>';
2028
                                                    } else {
2029
                                                        $output .= '<td>
2030
                                                        <a href="../exercise/exercise_show.php?origin='.$origin.'&id='.$my_exe_id.'&cidReq='.$courseCode.'" target="_parent">
2031
                                                        <img src="'.Display::returnIconPath('quiz.png').'" alt="'.get_lang('ShowAttempt').'" title="'.get_lang('ShowAttempt').'">
2032
                                                        </a></td>';
2033
                                                    }
2034
                                                } else {
2035
                                                    if (!$is_allowed_to_edit && $result_disabled_ext_all) {
2036
                                                        $output .= '<td>
2037
                                                            <img src="'.Display::returnIconPath('quiz_na.gif').'" alt="'.get_lang('ShowAndQualifyAttempt').'" title="'.get_lang('ShowAndQualifyAttempt').'"></td>';
2038
                                                    } else {
2039
                                                        $output .= '<td>
2040
                                                                <a href="../exercise/exercise_show.php?cidReq='.$courseCode.'&origin=correct_exercise_in_lp&id='.$my_exe_id.'" target="_parent">
2041
                                                                <img src="'.Display::returnIconPath('quiz.gif').'" alt="'.get_lang('ShowAndQualifyAttempt').'" title="'.get_lang('ShowAndQualifyAttempt').'"></a></td>';
2042
                                                    }
2043
                                                }
2044
                                            }
2045
                                            $output .= '</tr>';
2046
                                            $n++;
2047
                                        }
2048
                                    }
2049
                                } elseif (
2050
                                    'h5p' === $row['item_type']
2051
                                    && 'true' === $h5pImportEnable
2052
                                ) {
2053
                                    $em = Database::getManager();
2054
                                    $h5pImportResultsRepo = $em
2055
                                        ->getRepository('ChamiloPluginBundle:H5pImport\H5pImportResults');
2056
                                    $h5pResults = $h5pImportResultsRepo
2057
                                        ->findBy(
2058
                                            [
2059
                                                'user' => $user_id, 'cLpItemView' => $row['iv_id'],
2060
                                            ]
2061
                                        );
2062
2063
                                    if (count($h5pResults) > 0) {
2064
                                        foreach ($h5pResults as $result) {
2065
                                            $timeAttempt = ' - ';
2066
                                            if ($result->getTotalTime()) {
2067
                                                $timeAttempt = gmdate('H:i:s', $result->getTotalTime());
2068
                                            }
2069
                                            if (!$is_allowed_to_edit && $result_disabled_ext_all) {
2070
                                                $view_score = Display::return_icon(
2071
                                                    'invisible.png',
2072
                                                    get_lang(
2073
                                                        'ResultsHiddenByExerciseSetting'
2074
                                                    )
2075
                                                );
2076
                                            } else {
2077
                                                // Show only float when need it
2078
                                                if ($result->getScore() == 0) {
2079
                                                    $view_score = ExerciseLib::show_score(
2080
                                                        0,
2081
                                                        $result->getMaxScore(),
2082
                                                        false
2083
                                                    );
2084
                                                } else {
2085
                                                    if ($result->getMaxScore() == 0) {
2086
                                                        $view_score = $result->getScore();
2087
                                                    } else {
2088
                                                        $view_score = ExerciseLib::show_score(
2089
                                                            $result->getScore(),
2090
                                                            $result->getMaxScore(),
2091
                                                            false
2092
                                                        );
2093
                                                    }
2094
                                                }
2095
                                            }
2096
                                            $my_lesson_status = learnpathitem::humanize_status($row['status']);
2097
                                            $timeRow = '<td class="lp_time" colspan="2">'.$timeAttempt.'</td>';
2098
                                            if ($hideTime) {
2099
                                                $timeRow = '';
2100
                                            }
2101
2102
                                            $output .= '<tr class="'.$oddclass.'" >
2103
                                                <td></td>
2104
                                                <td>'.$extend_attempt_link.'</td>
2105
                                                <td colspan="3">'.get_lang('Attempt').' '.$n.'</td>
2106
                                                <td colspan="2">'.$my_lesson_status.'</td>
2107
                                                <td colspan="2">'.$view_score.'</td>'.$timeRow;
2108
2109
                                            if ($action == 'classic') {
2110
                                                if ($origin != 'tracking') {
2111
                                                    if (!$is_allowed_to_edit && $result_disabled_ext_all) {
2112
                                                        $output .= '<td>
2113
                                                        <img src="'.Display::returnIconPath('quiz_na.gif').'" alt="'.get_lang('ShowAttempt').'" title="'.get_lang('ShowAttempt').'">
2114
                                                        </td>';
2115
                                                    } else {
2116
                                                        $output .= '<td>
2117
                                                        <a href="../exercise/exercise_show.php?origin='.$origin.'&id='.$result->getIid().'&cidReq='.$courseCode.'" target="_parent">
2118
                                                        <img src="'.Display::returnIconPath('quiz.png').'" alt="'.get_lang('ShowAttempt').'" title="'.get_lang('ShowAttempt').'">
2119
                                                        </a></td>';
2120
                                                    }
2121
                                                } else {
2122
                                                    if (!$is_allowed_to_edit && $result_disabled_ext_all) {
2123
                                                        $output .= '<td>
2124
                                                            <img src="'.Display::returnIconPath('quiz_na.gif').'" alt="'.get_lang('ShowAndQualifyAttempt').'" title="'.get_lang('ShowAndQualifyAttempt').'"></td>';
2125
                                                    } else {
2126
                                                        $output .= '<td>
2127
                                                                <a href="../exercise/exercise_show.php?cidReq='.$courseCode.'&origin=correct_exercise_in_lp&id='.$result->getIid().'" target="_parent">
2128
                                                                <img src="'.Display::returnIconPath('quiz.gif').'" alt="'.get_lang('ShowAndQualifyAttempt').'" title="'.get_lang('ShowAndQualifyAttempt').'"></a></td>';
2129
                                                    }
2130
                                                }
2131
                                            }
2132
                                            $output .= '</tr>';
2133
                                            $n++;
2134
                                        }
2135
                                    }
2136
                                } else {
2137
                                    $output .= '<tr><td colspan="12">&nbsp;</td></tr>';
2138
                                }
2139
                            }
2140
                        }
2141
                    }
2142
                }
2143
2144
                $total_time += $time_for_total;
2145
                // QUIZZ IN LP
2146
                $a_my_id = [];
2147
                if (!empty($my_lp_id)) {
2148
                    $a_my_id[] = $my_lp_id;
2149
                }
2150
            }
2151
        }
2152
2153
        // NOT Extend all "left green cross"
2154
        if (!empty($a_my_id)) {
2155
            if ($extendedAttempt) {
2156
                // "Right green cross" extended
2157
                $total_score = self::get_avg_student_score(
2158
                    $user_id,
2159
                    $course_id,
2160
                    $a_my_id,
2161
                    $session_id,
2162
                    false,
2163
                    false
2164
                );
2165
            } else {
2166
                // "Left green cross" extended
2167
                $total_score = self::get_avg_student_score(
2168
                    $user_id,
2169
                    $course_id,
2170
                    $a_my_id,
2171
                    $session_id,
2172
                    false,
2173
                    true
2174
                );
2175
            }
2176
        } else {
2177
            // Extend all "left green cross"
2178
            $total_score = self::get_avg_student_score(
2179
                $user_id,
2180
                $course_id,
2181
                [$lp_id],
2182
                $session_id,
2183
                false,
2184
                false
2185
            );
2186
        }
2187
2188
        $total_time = learnpathItem::getScormTimeFromParameter('js', $total_time);
2189
        $total_time = str_replace('NaN', '00'.$h.'00\'00"', $total_time);
2190
2191
        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
2192
            $final_score = Display::return_icon('invisible.png', get_lang('ResultsHiddenByExerciseSetting'));
2193
            $finalScoreToCsv = get_lang('ResultsHiddenByExerciseSetting');
2194
        } else {
2195
            if (is_numeric($total_score)) {
2196
                $final_score = $total_score.'%';
2197
            } else {
2198
                $final_score = $total_score;
2199
            }
2200
            $finalScoreToCsv = $final_score;
2201
        }
2202
        $progress = learnpath::getProgress($lp_id, $user_id, $course_id, $session_id);
2203
2204
        $oddclass = 'row_even';
2205
        if (($counter % 2) == 0) {
2206
            $oddclass = 'row_odd';
2207
        }
2208
2209
        $action = null;
2210
        if ('classic' === $type) {
2211
            $action = '<td></td>';
2212
        }
2213
2214
        $timeTotal = '<td class="lp_time" colspan="2">'.$total_time.'</div>';
2215
        if ($hideTime) {
2216
            $timeTotal = '';
2217
        }
2218
2219
        $output .= '<tr class="'.$oddclass.'">
2220
                <td></td>
2221
                <td colspan="4">
2222
                    <i>'.get_lang('AccomplishedStepsTotal').'</i>
2223
                </td>
2224
                <td colspan="2">'.$progress.'%</td>
2225
                <td colspan="2">'.$final_score.'</td>
2226
                '.$timeTotal.'
2227
                '.$action.'
2228
           </tr>';
2229
2230
        $output .= '
2231
                    </tbody>
2232
                </table>
2233
            </div>
2234
        ';
2235
2236
        if (!empty($export_csv)) {
2237
            $temp = [
2238
                '',
2239
                '',
2240
                '',
2241
                '',
2242
            ];
2243
            $csv_content[] = $temp;
2244
            $temp = [
2245
                get_lang('AccomplishedStepsTotal'),
2246
                '',
2247
                $finalScoreToCsv,
2248
            ];
2249
2250
            if ($hideTime === false) {
2251
                $temp[] = $total_time;
2252
            }
2253
2254
            $csv_content[] = $temp;
2255
            ob_end_clean();
2256
            Export::arrayToCsv($csv_content, 'reporting_learning_path_details');
2257
            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...
2258
        }
2259
2260
        return $output;
2261
    }
2262
2263
    /**
2264
     * @param int  $userId
2265
     * @param bool $getCount
2266
     *
2267
     * @return array
2268
     */
2269
    public static function getStats($userId, $getCount = false)
2270
    {
2271
        $courses = [];
2272
        $students = [];
2273
        $assignedCourses = [];
2274
        $drhCount = 0;
2275
        $teachersCount = 0;
2276
        $studentBossCount = 0;
2277
        $courseCount = 0;
2278
        $assignedCourseCount = 0;
2279
        $studentCount = 0;
2280
        $checkSessionVisibility = api_get_configuration_value('show_users_in_active_sessions_in_tracking');
2281
        $allowDhrAccessToAllStudents = api_get_configuration_value('drh_allow_access_to_all_students');
2282
2283
        if (api_is_drh() && api_drh_can_access_all_session_content()) {
2284
            $studentBossesList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
2285
                'drh_all',
2286
                $userId,
2287
                $getCount,
2288
                null,
2289
                null,
2290
                null,
2291
                null,
2292
                null,
2293
                null,
2294
                null,
2295
                [],
2296
                [],
2297
                STUDENT_BOSS
2298
            );
2299
2300
            if ($getCount) {
2301
                $studentBossCount = $studentBossesList;
2302
            } else {
2303
                $studentBosses = [];
2304
                if (is_array($studentBossesList)) {
2305
                    foreach ($studentBossesList as $studentBossData) {
2306
                        $studentBosses[] = $studentBossData['user_id'];
2307
                    }
2308
                }
2309
            }
2310
2311
            $teacherList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
2312
                'drh_all',
2313
                $userId,
2314
                $getCount,
2315
                null,
2316
                null,
2317
                null,
2318
                null,
2319
                null,
2320
                null,
2321
                null,
2322
                [],
2323
                [],
2324
                COURSEMANAGER
2325
            );
2326
2327
            if ($getCount) {
2328
                $teachersCount = $teacherList;
2329
            } else {
2330
                $teachers = [];
2331
                foreach ($teacherList as $teacherData) {
2332
                    $teachers[] = $teacherData['user_id'];
2333
                }
2334
            }
2335
2336
            $humanResources = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
2337
                'drh_all',
2338
                $userId,
2339
                $getCount,
2340
                null,
2341
                null,
2342
                null,
2343
                null,
2344
                null,
2345
                null,
2346
                null,
2347
                [],
2348
                [],
2349
                DRH
2350
            );
2351
2352
            if ($getCount) {
2353
                $drhCount = $humanResources;
2354
            } else {
2355
                $humanResourcesList = [];
2356
                if (is_array($humanResources)) {
2357
                    foreach ($humanResources as $item) {
2358
                        $humanResourcesList[] = $item['user_id'];
2359
                    }
2360
                }
2361
            }
2362
2363
            $platformCourses = SessionManager::getAllCoursesFollowedByUser(
2364
                $userId,
2365
                null,
2366
                null,
2367
                null,
2368
                null,
2369
                null,
2370
                $getCount
2371
            );
2372
2373
            if ($getCount) {
2374
                $courseCount = $platformCourses;
2375
            } else {
2376
                foreach ($platformCourses as $course) {
2377
                    $courses[$course['code']] = $course['code'];
2378
                }
2379
            }
2380
2381
            $sessions = SessionManager::get_sessions_followed_by_drh(
2382
                $userId,
2383
                null,
2384
                null,
2385
                false
2386
            );
2387
        } else {
2388
            if (api_is_drh() && $allowDhrAccessToAllStudents) {
2389
                $studentList = UserManager::get_user_list(
2390
                    ['status' => STUDENT],
2391
                    [],
2392
                    false,
2393
                    false,
2394
                    null,
2395
                    null,
2396
                    null,
2397
                    false
2398
                );
2399
            } else {
2400
                $studentList = UserManager::getUsersFollowedByUser(
2401
                    $userId,
2402
                    STUDENT,
2403
                    false,
2404
                    false,
2405
                    false,
2406
                    null,
2407
                    null,
2408
                    null,
2409
                    null,
2410
                    null,
2411
                    null,
2412
                    COURSEMANAGER,
2413
                    null,
2414
                    $checkSessionVisibility
2415
                );
2416
            }
2417
2418
            $students = [];
2419
            if (is_array($studentList)) {
2420
                foreach ($studentList as $studentData) {
2421
                    $students[] = $studentData['user_id'];
2422
                }
2423
            }
2424
            if ($getCount) {
2425
                $studentCount = count($students);
2426
            }
2427
2428
            $studentBossesList = UserManager::getUsersFollowedByUser(
2429
                $userId,
2430
                STUDENT_BOSS,
2431
                false,
2432
                false,
2433
                $getCount,
2434
                null,
2435
                null,
2436
                null,
2437
                null,
2438
                null,
2439
                null,
2440
                COURSEMANAGER,
2441
                null,
2442
                $checkSessionVisibility
2443
            );
2444
2445
            if ($getCount) {
2446
                $studentBossCount = $studentBossesList;
2447
            } else {
2448
                $studentBosses = [];
2449
                if (is_array($studentBossesList)) {
2450
                    foreach ($studentBossesList as $studentBossData) {
2451
                        $studentBosses[] = $studentBossData['user_id'];
2452
                    }
2453
                }
2454
            }
2455
2456
            $teacherList = UserManager::getUsersFollowedByUser(
2457
                $userId,
2458
                COURSEMANAGER,
2459
                false,
2460
                false,
2461
                $getCount,
2462
                null,
2463
                null,
2464
                null,
2465
                null,
2466
                null,
2467
                null,
2468
                COURSEMANAGER,
2469
                null,
2470
                $checkSessionVisibility
2471
            );
2472
2473
            if ($getCount) {
2474
                $teachersCount = $teacherList;
2475
            } else {
2476
                $teachers = [];
2477
                foreach ($teacherList as $teacherData) {
2478
                    $teachers[] = $teacherData['user_id'];
2479
                }
2480
            }
2481
2482
            $humanResources = UserManager::getUsersFollowedByUser(
2483
                $userId,
2484
                DRH,
2485
                false,
2486
                false,
2487
                $getCount,
2488
                null,
2489
                null,
2490
                null,
2491
                null,
2492
                null,
2493
                null,
2494
                COURSEMANAGER,
2495
                null,
2496
                $checkSessionVisibility
2497
            );
2498
2499
            if ($getCount) {
2500
                $drhCount = $humanResources;
2501
            } else {
2502
                $humanResourcesList = [];
2503
                foreach ($humanResources as $item) {
2504
                    $humanResourcesList[] = $item['user_id'];
2505
                }
2506
            }
2507
2508
            $platformCourses = CourseManager::getCoursesFollowedByUser(
2509
                $userId,
2510
                COURSEMANAGER,
2511
                null,
2512
                null,
2513
                null,
2514
                null,
2515
                $getCount,
2516
                null,
2517
                null,
2518
                true
2519
            );
2520
2521
            if ($getCount) {
2522
                $assignedCourseCount = $platformCourses;
2523
            } else {
2524
                foreach ($platformCourses as $course) {
2525
                    $assignedCourses[$course['code']] = $course['code'];
2526
                }
2527
            }
2528
2529
            $platformCourses = CourseManager::getCoursesFollowedByUser(
2530
                $userId,
2531
                COURSEMANAGER,
2532
                null,
2533
                null,
2534
                null,
2535
                null,
2536
                $getCount
2537
            );
2538
2539
            if ($getCount) {
2540
                $courseCount = $platformCourses;
2541
            } else {
2542
                foreach ($platformCourses as $course) {
2543
                    $courses[$course['code']] = $course['code'];
2544
                }
2545
            }
2546
2547
            $sessions = SessionManager::getSessionsFollowedByUser(
2548
                $userId,
2549
                COURSEMANAGER,
2550
                null,
2551
                null,
2552
                false
2553
            );
2554
        }
2555
2556
        if ($getCount) {
2557
            return [
2558
                'drh' => $drhCount,
2559
                'teachers' => $teachersCount,
2560
                'student_count' => $studentCount,
2561
                'student_list' => $students,
2562
                'student_bosses' => $studentBossCount,
2563
                'courses' => $courseCount,
2564
                'session_count' => count($sessions),
2565
                'session_list' => $sessions,
2566
                'assigned_courses' => $assignedCourseCount,
2567
            ];
2568
        }
2569
2570
        return [
2571
            'drh' => $humanResourcesList,
2572
            'teachers' => $teachers,
2573
            'student_list' => $students,
2574
            'student_bosses' => $studentBosses,
2575
            'courses' => $courses,
2576
            'sessions' => $sessions,
2577
            'assigned_courses' => $assignedCourses,
2578
        ];
2579
    }
2580
2581
    /**
2582
     * Calculates the time spent on the platform by a user.
2583
     *
2584
     * @param int|array $userId
2585
     * @param string    $timeFilter       type of time filter: 'last_week' or 'custom'
2586
     * @param string    $start_date       start date date('Y-m-d H:i:s')
2587
     * @param string    $end_date         end date date('Y-m-d H:i:s')
2588
     * @param bool      $returnAllRecords
2589
     *
2590
     * @return int
2591
     */
2592
    public static function get_time_spent_on_the_platform(
2593
        $userId,
2594
        $timeFilter = 'last_7_days',
2595
        $start_date = null,
2596
        $end_date = null,
2597
        $returnAllRecords = false
2598
    ) {
2599
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2600
        $condition_time = '';
2601
2602
        if (is_array($userId)) {
2603
            $userList = array_map('intval', $userId);
2604
            $userCondition = " login_user_id IN ('".implode("','", $userList)."')";
2605
        } else {
2606
            $userId = (int) $userId;
2607
            $userCondition = " login_user_id = $userId ";
2608
        }
2609
2610
        $url_condition = null;
2611
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2612
        $url_table = null;
2613
        if (api_is_multiple_url_enabled()) {
2614
            $access_url_id = api_get_current_access_url_id();
2615
            $url_table = ", $tbl_url_rel_user as url_users";
2616
            $url_condition = " AND u.login_user_id = url_users.user_id AND access_url_id='$access_url_id'";
2617
        }
2618
2619
        if (empty($timeFilter)) {
2620
            $timeFilter = 'last_week';
2621
        }
2622
2623
        $today = new DateTime('now', new DateTimeZone('UTC'));
2624
2625
        switch ($timeFilter) {
2626
            case 'last_7_days':
2627
                $newDate = new DateTime('-7 day', new DateTimeZone('UTC'));
2628
                $condition_time = " AND (login_date >= '{$newDate->format('Y-m-d H:i:s')}'";
2629
                $condition_time .= " AND logout_date <= '{$today->format('Y-m-d H:i:s')}') ";
2630
                break;
2631
            case 'last_30_days':
2632
                $newDate = new DateTime('-30 days', new DateTimeZone('UTC'));
2633
                $condition_time = " AND (login_date >= '{$newDate->format('Y-m-d H:i:s')}'";
2634
                $condition_time .= "AND logout_date <= '{$today->format('Y-m-d H:i:s')}') ";
2635
                break;
2636
            case 'wide':
2637
                if (!empty($start_date) && !empty($end_date)) {
2638
                    $start_date = Database::escape_string($start_date);
2639
                    $end_date = Database::escape_string($end_date);
2640
                    $condition_time = ' AND (
2641
                        (login_date >= "'.$start_date.'" AND login_date <= "'.$end_date.'") OR
2642
                        (logout_date >= "'.$start_date.'" AND logout_date <= "'.$end_date.'") OR
2643
                        (login_date <= "'.$start_date.'" AND logout_date >= "'.$end_date.'")
2644
                    ) ';
2645
                }
2646
                break;
2647
            case 'custom':
2648
                if (!empty($start_date) && !empty($end_date)) {
2649
                    $start_date = Database::escape_string($start_date);
2650
                    $end_date = Database::escape_string($end_date);
2651
                    $condition_time = ' AND (login_date >= "'.$start_date.'" AND logout_date <= "'.$end_date.'" ) ';
2652
                }
2653
                break;
2654
        }
2655
2656
        if ($returnAllRecords) {
2657
            $sql = "SELECT login_date, logout_date, TIMESTAMPDIFF(SECOND, login_date, logout_date) diff
2658
                    FROM $tbl_track_login u $url_table
2659
                    WHERE $userCondition $condition_time $url_condition
2660
                    ORDER BY login_date";
2661
            $rs = Database::query($sql);
2662
2663
            return Database::store_result($rs, 'ASSOC');
2664
        }
2665
2666
        $sql = "SELECT SUM(TIMESTAMPDIFF(SECOND, login_date, logout_date)) diff
2667
    	        FROM $tbl_track_login u $url_table
2668
                WHERE $userCondition $condition_time $url_condition";
2669
        $rs = Database::query($sql);
2670
        $row = Database::fetch_array($rs, 'ASSOC');
2671
        $diff = $row['diff'];
2672
2673
        if ($diff >= 0) {
2674
            return $diff;
2675
        }
2676
2677
        return -1;
2678
    }
2679
2680
    /**
2681
     * @param string $startDate
2682
     * @param string $endDate
2683
     *
2684
     * @return int
2685
     */
2686
    public static function getTotalTimeSpentOnThePlatform(
2687
        $startDate = '',
2688
        $endDate = ''
2689
    ) {
2690
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2691
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2692
2693
        $url_table = null;
2694
        $url_condition = null;
2695
        if (api_is_multiple_url_enabled()) {
2696
            $access_url_id = api_get_current_access_url_id();
2697
            $url_table = ", ".$tbl_url_rel_user." as url_users";
2698
            $url_condition = " AND u.login_user_id = url_users.user_id AND access_url_id='$access_url_id'";
2699
        }
2700
2701
        if (!empty($startDate) && !empty($endDate)) {
2702
            $startDate = Database::escape_string($startDate);
2703
            $endDate = Database::escape_string($endDate);
2704
            $condition_time = ' (login_date >= "'.$startDate.'" AND logout_date <= "'.$endDate.'" ) ';
2705
        }
2706
        $sql = "SELECT SUM(TIMESTAMPDIFF(SECOND, login_date, logout_date)) diff
2707
    	        FROM $tbl_track_login u $url_table
2708
                WHERE $condition_time $url_condition";
2709
        $rs = Database::query($sql);
2710
        $row = Database::fetch_array($rs, 'ASSOC');
2711
        $diff = $row['diff'];
2712
2713
        if ($diff >= 0) {
2714
            return $diff;
2715
        }
2716
2717
        return -1;
2718
    }
2719
2720
    /**
2721
     * Return the total time spent in courses (no the total in platform).
2722
     *
2723
     * @return int
2724
     */
2725
    public static function getTotalTimeSpentInCourses(
2726
        string $dateFrom = '',
2727
        string $dateUntil = ''
2728
    ) {
2729
        $tableTrackLogin = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2730
        $tableUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2731
2732
        $tableUrl = null;
2733
        $urlCondition = null;
2734
        $conditionTime = null;
2735
        if (api_is_multiple_url_enabled()) {
2736
            $accessUrlId = api_get_current_access_url_id();
2737
            $tableUrl = ", ".$tableUrlRelUser." as url_users";
2738
            $urlCondition = " AND teca.user_id = url_users.user_id AND url_users.access_url_id = $accessUrlId";
2739
        }
2740
2741
        if (!empty($dateFrom) && !empty($dateUntil)) {
2742
            $dateFrom = Database::escape_string($dateFrom);
2743
            $dateUntil = Database::escape_string($dateUntil);
2744
            $conditionTime = " (teca.login_course_date >= '$dateFrom' AND teca.logout_course_date <= '$dateUntil' ) ";
2745
        }
2746
        $sql = "SELECT SUM(TIMESTAMPDIFF(HOUR, teca.login_course_date, teca.logout_course_date)) diff
2747
    	        FROM $tableTrackLogin teca $tableUrl
2748
                WHERE $conditionTime $urlCondition";
2749
2750
        $rs = Database::query($sql);
2751
        if (Database::num_rows($rs) < 1) {
2752
            return -1;
2753
        }
2754
        $row = Database::fetch_array($rs, 'ASSOC');
2755
        $diff = $row['diff'];
2756
2757
        if (isset($diff) && $diff >= 0) {
2758
            return $diff;
2759
        }
2760
2761
        return -1;
2762
    }
2763
2764
    /**
2765
     * Checks if the "lp_minimum_time" feature is available for the course.
2766
     *
2767
     * @param int $sessionId
2768
     * @param int $courseId
2769
     *
2770
     * @return bool
2771
     */
2772
    public static function minimumTimeAvailable($sessionId, $courseId)
2773
    {
2774
        if (!api_get_configuration_value('lp_minimum_time')) {
2775
            return false;
2776
        }
2777
2778
        if (!empty($sessionId)) {
2779
            $extraFieldValue = new ExtraFieldValue('session');
2780
            $value = $extraFieldValue->get_values_by_handler_and_field_variable($sessionId, 'new_tracking_system');
2781
2782
            if ($value && isset($value['value']) && 1 == $value['value']) {
2783
                return true;
2784
            }
2785
        } else {
2786
            if ($courseId) {
2787
                $extraFieldValue = new ExtraFieldValue('course');
2788
                $value = $extraFieldValue->get_values_by_handler_and_field_variable($courseId, 'new_tracking_system');
2789
                if ($value && isset($value['value']) && 1 == $value['value']) {
2790
                    return true;
2791
                }
2792
            }
2793
        }
2794
2795
        return false;
2796
    }
2797
2798
    /**
2799
     * Calculates the time spent on the course.
2800
     *
2801
     * @param int|array<int, int> $user_id
2802
     * @param int                 $courseId
2803
     * @param int                 $session_id
2804
     * @param string              $startDate  date string
2805
     * @param string              $endDate    date string
2806
     *
2807
     * @return int Time in seconds
2808
     */
2809
    public static function get_time_spent_on_the_course(
2810
        $user_id,
2811
        $courseId,
2812
        $session_id = 0,
2813
        $startDate = null,
2814
        $endDate = null
2815
    ) {
2816
        $courseId = (int) $courseId;
2817
2818
        if (empty($courseId) || empty($user_id)) {
2819
            return 0;
2820
        }
2821
2822
        if (self::minimumTimeAvailable($session_id, $courseId)) {
2823
            $courseTime = self::getCalculateTime($user_id, $courseId, $session_id);
2824
2825
            return isset($courseTime['total_time']) ? $courseTime['total_time'] : 0;
2826
        }
2827
2828
        $conditionUser = '';
2829
        $session_id = (int) $session_id;
2830
        if (is_array($user_id)) {
2831
            $user_id = array_map('intval', $user_id);
2832
            $conditionUser = " AND user_id IN (".implode(',', $user_id).") ";
2833
        } else {
2834
            if (!empty($user_id)) {
2835
                $user_id = (int) $user_id;
2836
                $conditionUser = " AND user_id = $user_id ";
2837
            }
2838
        }
2839
2840
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2841
        $sql = "SELECT
2842
                SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as nb_seconds
2843
                FROM $table
2844
                WHERE
2845
                    UNIX_TIMESTAMP(logout_course_date) > UNIX_TIMESTAMP(login_course_date) AND
2846
                    c_id = '$courseId' ";
2847
2848
        if (-1 != $session_id) {
2849
            $sql .= "AND session_id = '$session_id' ";
2850
        }
2851
2852
        if (!empty($startDate)) {
2853
            $startDate = api_get_utc_datetime($startDate, false, true);
2854
            $sql .= " AND login_course_date >= '".$startDate->format('Y-m-d 00:00:00')."' ";
2855
        }
2856
        if (!empty($endDate)) {
2857
            $endDate = api_get_utc_datetime($endDate, false, true);
2858
            $sql .= " AND login_course_date <= '".$endDate->format('Y-m-d 23:59:59')."' ";
2859
        }
2860
2861
        $sql .= $conditionUser;
2862
2863
        $rs = Database::query($sql);
2864
        $row = Database::fetch_array($rs);
2865
2866
        return $row['nb_seconds'];
2867
    }
2868
2869
    /**
2870
     * Get first connection date for a student.
2871
     *
2872
     * @param int $student_id
2873
     *
2874
     * @return string|bool Date format long without day or false if there are no connections
2875
     */
2876
    public static function get_first_connection_date($student_id, $dateFormat = DATE_FORMAT_SHORT)
2877
    {
2878
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2879
        $sql = 'SELECT login_date
2880
                FROM '.$table.'
2881
                WHERE login_user_id = '.intval($student_id).'
2882
                ORDER BY login_date ASC
2883
                LIMIT 0,1';
2884
2885
        $rs = Database::query($sql);
2886
        if (Database::num_rows($rs) > 0) {
2887
            if ($first_login_date = Database::result($rs, 0, 0)) {
2888
                return api_convert_and_format_date(
2889
                    $first_login_date,
2890
                    $dateFormat
2891
                );
2892
            }
2893
        }
2894
2895
        return false;
2896
    }
2897
2898
    /**
2899
     * Get las connection date for a student.
2900
     *
2901
     * @param int  $student_id
2902
     * @param bool $warning_message  Show a warning message (optional)
2903
     * @param bool $return_timestamp True for returning results in timestamp (optional)
2904
     *
2905
     * @return string|int|bool Date format long without day, false if there are no connections or
2906
     *                         timestamp if parameter $return_timestamp is true
2907
     */
2908
    public static function get_last_connection_date(
2909
        $student_id,
2910
        $warning_message = false,
2911
        $return_timestamp = false,
2912
        $dateFormat = DATE_FORMAT_SHORT
2913
    ) {
2914
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2915
        $sql = 'SELECT login_date
2916
                FROM '.$table.'
2917
                WHERE login_user_id = '.intval($student_id).'
2918
                ORDER BY login_date
2919
                DESC LIMIT 0,1';
2920
2921
        $rs = Database::query($sql);
2922
        if (Database::num_rows($rs) > 0) {
2923
            if ($last_login_date = Database::result($rs, 0, 0)) {
2924
                $last_login_date = api_get_local_time($last_login_date);
2925
                if ($return_timestamp) {
2926
                    return api_strtotime($last_login_date, 'UTC');
2927
                } else {
2928
                    if (!$warning_message) {
2929
                        return api_format_date($last_login_date, $dateFormat);
2930
                    } else {
2931
                        $timestamp = api_strtotime($last_login_date, 'UTC');
2932
                        $currentTimestamp = time();
2933
2934
                        // If the last connection is > than 7 days, the text is red
2935
                        // 604800 = 7 days in seconds
2936
                        if ($currentTimestamp - $timestamp > 604800) {
2937
                            return '<span style="color: #F00;">'.api_format_date($last_login_date, $dateFormat).'</span>';
2938
                        } else {
2939
                            return api_format_date($last_login_date, $dateFormat);
2940
                        }
2941
                    }
2942
                }
2943
            }
2944
        }
2945
2946
        return false;
2947
    }
2948
2949
    /**
2950
     * Get first user's connection date on the course.
2951
     *
2952
     * @param int User id
2953
     * @param int $courseId
2954
     * @param int Session id (optional, default=0)
2955
     * @param bool $convert_date
2956
     *
2957
     * @return string|bool Date with format long without day or false if there is no date
2958
     */
2959
    public static function get_first_connection_date_on_the_course(
2960
        $student_id,
2961
        $courseId,
2962
        $session_id = 0,
2963
        $convert_date = true
2964
    ) {
2965
        $student_id = (int) $student_id;
2966
        $courseId = (int) $courseId;
2967
        $session_id = (int) $session_id;
2968
2969
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2970
        $sql = 'SELECT login_course_date
2971
                FROM '.$table.'
2972
                WHERE
2973
                    user_id = '.$student_id.' AND
2974
                    c_id = '.$courseId.' AND
2975
                    session_id = '.$session_id.'
2976
                ORDER BY login_course_date ASC
2977
                LIMIT 0,1';
2978
        $rs = Database::query($sql);
2979
        if (Database::num_rows($rs) > 0) {
2980
            if ($first_login_date = Database::result($rs, 0, 0)) {
2981
                if (empty($first_login_date)) {
2982
                    return false;
2983
                }
2984
2985
                if ($convert_date) {
2986
                    return api_convert_and_format_date(
2987
                        $first_login_date,
2988
                        DATE_FORMAT_SHORT
2989
                    );
2990
                }
2991
2992
                return $first_login_date;
2993
            }
2994
        }
2995
2996
        return false;
2997
    }
2998
2999
    /**
3000
     * Get last user's connection date on the course.
3001
     *
3002
     * @param int         User id
3003
     * @param array $courseInfo real_id and code are used
3004
     * @param int            Session id (optional, default=0)
3005
     * @param bool $convert_date
3006
     *
3007
     * @return string|bool Date with format long without day or false if there is no date
3008
     */
3009
    public static function get_last_connection_date_on_the_course(
3010
        $student_id,
3011
        $courseInfo,
3012
        $session_id = 0,
3013
        $convert_date = true
3014
    ) {
3015
        // protect data
3016
        $student_id = (int) $student_id;
3017
        $session_id = (int) $session_id;
3018
3019
        if (empty($courseInfo) || empty($student_id)) {
3020
            return false;
3021
        }
3022
3023
        $courseId = $courseInfo['real_id'];
3024
3025
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
3026
3027
        if (self::minimumTimeAvailable($session_id, $courseId)) {
3028
            // Show the last date on which the user acceed the session when it was active
3029
            $where_condition = '';
3030
            $userInfo = api_get_user_info($student_id);
3031
            if (STUDENT == $userInfo['status'] && !empty($session_id)) {
3032
                // fin de acceso a la sesión
3033
                $sessionInfo = SessionManager::fetch($session_id);
3034
                $last_access = $sessionInfo['access_end_date'];
3035
                if (!empty($last_access)) {
3036
                    $where_condition = ' AND logout_course_date < "'.$last_access.'" ';
3037
                }
3038
            }
3039
            $sql = "SELECT logout_course_date
3040
                    FROM $table
3041
                    WHERE   user_id = $student_id AND
3042
                            c_id = $courseId AND
3043
                            session_id = $session_id $where_condition
3044
                    ORDER BY logout_course_date DESC
3045
                    LIMIT 0,1";
3046
3047
            $rs = Database::query($sql);
3048
            if (Database::num_rows($rs) > 0) {
3049
                if ($last_login_date = Database::result($rs, 0, 0)) {
3050
                    if (empty($last_login_date)) {
3051
                        return false;
3052
                    }
3053
                    if ($convert_date) {
3054
                        return api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
3055
                    }
3056
3057
                    return $last_login_date;
3058
                }
3059
            }
3060
        } else {
3061
            $sql = "SELECT logout_course_date
3062
                    FROM $table
3063
                    WHERE   user_id = $student_id AND
3064
                            c_id = $courseId AND
3065
                            session_id = $session_id
3066
                    ORDER BY logout_course_date DESC
3067
                    LIMIT 0,1";
3068
3069
            $rs = Database::query($sql);
3070
            if (Database::num_rows($rs) > 0) {
3071
                if ($last_login_date = Database::result($rs, 0, 0)) {
3072
                    if (empty($last_login_date)) {
3073
                        return false;
3074
                    }
3075
                    // see #5736
3076
                    $last_login_date_timestamp = api_strtotime($last_login_date);
3077
                    $now = time();
3078
                    // If the last connection is > than 7 days, the text is red
3079
                    // 604800 = 7 days in seconds
3080
                    if ($now - $last_login_date_timestamp > 604800) {
3081
                        if ($convert_date) {
3082
                            $last_login_date = api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
3083
                            $icon = null;
3084
                            if (api_is_allowed_to_edit()) {
3085
                                $url = api_get_path(WEB_CODE_PATH).
3086
                                    'announcements/announcements.php?action=add&remind_inactive='.$student_id.'&cidReq='.$courseInfo['code'];
3087
                                $icon = '<a href="'.$url.'" title="'.get_lang('RemindInactiveUser').'">
3088
                                  '.Display::return_icon('messagebox_warning.gif').'
3089
                                 </a>';
3090
                            }
3091
3092
                            return $icon.Display::label($last_login_date, 'warning');
3093
                        }
3094
3095
                        return $last_login_date;
3096
                    } else {
3097
                        if ($convert_date) {
3098
                            return api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
3099
                        }
3100
3101
                        return $last_login_date;
3102
                    }
3103
                }
3104
            }
3105
        }
3106
3107
        return false;
3108
    }
3109
3110
    public static function getLastConnectionInAnyCourse($studentId)
3111
    {
3112
        $studentId = (int) $studentId;
3113
3114
        if (empty($studentId)) {
3115
            return false;
3116
        }
3117
3118
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
3119
        $sql = "SELECT logout_course_date
3120
                FROM $table
3121
                WHERE user_id = $studentId
3122
                ORDER BY logout_course_date DESC
3123
                LIMIT 1";
3124
        $result = Database::query($sql);
3125
        if (Database::num_rows($result)) {
3126
            $row = Database::fetch_array($result);
3127
3128
            return $row['logout_course_date'];
3129
        }
3130
3131
        return false;
3132
    }
3133
3134
    /**
3135
     * Get last course access by course/session.
3136
     */
3137
    public static function getLastConnectionDateByCourse($courseId, $sessionId = 0)
3138
    {
3139
        $courseId = (int) $courseId;
3140
        $sessionId = (int) $sessionId;
3141
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
3142
3143
        $sql = "SELECT logout_course_date
3144
                FROM $table
3145
                WHERE
3146
                        c_id = $courseId AND
3147
                        session_id = $sessionId
3148
                ORDER BY logout_course_date DESC
3149
                LIMIT 0,1";
3150
3151
        $result = Database::query($sql);
3152
        if (Database::num_rows($result)) {
3153
            $row = Database::fetch_array($result);
3154
            if ($row) {
3155
                return $row['logout_course_date'];
3156
            }
3157
        }
3158
3159
        return '';
3160
    }
3161
3162
    /**
3163
     * Get count of the connections to the course during a specified period.
3164
     *
3165
     * @param int $courseId
3166
     * @param   int     Session id (optional)
3167
     * @param   int     Datetime from which to collect data (defaults to 0)
3168
     * @param   int     Datetime to which to collect data (defaults to now)
3169
     *
3170
     * @return int count connections
3171
     */
3172
    public static function get_course_connections_count(
3173
        $courseId,
3174
        $session_id = 0,
3175
        $start = 0,
3176
        $stop = null
3177
    ) {
3178
        if ($start < 0) {
3179
            $start = 0;
3180
        }
3181
        if (!isset($stop) || $stop < 0) {
3182
            $stop = api_get_utc_datetime();
3183
        }
3184
3185
        // Given we're storing in cache, round the start and end times
3186
        // to the lower minute
3187
        $roundedStart = substr($start, 0, -2).'00';
3188
        $roundedStop = substr($stop, 0, -2).'00';
3189
        $roundedStart = Database::escape_string($roundedStart);
3190
        $roundedStop = Database::escape_string($roundedStop);
3191
        $month_filter = " AND login_course_date > '$roundedStart' AND login_course_date < '$roundedStop' ";
3192
        $courseId = (int) $courseId;
3193
        $session_id = (int) $session_id;
3194
        $count = 0;
3195
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
3196
        $sql = "SELECT count(*) as count_connections
3197
                FROM $table
3198
                WHERE
3199
                    c_id = $courseId AND
3200
                    session_id = $session_id
3201
                    $month_filter";
3202
3203
        // This query can be very slow (several seconds on an indexed table
3204
        // with 14M rows). As such, we'll try to use APCu if it is
3205
        // available to store the resulting value for a few seconds
3206
        $cacheAvailable = api_get_configuration_value('apc');
3207
        if ($cacheAvailable === true) {
3208
            $apc = apcu_cache_info(true);
3209
            $apc_end = $apc['start_time'] + $apc['ttl'];
3210
            $apc_var = api_get_configuration_value('apc_prefix').'course_access_'.$courseId.'_'.$session_id.'_'.strtotime($roundedStart).'_'.strtotime($roundedStop);
3211
            if (apcu_exists($apc_var) && (time() < $apc_end)
3212
                && apcu_fetch($apc_var) > 0
3213
            ) {
3214
                $count = apcu_fetch($apc_var);
3215
            } else {
3216
                $rs = Database::query($sql);
3217
                if (Database::num_rows($rs) > 0) {
3218
                    $row = Database::fetch_object($rs);
3219
                    $count = $row->count_connections;
3220
                }
3221
                apcu_clear_cache();
3222
                apcu_store($apc_var, $count, 60);
3223
            }
3224
        } else {
3225
            $rs = Database::query($sql);
3226
            if (Database::num_rows($rs) > 0) {
3227
                $row = Database::fetch_object($rs);
3228
                $count = $row->count_connections;
3229
            }
3230
        }
3231
3232
        return $count;
3233
    }
3234
3235
    /**
3236
     * Get count courses per student.
3237
     *
3238
     * @param int  $user_id          Student id
3239
     * @param bool $include_sessions Include sessions (optional)
3240
     *
3241
     * @return int count courses
3242
     */
3243
    public static function count_course_per_student($user_id, $include_sessions = true)
3244
    {
3245
        $user_id = (int) $user_id;
3246
        $tbl_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3247
        $tbl_session_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3248
3249
        $sql = 'SELECT DISTINCT c_id
3250
                FROM '.$tbl_course_rel_user.'
3251
                WHERE user_id = '.$user_id.' AND relation_type<>'.COURSE_RELATION_TYPE_RRHH;
3252
        $rs = Database::query($sql);
3253
        $nb_courses = Database::num_rows($rs);
3254
3255
        if ($include_sessions) {
3256
            $sql = 'SELECT DISTINCT c_id
3257
                    FROM '.$tbl_session_course_rel_user.'
3258
                    WHERE user_id = '.$user_id;
3259
            $rs = Database::query($sql);
3260
            $nb_courses += Database::num_rows($rs);
3261
        }
3262
3263
        return $nb_courses;
3264
    }
3265
3266
    public static function countSessionsPerStudent(int $userId): int
3267
    {
3268
        $tblSessionUser = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3269
3270
        $sql = 'SELECT DISTINCT id
3271
            FROM '.$tblSessionUser.'
3272
            WHERE relation_type = '.SessionEntity::STUDENT.' AND user_id = '.$userId;
3273
3274
        $rs = Database::query($sql);
3275
3276
        return Database::num_rows($rs);
3277
    }
3278
3279
    /**
3280
     * Gets the score average from all tests in a course by student.
3281
     *
3282
     * @param $student_id
3283
     * @param $course_code
3284
     * @param int  $exercise_id
3285
     * @param null $session_id
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $session_id is correct as it would always require null to be passed?
Loading history...
3286
     * @param int  $active_filter 2 for consider all tests
3287
     *                            1 for active <> -1
3288
     *                            0 for active <> 0
3289
     * @param int  $into_lp       1 for all exercises
3290
     *                            0 for without LP
3291
     * @param mixed id
3292
     * @param string code
3293
     * @param int id (optional), filtered by exercise
3294
     * @param int id (optional), if param $session_id is null
3295
     *                                                it'll return results including sessions, 0 = session is not
3296
     *                                                filtered
3297
     *
3298
     * @return string value (number %) Which represents a round integer about the score average
3299
     */
3300
    public static function get_avg_student_exercise_score(
3301
        $student_id,
3302
        $course_code,
3303
        $exercise_id = 0,
3304
        $session_id = null,
3305
        $active_filter = 1,
3306
        $into_lp = 0
3307
    ) {
3308
        $course_code = Database::escape_string($course_code);
3309
        $course_info = api_get_course_info($course_code);
3310
        if (!empty($course_info)) {
3311
            // table definition
3312
            $tbl_course_quiz = Database::get_course_table(TABLE_QUIZ_TEST);
3313
            $tbl_stats_exercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3314
3315
            // Compose a filter based on optional exercise given
3316
            $condition_quiz = "";
3317
            if (!empty($exercise_id)) {
3318
                $exercise_id = intval($exercise_id);
3319
                $condition_quiz = " AND iid = $exercise_id ";
3320
            }
3321
3322
            // Compose a filter based on optional session id given
3323
            $condition_session = '';
3324
            if (isset($session_id)) {
3325
                $session_id = intval($session_id);
3326
                $condition_session = " AND session_id = $session_id ";
3327
            }
3328
            if ($active_filter == 1) {
3329
                $condition_active = 'AND active <> -1';
3330
            } elseif ($active_filter == 0) {
3331
                $condition_active = 'AND active <> 0';
3332
            } else {
3333
                $condition_active = '';
3334
            }
3335
            $condition_into_lp = '';
3336
            $select_lp_id = '';
3337
            if ($into_lp == 0) {
3338
                $condition_into_lp = 'AND orig_lp_id = 0 AND orig_lp_item_id = 0';
3339
            } else {
3340
                $select_lp_id = ', orig_lp_id as lp_id ';
3341
            }
3342
3343
            $sql = "SELECT count(iid)
3344
    		        FROM $tbl_course_quiz
3345
    				WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz ";
3346
            $count_quiz = 0;
3347
            $countQuizResult = Database::query($sql);
3348
            if (!empty($countQuizResult)) {
3349
                $count_quiz = Database::fetch_row($countQuizResult);
3350
            }
3351
3352
            if (!empty($count_quiz[0]) && !empty($student_id)) {
3353
                if (is_array($student_id)) {
3354
                    $student_id = array_map('intval', $student_id);
3355
                    $condition_user = " AND exe_user_id IN (".implode(',', $student_id).") ";
3356
                } else {
3357
                    $student_id = intval($student_id);
3358
                    $condition_user = " AND exe_user_id = '$student_id' ";
3359
                }
3360
3361
                if (empty($exercise_id)) {
3362
                    $sql = "SELECT iid FROM $tbl_course_quiz
3363
                            WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz";
3364
                    $result = Database::query($sql);
3365
                    $exercise_list = [];
3366
                    $exercise_id = null;
3367
                    if (!empty($result) && Database::num_rows($result)) {
3368
                        while ($row = Database::fetch_array($result)) {
3369
                            $exercise_list[] = $row['iid'];
3370
                        }
3371
                    }
3372
                    if (!empty($exercise_list)) {
3373
                        $exercise_id = implode("','", $exercise_list);
3374
                    }
3375
                }
3376
3377
                $count_quiz = Database::fetch_row(Database::query($sql));
3378
                $sql = "SELECT
3379
                        SUM(exe_result/exe_weighting*100) as avg_score,
3380
                        COUNT(*) as num_attempts
3381
                        $select_lp_id
3382
                        FROM $tbl_stats_exercise
3383
                        WHERE
3384
                            exe_exo_id IN ('".$exercise_id."')
3385
                            $condition_user AND
3386
                            status = '' AND
3387
                            c_id = {$course_info['real_id']}
3388
                            $condition_session
3389
                            $condition_into_lp
3390
                        ORDER BY exe_date DESC";
3391
3392
                $res = Database::query($sql);
3393
                $row = Database::fetch_array($res);
3394
                $quiz_avg_score = null;
3395
3396
                if (!empty($row['avg_score'])) {
3397
                    $quiz_avg_score = round($row['avg_score'], 2);
3398
                }
3399
3400
                if (!empty($row['num_attempts'])) {
3401
                    $quiz_avg_score = round($quiz_avg_score / $row['num_attempts'], 2);
3402
                }
3403
                if (is_array($student_id)) {
3404
                    $quiz_avg_score = round($quiz_avg_score / count($student_id), 2);
3405
                }
3406
                if ($into_lp == 0) {
3407
                    return $quiz_avg_score;
3408
                } else {
3409
                    if (!empty($row['lp_id'])) {
3410
                        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
3411
                        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3412
                        $sql = "SELECT lp.name
3413
                                FROM $tbl_lp as lp, $tbl_course as c
3414
                                WHERE
3415
                                    c.code = '$course_code' AND
3416
                                    lp.id = ".$row['lp_id']." AND
3417
                                    lp.c_id = c.id
3418
                                LIMIT 1;
3419
                        ";
3420
                        $result = Database::query($sql);
3421
                        $row_lp = Database::fetch_row($result);
3422
                        $lp_name = null;
3423
                        if ($row_lp && isset($row_lp[0])) {
3424
                            $lp_name = $row_lp[0];
3425
                        }
3426
3427
                        return [$quiz_avg_score, $lp_name];
3428
                    } else {
3429
                        return [$quiz_avg_score, null];
3430
                    }
3431
                }
3432
            }
3433
        }
3434
3435
        return null;
3436
    }
3437
3438
    /**
3439
     * Get count student's exercise COMPLETED attempts.
3440
     *
3441
     * @param int $student_id
3442
     * @param int $courseId
3443
     * @param int $exercise_id
3444
     * @param int $lp_id
3445
     * @param int $lp_item_id
3446
     * @param int $session_id
3447
     * @param int $find_all_lp 0 = just LP specified
3448
     *                         1 = LP specified or whitout LP,
3449
     *                         2 = all rows
3450
     *
3451
     * @internal param \Student $int id
3452
     * @internal param \Course $string code
3453
     * @internal param \Exercise $int id
3454
     * @internal param \Learning $int path id (optional),
3455
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required
3456
     * @internal param \Learning $int path item id (optional),
3457
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required
3458
     *
3459
     * @return int count of attempts
3460
     */
3461
    public static function count_student_exercise_attempts(
3462
        $student_id,
3463
        $courseId,
3464
        $exercise_id,
3465
        $lp_id = 0,
3466
        $lp_item_id = 0,
3467
        $session_id = 0,
3468
        $find_all_lp = 0
3469
    ) {
3470
        $courseId = intval($courseId);
3471
        $student_id = intval($student_id);
3472
        $exercise_id = intval($exercise_id);
3473
        $session_id = intval($session_id);
3474
3475
        $lp_id = intval($lp_id);
3476
        $lp_item_id = intval($lp_item_id);
3477
        $tbl_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3478
3479
        $sql = "SELECT COUNT(ex.exe_id) as essais
3480
                FROM $tbl_stats_exercises AS ex
3481
                WHERE
3482
                    ex.c_id = $courseId AND
3483
                    ex.exe_exo_id = $exercise_id AND
3484
                    status = '' AND
3485
                    exe_user_id= $student_id AND
3486
                    session_id = $session_id ";
3487
3488
        if ($find_all_lp == 1) {
3489
            $sql .= "AND (orig_lp_id = $lp_id OR orig_lp_id = 0)
3490
                AND (orig_lp_item_id = $lp_item_id OR orig_lp_item_id = 0)";
3491
        } elseif ($find_all_lp == 0) {
3492
            $sql .= "AND orig_lp_id = $lp_id
3493
                AND orig_lp_item_id = $lp_item_id";
3494
        }
3495
3496
        $rs = Database::query($sql);
3497
        $row = Database::fetch_row($rs);
3498
        $count_attempts = $row[0];
3499
3500
        return $count_attempts;
3501
    }
3502
3503
    /**
3504
     * It gets the last finalization date of learnpaths in a course.
3505
     *
3506
     * @return string finalization date formatted or false if it is empty.
3507
     */
3508
    public static function getCourseLpFinalizationDate(
3509
        int $userId,
3510
        int $courseId,
3511
        int $sessionId,
3512
        bool $convertDate = true
3513
    ) {
3514
        $tblLpView = Database::get_course_table(TABLE_LP_VIEW);
3515
        $tblLpItem = Database::get_course_table(TABLE_LP_ITEM);
3516
        $tblLpItemView = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3517
3518
        $sql = "SELECT FROM_UNIXTIME(liv.start_time) as start_date
3519
                FROM $tblLpItemView liv
3520
                INNER JOIN
3521
                    $tblLpView lv ON lv.iid = liv.lp_view_id
3522
                INNER JOIN
3523
                    $tblLpItem li ON li.iid = liv.lp_item_id
3524
                WHERE
3525
                    lv.user_id = $userId AND
3526
                    lv.c_id = $courseId AND
3527
                    lv.session_id = $sessionId AND
3528
                    li.item_type = '".TOOL_LP_FINAL_ITEM."' AND
3529
                    liv.status = 'completed'
3530
                ORDER BY start_date DESC
3531
                LIMIT 1";
3532
3533
        $rs = Database::query($sql);
3534
        $lpFinalDate = Database::result($rs, 0, 0);
3535
3536
        if (empty($lpFinalDate)) {
3537
            return false;
3538
        }
3539
3540
        if ($convertDate) {
3541
            return api_convert_and_format_date($lpFinalDate, DATE_FORMAT_SHORT);
3542
        }
3543
3544
        return $lpFinalDate;
3545
    }
3546
3547
    /**
3548
     * It gets the last finalization date of exercises in a course.
3549
     *
3550
     * @return string finalization date formatted or false if it is empty.
3551
     */
3552
    public static function getCourseQuizLastFinalizationDate(
3553
        int $userId,
3554
        int $courseId,
3555
        int $sessionId,
3556
        bool $convertDate = true
3557
    ) {
3558
        $tblTrackExercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3559
3560
        $sql = "SELECT ex.exe_date
3561
                FROM $tblTrackExercise AS ex
3562
                WHERE
3563
                    ex.c_id = $courseId AND
3564
                    ex.session_id  = $sessionId AND
3565
                    ex.exe_user_id = $userId AND
3566
                    ex.status = ''
3567
                ORDER BY ex.exe_date DESC
3568
                LIMIT 1";
3569
        $rs = Database::query($sql);
3570
        $exeDate = Database::result($rs, 0, 0);
3571
3572
        if (empty($exeDate)) {
3573
            return false;
3574
        }
3575
3576
        if ($convertDate) {
3577
            return api_convert_and_format_date($exeDate, DATE_FORMAT_SHORT);
3578
        }
3579
3580
        return $exeDate;
3581
    }
3582
3583
    /**
3584
     * Get count student's exercise progress.
3585
     *
3586
     * @param array $exercise_list
3587
     * @param int   $user_id
3588
     * @param int   $courseId
3589
     * @param int   $session_id
3590
     *
3591
     * @return string
3592
     */
3593
    public static function get_exercise_student_progress(
3594
        $exercise_list,
3595
        $user_id,
3596
        $courseId,
3597
        $session_id
3598
    ) {
3599
        $courseId = (int) $courseId;
3600
        $user_id = (int) $user_id;
3601
        $session_id = (int) $session_id;
3602
3603
        if (empty($exercise_list)) {
3604
            return '0%';
3605
        }
3606
        $tbl_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3607
        $exercise_list = array_keys($exercise_list);
3608
        $exercise_list = array_map('intval', $exercise_list);
3609
3610
        $exercise_list_imploded = implode("' ,'", $exercise_list);
3611
3612
        $sql = "SELECT COUNT(DISTINCT ex.exe_exo_id)
3613
                FROM $tbl_stats_exercises AS ex
3614
                WHERE
3615
                    ex.c_id = $courseId AND
3616
                    ex.session_id  = $session_id AND
3617
                    ex.exe_user_id = $user_id AND
3618
                    ex.status = '' AND
3619
                    ex.exe_exo_id IN ('$exercise_list_imploded') ";
3620
3621
        $rs = Database::query($sql);
3622
        $count = 0;
3623
        if ($rs) {
3624
            $row = Database::fetch_row($rs);
3625
            $count = $row[0];
3626
        }
3627
        $count = ($count != 0) ? 100 * round(intval($count) / count($exercise_list), 2).'%' : '0%';
3628
3629
        return $count;
3630
    }
3631
3632
    /**
3633
     * @param array $exercise_list
3634
     * @param int   $user_id
3635
     * @param int   $courseId
3636
     * @param int   $session_id
3637
     *
3638
     * @return string
3639
     */
3640
    public static function get_exercise_student_average_best_attempt(
3641
        $exercise_list,
3642
        $user_id,
3643
        $courseId,
3644
        $session_id
3645
    ) {
3646
        $result = 0;
3647
        if (!empty($exercise_list)) {
3648
            foreach ($exercise_list as $exercise_data) {
3649
                $exercise_id = $exercise_data['iid'];
3650
                $best_attempt = Event::get_best_attempt_exercise_results_per_user(
3651
                    $user_id,
3652
                    $exercise_id,
3653
                    $courseId,
3654
                    $session_id
3655
                );
3656
3657
                if (!empty($best_attempt) && !empty($best_attempt['exe_weighting'])) {
3658
                    $result += $best_attempt['exe_result'] / $best_attempt['exe_weighting'];
3659
                }
3660
            }
3661
            $result = $result / count($exercise_list);
3662
            $result = round($result, 2) * 100;
3663
        }
3664
3665
        return $result.'%';
3666
    }
3667
3668
    /**
3669
     * Returns the average student progress in the learning paths of the given
3670
     * course, it will take into account the progress that were not started.
3671
     *
3672
     * @param int|array $studentId
3673
     * @param string    $courseCode
3674
     * @param array     $lpIdList        Limit average to listed lp ids
3675
     * @param int       $sessionId       Session id (optional),
3676
     *                                   if parameter $session_id is null(default) it'll return results including
3677
     *                                   sessions, 0 = session is not filtered
3678
     * @param bool      $returnArray     Will return an array of the type:
3679
     *                                   [sum_of_progresses, number] if it is set to true
3680
     * @param bool      $onlySeriousGame Optional. Limit average to lp on seriousgame mode
3681
     * @param bool      $maxInsteadAvg   Optional. It will return the max progress instead the average
3682
     *
3683
     * @return float Average or max progress of the user in this course from 0 to 100
3684
     */
3685
    public static function get_avg_student_progress(
3686
        $studentId,
3687
        $courseCode = null,
3688
        $lpIdList = [],
3689
        $sessionId = null,
3690
        $returnArray = false,
3691
        $onlySeriousGame = false,
3692
        $maxInsteadAvg = false,
3693
        $startDate = null,
3694
        $endDate = null
3695
    ) {
3696
        // If there is at least one learning path and one student.
3697
        if (empty($studentId)) {
3698
            return false;
3699
        }
3700
3701
        $sessionId = (int) $sessionId;
3702
        $courseInfo = api_get_course_info($courseCode);
3703
3704
        if (empty($courseInfo)) {
3705
            return false;
3706
        }
3707
3708
        $lPTable = Database::get_course_table(TABLE_LP_MAIN);
3709
        $lpViewTable = Database::get_course_table(TABLE_LP_VIEW);
3710
        $lpConditions = [];
3711
        $lpConditions['c_id = ? '] = $courseInfo['real_id'];
3712
3713
        if ($sessionId > 0) {
3714
            $lpConditions['AND (session_id = ? OR session_id = 0 OR session_id IS NULL)'] = $sessionId;
3715
        } else {
3716
            $lpConditions['AND session_id = ?'] = $sessionId;
3717
        }
3718
3719
        if (is_array($lpIdList) && count($lpIdList) > 0) {
3720
            $placeHolders = [];
3721
            for ($i = 0; $i < count($lpIdList); $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...
3722
                $placeHolders[] = '?';
3723
            }
3724
            $lpConditions['AND id IN('.implode(', ', $placeHolders).') '] = $lpIdList;
3725
        }
3726
3727
        if ($onlySeriousGame) {
3728
            $lpConditions['AND seriousgame_mode = ? '] = true;
3729
        }
3730
3731
        $resultLP = Database::select(
3732
            'id',
3733
            $lPTable,
3734
            ['where' => $lpConditions]
3735
        );
3736
        $filteredLP = array_keys($resultLP);
3737
3738
        if (empty($filteredLP)) {
3739
            return false;
3740
        }
3741
3742
        $conditions = [
3743
            " lp_view.c_id = {$courseInfo['real_id']} ",
3744
            " lp_view.lp_id IN (".implode(', ', $filteredLP).") ",
3745
        ];
3746
3747
        $groupBy = 'GROUP BY lp_view.lp_id';
3748
3749
        if (is_array($studentId)) {
3750
            $studentId = array_map('intval', $studentId);
3751
            $conditions[] = " lp_view.user_id IN (".implode(',', $studentId).")  ";
3752
        } else {
3753
            $studentId = (int) $studentId;
3754
            $conditions[] = " lp_view.user_id = '$studentId' ";
3755
3756
            if (empty($lpIdList)) {
3757
                $lpList = new LearnpathList(
3758
                    $studentId,
3759
                    $courseInfo,
3760
                    $sessionId,
3761
                    null,
3762
                    false,
3763
                    null,
3764
                    true,
3765
                    false,
3766
                    true,
3767
                    true
3768
                );
3769
                $lpList = $lpList->get_flat_list();
3770
                if (!empty($lpList)) {
3771
                    /** @var $lp */
3772
                    foreach ($lpList as $lpId => $lp) {
3773
                        $lpIdList[] = $lp['lp_old_id'];
3774
                    }
3775
                }
3776
            }
3777
        }
3778
3779
        if (!empty($sessionId)) {
3780
            $conditions[] = " session_id = $sessionId ";
3781
        } else {
3782
            $conditions[] = ' (session_id = 0 OR session_id IS NULL) ';
3783
        }
3784
3785
        $innerJoin = "";
3786
        if (!empty($startDate) || !empty($endDate)) {
3787
            $lpItemViewTable = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3788
            $innerJoin = " INNER JOIN $lpItemViewTable liv ON liv.lp_view_id = lp_view.iid";
3789
            if (!empty($startDate)) {
3790
                $startDate = api_get_utc_datetime($startDate, false, true);
3791
                $startTime = strtotime($startDate->format('Y-m-d 00:00:00'));
3792
                $conditions[] = " liv.start_time >= '".$startTime."' ";
3793
            }
3794
            if (!empty($endDate)) {
3795
                $endDate = api_get_utc_datetime($endDate, false, true);
3796
                $endTime = strtotime($endDate->format('Y-m-d 23:59:59'));
3797
                $conditions[] = " liv.start_time <= '".$endTime."' ";
3798
            }
3799
        }
3800
3801
        $conditionToString = implode('AND', $conditions);
3802
        $sql = "SELECT lp_view.lp_id, lp_view.view_count, lp_view.progress
3803
                FROM $lpViewTable lp_view
3804
                $innerJoin
3805
                WHERE
3806
                    $conditionToString
3807
                    $groupBy
3808
                ORDER BY view_count DESC";
3809
        $result = Database::query($sql);
3810
3811
        $progress = [];
3812
        $viewCount = [];
3813
        while ($row = Database::fetch_array($result, 'ASSOC')) {
3814
            if (!isset($viewCount[$row['lp_id']])) {
3815
                $progress[$row['lp_id']] = $row['progress'];
3816
            }
3817
            $viewCount[$row['lp_id']] = $row['view_count'];
3818
        }
3819
3820
        // Fill with lp ids
3821
        $newProgress = [];
3822
        if (!empty($lpIdList)) {
3823
            foreach ($lpIdList as $lpId) {
3824
                if (isset($progress[$lpId])) {
3825
                    $newProgress[] = $progress[$lpId];
3826
                }
3827
            }
3828
            $total = count($lpIdList);
3829
        } else {
3830
            $newProgress = $progress;
3831
            $total = count($newProgress);
3832
        }
3833
3834
        $average = 0;
3835
        $sum = 0;
3836
        if (!empty($newProgress)) {
3837
            if ($maxInsteadAvg) {
3838
                // It will return the max progress instead the average
3839
                return max($newProgress);
3840
            } else {
3841
                $sum = array_sum($newProgress);
3842
                $average = $sum / $total;
3843
            }
3844
        }
3845
3846
        if ($returnArray) {
3847
            return [
3848
                $sum,
3849
                $total,
3850
            ];
3851
        }
3852
3853
        return round($average, 1);
3854
    }
3855
3856
    /**
3857
     * This function gets:
3858
     * 1. The score average from all SCORM Test items in all LP in a course-> All the answers / All the max scores.
3859
     * 2. The score average from all Tests (quiz) in all LP in a course-> All the answers / All the max scores.
3860
     * 3. And finally it will return the average between 1. and 2.
3861
     *
3862
     * @todo improve performance, when loading 1500 users with 20 lps the script dies
3863
     * This function does not take the results of a Test out of a LP
3864
     *
3865
     * @param mixed  $student_id                      Array of user ids or an user id
3866
     * @param string $course_code
3867
     * @param array  $lp_ids                          List of LP ids
3868
     * @param int    $session_id                      Session id (optional),
3869
     *                                                if param $session_id is null(default) it'll return results
3870
     *                                                including sessions, 0 = session is not filtered
3871
     * @param bool   $return_array                    Returns an array of the
3872
     *                                                type [sum_score, num_score] if set to true
3873
     * @param bool   $get_only_latest_attempt_results get only the latest attempts or ALL attempts
3874
     * @param bool   $getOnlyBestAttempt
3875
     *
3876
     * @return string value (number %) Which represents a round integer explain in got in 3
3877
     */
3878
    public static function get_avg_student_score(
3879
        $student_id,
3880
        $course_code,
3881
        $lp_ids = [],
3882
        $session_id = null,
3883
        $return_array = false,
3884
        $get_only_latest_attempt_results = false,
3885
        $getOnlyBestAttempt = false
3886
    ) {
3887
        $debug = false;
3888
        if ($debug) {
3889
            echo '<h1>Tracking::get_avg_student_score</h1>';
3890
        }
3891
        $tbl_stats_exercices = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3892
        $tbl_stats_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
3893
        $course = api_get_course_info($course_code);
3894
3895
        if (empty($course)) {
3896
            return null;
3897
        }
3898
3899
        // Get course tables names
3900
        $tbl_quiz_questions = Database::get_course_table(TABLE_QUIZ_QUESTION);
3901
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3902
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
3903
        $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
3904
        $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3905
        $course_id = $course['real_id'];
3906
3907
        // Compose a filter based on optional learning paths list given
3908
        $condition_lp = '';
3909
        if (count($lp_ids) > 0) {
3910
            $condition_lp = " AND id IN(".implode(',', $lp_ids).") ";
3911
        }
3912
3913
        // Compose a filter based on optional session id
3914
        $session_id = (int) $session_id;
3915
        if (count($lp_ids) > 0) {
3916
            $condition_session = " AND session_id = $session_id ";
3917
        } else {
3918
            $condition_session = " WHERE session_id = $session_id ";
3919
        }
3920
3921
        // Check the real number of LPs corresponding to the filter in the
3922
        // database (and if no list was given, get them all)
3923
        if (empty($session_id)) {
3924
            $sql = "SELECT DISTINCT(id), use_max_score
3925
                    FROM $lp_table
3926
                    WHERE
3927
                        c_id = $course_id AND
3928
                        (session_id = 0 OR session_id IS NULL) $condition_lp ";
3929
        } else {
3930
            $sql = "SELECT DISTINCT(id), use_max_score
3931
                    FROM $lp_table
3932
                    WHERE c_id = $course_id $condition_lp ";
3933
        }
3934
3935
        $res_row_lp = Database::query($sql);
3936
        $count_row_lp = Database::num_rows($res_row_lp);
3937
3938
        $lp_list = $use_max_score = [];
3939
        while ($row_lp = Database::fetch_array($res_row_lp)) {
3940
            $lp_list[] = $row_lp['id'];
3941
            $use_max_score[$row_lp['id']] = $row_lp['use_max_score'];
3942
        }
3943
3944
        // prepare filter on users
3945
        if (is_array($student_id)) {
3946
            array_walk($student_id, 'intval');
3947
            $condition_user1 = " AND user_id IN (".implode(',', $student_id).") ";
3948
        } else {
3949
            $condition_user1 = " AND user_id = $student_id ";
3950
        }
3951
3952
        if (empty($count_row_lp) || empty($student_id)) {
3953
            return null;
3954
        }
3955
3956
        // Getting latest LP result for a student
3957
        // @todo problem when a  course have more than 1500 users
3958
        $sql = "SELECT MAX(view_count) as vc, id, progress, lp_id, user_id
3959
                FROM $lp_view_table
3960
                WHERE
3961
                    c_id = $course_id AND
3962
                    lp_id IN (".implode(',', $lp_list).")
3963
                    $condition_user1 AND
3964
                    session_id = $session_id
3965
                GROUP BY lp_id, user_id";
3966
3967
        $rs_last_lp_view_id = Database::query($sql);
3968
        $global_result = 0;
3969
3970
        if (Database::num_rows($rs_last_lp_view_id) > 0) {
3971
            // Cycle through each line of the results (grouped by lp_id, user_id)
3972
            while ($row_lp_view = Database::fetch_array($rs_last_lp_view_id)) {
3973
                $count_items = 0;
3974
                $lpPartialTotal = 0;
3975
                $list = [];
3976
                $lp_view_id = $row_lp_view['id'];
3977
                $lp_id = $row_lp_view['lp_id'];
3978
                $user_id = $row_lp_view['user_id'];
3979
3980
                if ($debug) {
3981
                    echo '<h2>LP id '.$lp_id.'</h2>';
3982
                    echo "get_only_latest_attempt_results: $get_only_latest_attempt_results <br />";
3983
                    echo "getOnlyBestAttempt: $getOnlyBestAttempt <br />";
3984
                }
3985
3986
                if ($get_only_latest_attempt_results || $getOnlyBestAttempt) {
3987
                    // Getting lp_items done by the user
3988
                    $sql = "SELECT DISTINCT lp_item_id
3989
                            FROM $lp_item_view_table
3990
                            WHERE
3991
                                c_id = $course_id AND
3992
                                lp_view_id = $lp_view_id
3993
                            ORDER BY lp_item_id";
3994
                    $res_lp_item = Database::query($sql);
3995
3996
                    while ($row_lp_item = Database::fetch_array($res_lp_item, 'ASSOC')) {
3997
                        $my_lp_item_id = $row_lp_item['lp_item_id'];
3998
                        $order = ' view_count DESC';
3999
                        if ($getOnlyBestAttempt) {
4000
                            $order = ' lp_iv.score DESC';
4001
                        }
4002
4003
                        // Getting the most recent attempt
4004
                        $sql = "SELECT
4005
                                    lp_iv.id as lp_item_view_id,
4006
                                    lp_iv.score as score,
4007
                                    lp_i.max_score,
4008
                                    lp_iv.max_score as max_score_item_view,
4009
                                    lp_i.path,
4010
                                    lp_i.item_type,
4011
                                    lp_i.id as iid
4012
                                FROM $lp_item_view_table as lp_iv
4013
                                INNER JOIN $lp_item_table as lp_i
4014
                                ON (
4015
                                    lp_i.id = lp_iv.lp_item_id AND
4016
                                    lp_iv.c_id = lp_i.c_id
4017
                                )
4018
                                WHERE
4019
                                    lp_iv.c_id = $course_id AND
4020
                                    lp_i.c_id  = $course_id AND
4021
                                    lp_item_id = $my_lp_item_id AND
4022
                                    lp_view_id = $lp_view_id AND
4023
                                    (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."')
4024
                                ORDER BY $order
4025
                                LIMIT 1";
4026
4027
                        $res_lp_item_result = Database::query($sql);
4028
                        while ($row_max_score = Database::fetch_array($res_lp_item_result, 'ASSOC')) {
4029
                            $list[] = $row_max_score;
4030
                        }
4031
                    }
4032
                } else {
4033
                    // For the currently analysed view, get the score and
4034
                    // max_score of each item if it is a sco or a TOOL_QUIZ
4035
                    $sql = "SELECT
4036
                                lp_iv.id as lp_item_view_id,
4037
                                lp_iv.score as score,
4038
                                lp_i.max_score,
4039
                                lp_iv.max_score as max_score_item_view,
4040
                                lp_i.path,
4041
                                lp_i.item_type,
4042
                                lp_i.id as iid
4043
                              FROM $lp_item_view_table as lp_iv
4044
                              INNER JOIN $lp_item_table as lp_i
4045
                              ON lp_i.id = lp_iv.lp_item_id AND
4046
                                 lp_iv.c_id = lp_i.c_id
4047
                              WHERE
4048
                                lp_iv.c_id = $course_id AND
4049
                                lp_i.c_id  = $course_id AND
4050
                                lp_view_id = $lp_view_id AND
4051
                                (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."')
4052
                            ";
4053
                    $res_max_score = Database::query($sql);
4054
                    while ($row_max_score = Database::fetch_array($res_max_score, 'ASSOC')) {
4055
                        $list[] = $row_max_score;
4056
                    }
4057
                }
4058
4059
                // Go through each scorable element of this view
4060
                $score_of_scorm_calculate = 0;
4061
                foreach ($list as $row_max_score) {
4062
                    // Came from the original lp_item
4063
                    $max_score = $row_max_score['max_score'];
4064
                    // Came from the lp_item_view
4065
                    $max_score_item_view = $row_max_score['max_score_item_view'];
4066
                    $score = $row_max_score['score'];
4067
                    if ($debug) {
4068
                        echo '<h3>Item Type: '.$row_max_score['item_type'].'</h3>';
4069
                    }
4070
4071
                    if ($row_max_score['item_type'] === 'sco') {
4072
                        /* Check if it is sco (easier to get max_score)
4073
                           when there's no max score, we assume 100 as the max score,
4074
                           as the SCORM 1.2 says that the value should always be between 0 and 100.
4075
                        */
4076
                        if ($max_score == 0 || is_null($max_score) || $max_score == '') {
4077
                            // Chamilo style
4078
                            if ($use_max_score[$lp_id]) {
4079
                                $max_score = 100;
4080
                            } else {
4081
                                // Overwrites max score = 100 to use the one that came in the lp_item_view see BT#1613
4082
                                $max_score = $max_score_item_view;
4083
                            }
4084
                        }
4085
                        // Avoid division by zero errors
4086
                        if (!empty($max_score)) {
4087
                            $lpPartialTotal += $score / $max_score;
4088
                        }
4089
                        if ($debug) {
4090
                            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...
4091
                            var_dump("score: $score");
4092
                            var_dump("max_score: $max_score");
4093
                        }
4094
                    } else {
4095
                        // Case of a TOOL_QUIZ element
4096
                        $item_id = $row_max_score['iid'];
4097
                        $item_path = $row_max_score['path'];
4098
                        $lp_item_view_id = (int) $row_max_score['lp_item_view_id'];
4099
4100
                        if (empty($lp_item_view_id)) {
4101
                            $lpItemCondition = ' (orig_lp_item_view_id = 0 OR orig_lp_item_view_id IS NULL) ';
4102
                        } else {
4103
                            $lpItemCondition = " orig_lp_item_view_id = $lp_item_view_id ";
4104
                        }
4105
4106
                        // Get last attempt to this exercise through
4107
                        // the current lp for the current user
4108
                        $order = 'exe_date DESC';
4109
                        if ($getOnlyBestAttempt) {
4110
                            $order = 'exe_result DESC';
4111
                        }
4112
                        $sql = "SELECT exe_id, exe_result
4113
                                FROM $tbl_stats_exercices
4114
                                WHERE
4115
                                    exe_exo_id = '$item_path' AND
4116
                                    exe_user_id = $user_id AND
4117
                                    orig_lp_item_id = $item_id AND
4118
                                    $lpItemCondition AND
4119
                                    c_id = $course_id AND
4120
                                    session_id = $session_id AND
4121
                                    status = ''
4122
                                ORDER BY $order
4123
                                LIMIT 1";
4124
4125
                        $result_last_attempt = Database::query($sql);
4126
                        $num = Database::num_rows($result_last_attempt);
4127
                        if ($num > 0) {
4128
                            $attemptResult = Database::fetch_array($result_last_attempt, 'ASSOC');
4129
                            $id_last_attempt = (int) $attemptResult['exe_id'];
4130
                            // We overwrite the score with the best one not the one saved in the LP (latest)
4131
                            if ($getOnlyBestAttempt && $get_only_latest_attempt_results == false) {
4132
                                if ($debug) {
4133
                                    echo "Following score comes from the track_exercise table not in the LP because the score is the best<br />";
4134
                                }
4135
                                $score = $attemptResult['exe_result'];
4136
                            }
4137
4138
                            if ($debug) {
4139
                                echo "Attempt id: $id_last_attempt with score $score<br />";
4140
                            }
4141
                            // Within the last attempt number tracking, get the sum of
4142
                            // the max_scores of all questions that it was
4143
                            // made of (we need to make this call dynamic because of random questions selection)
4144
                            $sql = "SELECT SUM(t.ponderation) as maxscore FROM
4145
                                    (
4146
                                        SELECT DISTINCT
4147
                                            question_id,
4148
                                            marks,
4149
                                            ponderation
4150
                                        FROM $tbl_stats_attempts AS at
4151
                                        INNER JOIN $tbl_quiz_questions AS q
4152
                                        ON q.iid = at.question_id
4153
                                        WHERE
4154
                                            exe_id = $id_last_attempt AND
4155
                                            at.c_id = $course_id
4156
                                    )
4157
                                    AS t";
4158
4159
                            $res_max_score_bis = Database::query($sql);
4160
                            $row_max_score_bis = Database::fetch_array($res_max_score_bis);
4161
4162
                            if (!empty($row_max_score_bis['maxscore'])) {
4163
                                $max_score = $row_max_score_bis['maxscore'];
4164
                            }
4165
                            if (!empty($max_score) && floatval($max_score) > 0) {
4166
                                $lpPartialTotal += $score / $max_score;
4167
                            }
4168
                            if ($debug) {
4169
                                var_dump("score: $score");
4170
                                var_dump("max_score: $max_score");
4171
                                var_dump("lpPartialTotal: $lpPartialTotal");
4172
                            }
4173
                        }
4174
                    }
4175
4176
                    if (in_array($row_max_score['item_type'], ['quiz', 'sco'])) {
4177
                        // Normal way
4178
                        if ($use_max_score[$lp_id]) {
4179
                            $count_items++;
4180
                        } else {
4181
                            if ($max_score != '') {
4182
                                $count_items++;
4183
                            }
4184
                        }
4185
                        if ($debug) {
4186
                            echo '$count_items: '.$count_items;
4187
                        }
4188
                    }
4189
                } // end for
4190
4191
                $score_of_scorm_calculate += $count_items ? (($lpPartialTotal / $count_items) * 100) : 0;
4192
                $global_result += $score_of_scorm_calculate;
4193
4194
                if ($debug) {
4195
                    var_dump("count_items: $count_items");
4196
                    var_dump("score_of_scorm_calculate: $score_of_scorm_calculate");
4197
                    var_dump("global_result: $global_result");
4198
                }
4199
            } // end while
4200
        }
4201
4202
        $lp_with_quiz = 0;
4203
        foreach ($lp_list as $lp_id) {
4204
            // Check if LP have a score we assume that all SCO have an score
4205
            $sql = "SELECT count(id) as count
4206
                    FROM $lp_item_table
4207
                    WHERE
4208
                        c_id = $course_id AND
4209
                        (item_type = 'quiz' OR item_type = 'sco') AND
4210
                        lp_id = ".$lp_id;
4211
            $result_have_quiz = Database::query($sql);
4212
            if (Database::num_rows($result_have_quiz) > 0) {
4213
                $row = Database::fetch_array($result_have_quiz, 'ASSOC');
4214
                if (is_numeric($row['count']) && $row['count'] != 0) {
4215
                    $lp_with_quiz++;
4216
                }
4217
            }
4218
        }
4219
4220
        if ($debug) {
4221
            echo '<h3>$lp_with_quiz '.$lp_with_quiz.' </h3>';
4222
        }
4223
        if ($debug) {
4224
            echo '<h3>Final return</h3>';
4225
        }
4226
4227
        if ($lp_with_quiz != 0) {
4228
            if (!$return_array) {
4229
                $score_of_scorm_calculate = round($global_result / $lp_with_quiz, 2);
4230
                if ($debug) {
4231
                    var_dump($score_of_scorm_calculate);
4232
                }
4233
                if (empty($lp_ids)) {
4234
                    if ($debug) {
4235
                        echo '<h2>All lps fix: '.$score_of_scorm_calculate.'</h2>';
4236
                    }
4237
                }
4238
4239
                return $score_of_scorm_calculate;
4240
            }
4241
4242
            if ($debug) {
4243
                var_dump($global_result, $lp_with_quiz);
4244
            }
4245
4246
            return [$global_result, $lp_with_quiz];
4247
        }
4248
4249
        return '-';
4250
    }
4251
4252
    /**
4253
     * This function gets:
4254
     * 1. The score average from all SCORM Test items in all LP in a course-> All the answers / All the max scores.
4255
     * 2. The score average from all Tests (quiz) in all LP in a course-> All the answers / All the max scores.
4256
     * 3. And finally it will return the average between 1. and 2.
4257
     * This function does not take the results of a Test out of a LP.
4258
     *
4259
     * @param int|array $student_id  Array of user ids or an user id
4260
     * @param string    $course_code Course code
4261
     * @param array     $lp_ids      List of LP ids
4262
     * @param int       $session_id  Session id (optional), if param $session_id is 0(default)
4263
     *                               it'll return results including sessions, 0 = session is not filtered
4264
     *
4265
     * @return string value (number %) Which represents a round integer explain in got in 3
4266
     */
4267
    public static function getAverageStudentScore(
4268
        $student_id,
4269
        $course_code = '',
4270
        $lp_ids = [],
4271
        $session_id = 0
4272
    ) {
4273
        if (empty($student_id)) {
4274
            return 0;
4275
        }
4276
4277
        $conditions = [];
4278
        if (!empty($course_code)) {
4279
            $course = api_get_course_info($course_code);
4280
            $courseId = $course['real_id'];
4281
            $conditions[] = " lp.c_id = $courseId";
4282
        }
4283
4284
        // Get course tables names
4285
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
4286
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
4287
        $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
4288
        $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
4289
4290
        // Compose a filter based on optional learning paths list given
4291
        if (!empty($lp_ids) && count($lp_ids) > 0) {
4292
            $conditions[] = ' lp.id IN ('.implode(',', $lp_ids).') ';
4293
        }
4294
4295
        // Compose a filter based on optional session id
4296
        $session_id = (int) $session_id;
4297
        if (!empty($session_id)) {
4298
            $conditions[] = " lp_view.session_id = $session_id ";
4299
        }
4300
4301
        if (is_array($student_id)) {
4302
            array_walk($student_id, 'intval');
4303
            $conditions[] = " lp_view.user_id IN (".implode(',', $student_id).") ";
4304
        } else {
4305
            $student_id = (int) $student_id;
4306
            $conditions[] = " lp_view.user_id = $student_id ";
4307
        }
4308
4309
        $conditionsToString = implode(' AND ', $conditions);
4310
        $sql = "SELECT
4311
                    SUM(lp_iv.score) sum_score,
4312
                    SUM(lp_i.max_score) sum_max_score
4313
                FROM $lp_table as lp
4314
                INNER JOIN $lp_item_table as lp_i
4315
                ON lp.iid = lp_id AND lp.c_id = lp_i.c_id
4316
                INNER JOIN $lp_view_table as lp_view
4317
                ON lp_view.lp_id = lp_i.lp_id AND lp_view.c_id = lp_i.c_id
4318
                INNER JOIN $lp_item_view_table as lp_iv
4319
                ON lp_i.iid = lp_iv.lp_item_id AND lp_view.c_id = lp_iv.c_id AND lp_iv.lp_view_id = lp_view.iid
4320
                WHERE (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."') AND
4321
                $conditionsToString
4322
        ";
4323
        $result = Database::query($sql);
4324
        $row = Database::fetch_array($result, 'ASSOC');
4325
4326
        if (empty($row['sum_max_score'])) {
4327
            return 0;
4328
        }
4329
4330
        return ($row['sum_score'] / $row['sum_max_score']) * 100;
4331
    }
4332
4333
    /**
4334
     * This function gets time spent in learning path for a student inside a course.
4335
     *
4336
     * @param int|array $student_id  Student id(s)
4337
     * @param string    $course_code Course code
4338
     * @param array     $lp_ids      Limit average to listed lp ids
4339
     * @param int       $session_id  Session id (optional), if param $session_id is null(default)
4340
     *                               it'll return results including sessions, 0 = session is not filtered
4341
     *
4342
     * @return int Total time in seconds
4343
     */
4344
    public static function get_time_spent_in_lp(
4345
        $student_id,
4346
        $course_code,
4347
        $lp_ids = [],
4348
        $session_id = 0
4349
    ) {
4350
        $course = api_get_course_info($course_code);
4351
        $student_id = (int) $student_id;
4352
        $session_id = (int) $session_id;
4353
        $total_time = 0;
4354
4355
        if (!empty($course)) {
4356
            $lpTable = Database::get_course_table(TABLE_LP_MAIN);
4357
            $lpItemTable = Database::get_course_table(TABLE_LP_ITEM);
4358
            $lpViewTable = Database::get_course_table(TABLE_LP_VIEW);
4359
            $lpItemViewTable = Database::get_course_table(TABLE_LP_ITEM_VIEW);
4360
            $trackExercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
4361
            $course_id = $course['real_id'];
4362
4363
            // Compose a filter based on optional learning paths list given
4364
            $condition_lp = '';
4365
            if (count($lp_ids) > 0) {
4366
                $condition_lp = " AND id IN(".implode(',', $lp_ids).") ";
4367
            }
4368
4369
            // Check the real number of LPs corresponding to the filter in the
4370
            // database (and if no list was given, get them all)
4371
            $sql = "SELECT DISTINCT(id) FROM $lpTable
4372
                    WHERE c_id = $course_id $condition_lp";
4373
            $result = Database::query($sql);
4374
            $session_condition = api_get_session_condition($session_id);
4375
4376
            // calculates time
4377
            if (Database::num_rows($result) > 0) {
4378
                while ($row = Database::fetch_array($result)) {
4379
                    $lp_id = (int) $row['id'];
4380
4381
                    // Start Exercise in LP total_time
4382
                    // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
4383
                    $list = learnpath::get_flat_ordered_items_list($lp_id, 0, $course_id);
4384
                    foreach ($list as $itemId) {
4385
                        $sql = "SELECT max(view_count)
4386
                                FROM $lpViewTable
4387
                                WHERE
4388
                                    c_id = $course_id AND
4389
                                    lp_id = $lp_id AND
4390
                                    user_id = $student_id
4391
                                    $session_condition";
4392
                        $res = Database::query($sql);
4393
                        $view = '';
4394
                        if (Database::num_rows($res) > 0) {
4395
                            $myrow = Database::fetch_array($res);
4396
                            $view = $myrow[0];
4397
                        }
4398
                        $viewCondition = null;
4399
                        if (!empty($view)) {
4400
                            $viewCondition = " AND v.view_count = $view  ";
4401
                        }
4402
                        $sql = "SELECT
4403
                            iv.iid,
4404
                            iv.total_time as mytime,
4405
                            i.id as myid,
4406
                            iv.view_count as iv_view_count,
4407
                            path
4408
                        FROM $lpItemTable as i
4409
                        INNER JOIN $lpItemViewTable as iv
4410
                        ON (i.id = iv.lp_item_id AND i.c_id = iv.c_id)
4411
                        INNER JOIN $lpViewTable as v
4412
                        ON (iv.lp_view_id = v.id AND v.c_id = iv.c_id)
4413
                        WHERE
4414
                            v.c_id = $course_id AND
4415
                            i.id = $itemId AND
4416
                            i.lp_id = $lp_id  AND
4417
                            v.user_id = $student_id AND
4418
                            item_type = 'quiz' AND
4419
                            path <> '' AND
4420
                            v.session_id = $session_id
4421
                            $viewCondition
4422
                        ORDER BY iv.view_count DESC ";
4423
4424
                        $resultRow = Database::query($sql);
4425
                        if (Database::num_rows($resultRow)) {
4426
                            $row = Database::fetch_array($resultRow);
4427
                            $totalTimeInLpItemView = $row['mytime'];
4428
                            $lpItemViewId = $row['iid'];
4429
4430
                            $sql = 'SELECT SUM(exe_duration) exe_duration
4431
                                    FROM '.$trackExercises.'
4432
                                    WHERE
4433
                                        exe_exo_id="'.$row['path'].'" AND
4434
                                        exe_user_id="'.$student_id.'" AND
4435
                                        orig_lp_id = "'.$lp_id.'" AND
4436
                                        orig_lp_item_id = "'.$row['myid'].'" AND
4437
                                        c_id = '.$course_id.' AND
4438
                                        status <> "incomplete" AND
4439
                                        session_id = '.$session_id.'
4440
                                     ORDER BY exe_date DESC ';
4441
4442
                            $sumScoreResult = Database::query($sql);
4443
                            $durationRow = Database::fetch_array($sumScoreResult, 'ASSOC');
4444
                            if (!empty($durationRow['exe_duration'])) {
4445
                                $exeDuration = $durationRow['exe_duration'];
4446
                                if ($exeDuration != $totalTimeInLpItemView
4447
                                    && !empty($lpItemViewId)
4448
                                    && !empty($exeDuration)
4449
                                ) {
4450
                                    // Update c_lp_item_view.total_time
4451
                                    $sqlUpdate = "UPDATE $lpItemViewTable SET total_time = '$exeDuration'
4452
                                                  WHERE iid = ".$lpItemViewId;
4453
                                    Database::query($sqlUpdate);
4454
                                }
4455
                            }
4456
                        }
4457
                    }
4458
4459
                    // End total_time fix
4460
4461
                    // Calculate total time
4462
                    $sql = "SELECT SUM(total_time)
4463
                            FROM $lpItemViewTable AS item_view
4464
                            INNER JOIN $lpViewTable AS view
4465
                            ON (
4466
                                item_view.lp_view_id = view.id AND
4467
                                item_view.c_id = view.c_id
4468
                            )
4469
                            WHERE
4470
                                item_view.c_id = $course_id AND
4471
                                view.c_id = $course_id AND
4472
                                view.lp_id = $lp_id AND
4473
                                view.user_id = $student_id AND
4474
                                session_id = $session_id";
4475
4476
                    $rs = Database::query($sql);
4477
                    if (Database::num_rows($rs) > 0) {
4478
                        $total_time += Database::result($rs, 0, 0);
4479
                    }
4480
                }
4481
            }
4482
        }
4483
4484
        return $total_time;
4485
    }
4486
4487
    /**
4488
     * This function gets last connection time to one learning path.
4489
     *
4490
     * @param int|array $student_id  Student id(s)
4491
     * @param string    $course_code Course code
4492
     * @param int       $lp_id       Learning path id
4493
     * @param int       $session_id
4494
     *
4495
     * @return int last connection timestamp
4496
     */
4497
    public static function get_last_connection_time_in_lp(
4498
        $student_id,
4499
        $course_code,
4500
        $lp_id,
4501
        $session_id = 0
4502
    ) {
4503
        $course = api_get_course_info($course_code);
4504
4505
        if (empty($course)) {
4506
            return 0;
4507
        }
4508
4509
        $course_id = $course['real_id'];
4510
        $student_id = (int) $student_id;
4511
        $lp_id = (int) $lp_id;
4512
        $session_id = (int) $session_id;
4513
        $lastTime = 0;
4514
4515
        // Use new system
4516
        if (self::minimumTimeAvailable($session_id, $course_id)) {
4517
            $sql = "SELECT MAX(date_reg) max
4518
                    FROM track_e_access_complete
4519
                    WHERE
4520
                        user_id = $student_id AND
4521
                        c_id = $course_id AND
4522
                        session_id = $session_id AND
4523
                        tool = 'learnpath' AND
4524
                        tool_id = $lp_id AND
4525
                        action = 'view' AND
4526
                        login_as = 0
4527
                    ORDER BY date_reg ASC
4528
                    LIMIT 1";
4529
            $rs = Database::query($sql);
4530
4531
            $lastConnection = 0;
4532
            if (Database::num_rows($rs) > 0) {
4533
                $value = Database::fetch_array($rs);
4534
                if (isset($value['max']) && !empty($value['max'])) {
4535
                    $lastConnection = api_strtotime($value['max'], 'UTC');
4536
                }
4537
            }
4538
4539
            if (!empty($lastConnection)) {
4540
                return $lastConnection;
4541
            }
4542
        }
4543
4544
        if (!empty($course)) {
4545
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
4546
            $t_lpv = Database::get_course_table(TABLE_LP_VIEW);
4547
            $t_lpiv = Database::get_course_table(TABLE_LP_ITEM_VIEW);
4548
4549
            // Check the real number of LPs corresponding to the filter in the
4550
            // database (and if no list was given, get them all)
4551
            $sql = "SELECT id FROM $lp_table
4552
                    WHERE c_id = $course_id AND id = $lp_id ";
4553
            $row = Database::query($sql);
4554
            $count = Database::num_rows($row);
4555
4556
            // calculates last connection time
4557
            if ($count > 0) {
4558
                $sql = 'SELECT MAX(start_time)
4559
                        FROM '.$t_lpiv.' AS item_view
4560
                        INNER JOIN '.$t_lpv.' AS view
4561
                        ON (item_view.lp_view_id = view.id AND item_view.c_id = view.c_id)
4562
                        WHERE
4563
                            item_view.c_id = '.$course_id.' AND
4564
                            view.c_id = '.$course_id.' AND
4565
                            view.lp_id = '.$lp_id.' AND
4566
                            view.user_id = '.$student_id.' AND
4567
                            view.session_id = '.$session_id;
4568
                $rs = Database::query($sql);
4569
                if (Database::num_rows($rs) > 0) {
4570
                    $lastTime = Database::result($rs, 0, 0);
4571
                }
4572
            }
4573
        }
4574
4575
        return $lastTime;
4576
    }
4577
4578
    /**
4579
     * Gets the last connection time in the last learning path for a student in a course session.
4580
     */
4581
    public static function getLastConnectionTimeInSessionCourseLp(
4582
        int $studentId,
4583
        string $courseCode,
4584
        int $sessionId = 0
4585
    ): int {
4586
        $course = api_get_course_info($courseCode);
4587
4588
        if (empty($course)) {
4589
            return 0;
4590
        }
4591
4592
        $courseId = $course['real_id'];
4593
        $lastTime = 0;
4594
4595
        $tLpv = Database::get_course_table(TABLE_LP_VIEW);
4596
        $tLpiv = Database::get_course_table(TABLE_LP_ITEM_VIEW);
4597
4598
        $sql = 'SELECT MAX(item_view.start_time) as last_time
4599
            FROM '.$tLpiv.' AS item_view
4600
            INNER JOIN '.$tLpv.' AS view
4601
            ON (item_view.lp_view_id = view.id)
4602
            WHERE
4603
                view.c_id = '.$courseId.' AND
4604
                view.user_id = '.$studentId.' AND
4605
                view.session_id = '.$sessionId;
4606
4607
        $rs = Database::query($sql);
4608
        if ($rs && Database::num_rows($rs) > 0) {
4609
            $lastTime = (int) Database::result($rs, 0, 'last_time');
4610
        }
4611
4612
        return $lastTime;
4613
    }
4614
4615
    public static function getFirstConnectionTimeInLp(
4616
        $student_id,
4617
        $course_code,
4618
        $lp_id,
4619
        $session_id = 0
4620
    ) {
4621
        $course = api_get_course_info($course_code);
4622
        $student_id = (int) $student_id;
4623
        $lp_id = (int) $lp_id;
4624
        $session_id = (int) $session_id;
4625
        $time = 0;
4626
4627
        if (!empty($course)) {
4628
            $course_id = $course['real_id'];
4629
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
4630
            $t_lpv = Database::get_course_table(TABLE_LP_VIEW);
4631
            $t_lpiv = Database::get_course_table(TABLE_LP_ITEM_VIEW);
4632
4633
            // Check the real number of LPs corresponding to the filter in the
4634
            // database (and if no list was given, get them all)
4635
            $sql = "SELECT id FROM $lp_table
4636
                    WHERE c_id = $course_id AND id = $lp_id ";
4637
            $row = Database::query($sql);
4638
            $count = Database::num_rows($row);
4639
4640
            // calculates first connection time
4641
            if ($count > 0) {
4642
                $sql = 'SELECT MIN(start_time)
4643
                        FROM '.$t_lpiv.' AS item_view
4644
                        INNER JOIN '.$t_lpv.' AS view
4645
                        ON (item_view.lp_view_id = view.id AND item_view.c_id = view.c_id)
4646
                        WHERE
4647
                            status != "not attempted" AND
4648
                            item_view.c_id = '.$course_id.' AND
4649
                            view.c_id = '.$course_id.' AND
4650
                            view.lp_id = '.$lp_id.' AND
4651
                            view.user_id = '.$student_id.' AND
4652
                            view.session_id = '.$session_id;
4653
                $rs = Database::query($sql);
4654
                if (Database::num_rows($rs) > 0) {
4655
                    $time = Database::result($rs, 0, 0);
4656
                }
4657
            }
4658
        }
4659
4660
        return $time;
4661
    }
4662
4663
    /**
4664
     * gets the list of students followed by coach.
4665
     *
4666
     * @param int $coach_id Coach id
4667
     *
4668
     * @return array List of students
4669
     */
4670
    public static function get_student_followed_by_coach($coach_id)
4671
    {
4672
        $coach_id = intval($coach_id);
4673
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4674
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4675
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
4676
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4677
4678
        $students = [];
4679
        // At first, courses where $coach_id is coach of the course //
4680
        $sql = 'SELECT session_id, c_id
4681
                FROM '.$tbl_session_course_user.'
4682
                WHERE user_id='.$coach_id.' AND status=2';
4683
4684
        if (api_is_multiple_url_enabled()) {
4685
            $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4686
            $access_url_id = api_get_current_access_url_id();
4687
            if (-1 != $access_url_id) {
4688
                $sql = 'SELECT scu.session_id, scu.c_id
4689
                        FROM '.$tbl_session_course_user.' scu
4690
                        INNER JOIN '.$tbl_session_rel_access_url.'  sru
4691
                        ON (scu.session_id=sru.session_id)
4692
                        WHERE
4693
                            scu.user_id='.$coach_id.' AND
4694
                            scu.status=2 AND
4695
                            sru.access_url_id = '.$access_url_id;
4696
            }
4697
        }
4698
4699
        $result = Database::query($sql);
4700
4701
        while ($a_courses = Database::fetch_array($result)) {
4702
            $courseId = $a_courses['c_id'];
4703
            $id_session = $a_courses['session_id'];
4704
4705
            $sql = "SELECT DISTINCT srcru.user_id
4706
                    FROM $tbl_session_course_user AS srcru
4707
                    INNER JOIN $tbl_session_user sru
4708
                    ON (srcru.user_id = sru.user_id AND srcru.session_id = sru.session_id)
4709
                    WHERE
4710
                        sru.relation_type <> ".SESSION_RELATION_TYPE_RRHH." AND
4711
                        srcru.c_id = '$courseId' AND
4712
                        srcru.session_id = '$id_session'";
4713
4714
            $rs = Database::query($sql);
4715
            while ($row = Database::fetch_array($rs)) {
4716
                $students[$row['user_id']] = $row['user_id'];
4717
            }
4718
        }
4719
4720
        // Then, courses where $coach_id is coach of the session
4721
        $sql = 'SELECT session_course_user.user_id
4722
                FROM '.$tbl_session_course_user.' as session_course_user
4723
                INNER JOIN '.$tbl_session_user.' sru
4724
                ON session_course_user.user_id = sru.user_id AND session_course_user.session_id = sru.session_id
4725
                INNER JOIN '.$tbl_session_course.' as session_course
4726
                ON session_course.c_id = session_course_user.c_id
4727
                AND session_course_user.session_id = session_course.session_id
4728
                INNER JOIN '.$tbl_session.' as session
4729
                ON session.id = session_course.session_id
4730
                AND session.id_coach = '.$coach_id;
4731
        if (api_is_multiple_url_enabled()) {
4732
            $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4733
            $access_url_id = api_get_current_access_url_id();
4734
            if (-1 != $access_url_id) {
4735
                $sql = 'SELECT session_course_user.user_id
4736
                        FROM '.$tbl_session_course_user.' as session_course_user
4737
                        INNER JOIN '.$tbl_session_user.' sru
4738
                        ON session_course_user.user_id = sru.user_id AND
4739
                           session_course_user.session_id = sru.session_id
4740
                        INNER JOIN '.$tbl_session_course.' as session_course
4741
                        ON session_course.c_id = session_course_user.c_id AND
4742
                        session_course_user.session_id = session_course.session_id
4743
                        INNER JOIN '.$tbl_session.' as session
4744
                        ON session.id = session_course.session_id AND
4745
                        session.id_coach = '.$coach_id.'
4746
                        INNER JOIN '.$tbl_session_rel_access_url.' session_rel_url
4747
                        ON session.id = session_rel_url.session_id
4748
                        WHERE access_url_id = '.$access_url_id;
4749
            }
4750
        }
4751
4752
        $result = Database::query($sql);
4753
        while ($row = Database::fetch_array($result)) {
4754
            $students[$row['user_id']] = $row['user_id'];
4755
        }
4756
4757
        return $students;
4758
    }
4759
4760
    /**
4761
     * Check if a coach is allowed to follow a student.
4762
     *
4763
     * @param    int        Coach id
4764
     * @param    int        Student id
4765
     *
4766
     * @return bool
4767
     */
4768
    public static function is_allowed_to_coach_student($coach_id, $student_id)
4769
    {
4770
        $coach_id = intval($coach_id);
4771
        $student_id = intval($student_id);
4772
4773
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4774
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4775
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4776
4777
        // At first, courses where $coach_id is coach of the course
4778
        $sql = 'SELECT 1 FROM '.$tbl_session_course_user.'
4779
                WHERE user_id='.$coach_id.' AND status=2';
4780
        $result = Database::query($sql);
4781
        if (Database::num_rows($result) > 0) {
4782
            return true;
4783
        }
4784
4785
        // Then, courses where $coach_id is coach of the session
4786
        $sql = 'SELECT session_course_user.user_id
4787
                FROM '.$tbl_session_course_user.' as session_course_user
4788
                INNER JOIN '.$tbl_session_course.' as session_course
4789
                ON session_course.c_id = session_course_user.c_id
4790
                INNER JOIN '.$tbl_session.' as session
4791
                ON session.id = session_course.session_id
4792
                AND session.id_coach = '.$coach_id.'
4793
                WHERE user_id = '.$student_id;
4794
        $result = Database::query($sql);
4795
        if (Database::num_rows($result) > 0) {
4796
            return true;
4797
        }
4798
4799
        return false;
4800
    }
4801
4802
    /**
4803
     * Get courses followed by coach.
4804
     *
4805
     * @param     int        Coach id
4806
     * @param    int        Session id (optional)
4807
     *
4808
     * @return array Courses list
4809
     */
4810
    public static function get_courses_followed_by_coach($coach_id, $id_session = 0)
4811
    {
4812
        $coach_id = intval($coach_id);
4813
        if (!empty($id_session)) {
4814
            $id_session = intval($id_session);
4815
        }
4816
4817
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4818
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4819
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4820
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
4821
        $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
4822
4823
        // At first, courses where $coach_id is coach of the course.
4824
        $sql = 'SELECT DISTINCT c.code
4825
                FROM '.$tbl_session_course_user.' sc
4826
                INNER JOIN '.$tbl_course.' c
4827
                ON (c.id = sc.c_id)
4828
                WHERE user_id = '.$coach_id.' AND status = 2';
4829
4830
        if (api_is_multiple_url_enabled()) {
4831
            $access_url_id = api_get_current_access_url_id();
4832
            if (-1 != $access_url_id) {
4833
                $sql = 'SELECT DISTINCT c.code
4834
                        FROM '.$tbl_session_course_user.' scu
4835
                        INNER JOIN '.$tbl_course.' c
4836
                        ON (c.code = scu.c_id)
4837
                        INNER JOIN '.$tbl_course_rel_access_url.' cru
4838
                        ON (c.id = cru.c_id)
4839
                        WHERE
4840
                            scu.user_id='.$coach_id.' AND
4841
                            scu.status=2 AND
4842
                            cru.access_url_id = '.$access_url_id;
4843
            }
4844
        }
4845
4846
        if (!empty($id_session)) {
4847
            $sql .= ' AND session_id='.$id_session;
4848
        }
4849
4850
        $courseList = [];
4851
        $result = Database::query($sql);
4852
        while ($row = Database::fetch_array($result)) {
4853
            $courseList[$row['code']] = $row['code'];
4854
        }
4855
4856
        // Then, courses where $coach_id is coach of the session
4857
        $sql = 'SELECT DISTINCT course.code
4858
                FROM '.$tbl_session_course.' as session_course
4859
                INNER JOIN '.$tbl_session.' as session
4860
                    ON session.id = session_course.session_id
4861
                    AND session.id_coach = '.$coach_id.'
4862
                INNER JOIN '.$tbl_course.' as course
4863
                    ON course.id = session_course.c_id';
4864
4865
        if (api_is_multiple_url_enabled()) {
4866
            $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
4867
            $access_url_id = api_get_current_access_url_id();
4868
            if (-1 != $access_url_id) {
4869
                $sql = 'SELECT DISTINCT c.code
4870
                    FROM '.$tbl_session_course.' as session_course
4871
                    INNER JOIN '.$tbl_course.' c
4872
                    ON (c.id = session_course.c_id)
4873
                    INNER JOIN '.$tbl_session.' as session
4874
                    ON session.id = session_course.session_id
4875
                        AND session.id_coach = '.$coach_id.'
4876
                    INNER JOIN '.$tbl_course.' as course
4877
                        ON course.id = session_course.c_id
4878
                     INNER JOIN '.$tbl_course_rel_access_url.' course_rel_url
4879
                    ON (course_rel_url.c_id = c.id)';
4880
            }
4881
        }
4882
4883
        if (!empty($id_session)) {
4884
            $sql .= ' WHERE session_course.session_id='.$id_session;
4885
            if (api_is_multiple_url_enabled()) {
4886
                $sql .= ' AND access_url_id = '.$access_url_id;
4887
            }
4888
        } else {
4889
            if (api_is_multiple_url_enabled()) {
4890
                $sql .= ' WHERE access_url_id = '.$access_url_id;
4891
            }
4892
        }
4893
4894
        $result = Database::query($sql);
4895
        while ($row = Database::fetch_array($result)) {
4896
            $courseList[$row['code']] = $row['code'];
4897
        }
4898
4899
        return $courseList;
4900
    }
4901
4902
    /**
4903
     * Get sessions coached by user.
4904
     *
4905
     * @param int    $coach_id
4906
     * @param int    $start
4907
     * @param int    $limit
4908
     * @param bool   $getCount
4909
     * @param string $keyword
4910
     * @param string $description
4911
     * @param string $orderByName
4912
     * @param string $orderByDirection
4913
     * @param array  $options
4914
     */
4915
    public static function get_sessions_coached_by_user(
4916
        $coach_id,
4917
        $start = 0,
4918
        $limit = 0,
4919
        $getCount = false,
4920
        $keyword = '',
4921
        $description = '',
4922
        $orderByName = '',
4923
        $orderByDirection = '',
4924
        $options = []
4925
    ) {
4926
        // table definition
4927
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4928
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4929
        $coach_id = (int) $coach_id;
4930
4931
        $select = ' SELECT * FROM ';
4932
        if ($getCount) {
4933
            $select = ' SELECT count(DISTINCT id) as count FROM ';
4934
        }
4935
4936
        $limitCondition = null;
4937
        if (!empty($start) && !empty($limit)) {
4938
            $limitCondition = " LIMIT ".intval($start).", ".intval($limit);
4939
        }
4940
4941
        $keywordCondition = null;
4942
        if (!empty($keyword)) {
4943
            $keyword = Database::escape_string($keyword);
4944
            $keywordCondition = " AND (name LIKE '%$keyword%' ) ";
4945
4946
            if (!empty($description)) {
4947
                $description = Database::escape_string($description);
4948
                $keywordCondition = " AND (name LIKE '%$keyword%' OR description LIKE '%$description%' ) ";
4949
            }
4950
        }
4951
4952
        $extraFieldModel = new ExtraFieldModel('session');
4953
        $conditions = $extraFieldModel->parseConditions($options);
4954
        $sqlInjectJoins = $conditions['inject_joins'];
4955
        $extraFieldsConditions = $conditions['where'];
4956
        $sqlInjectWhere = $conditions['inject_where'];
4957
        $injectExtraFields = $conditions['inject_extra_fields'];
4958
4959
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4960
        $access_url_id = api_get_current_access_url_id();
4961
4962
        $orderBy = '';
4963
        if (!empty($orderByName)) {
4964
            if (in_array($orderByName, ['name', 'access_start_date'])) {
4965
                $orderByDirection = in_array(strtolower($orderByDirection), ['asc', 'desc']) ? $orderByDirection : 'asc';
4966
                $orderByName = Database::escape_string($orderByName);
4967
                $orderBy .= " ORDER BY `$orderByName` $orderByDirection";
4968
            }
4969
        }
4970
4971
        $sql = "
4972
            $select
4973
            (
4974
                SELECT DISTINCT
4975
                    s.id,
4976
                    name,
4977
                    $injectExtraFields
4978
                    access_start_date,
4979
                    access_end_date
4980
                FROM $tbl_session s
4981
                INNER JOIN $tbl_session_rel_access_url session_rel_url
4982
                ON (s.id = session_rel_url.session_id)
4983
                $sqlInjectJoins
4984
                WHERE
4985
                    id_coach = $coach_id AND
4986
                    access_url_id = $access_url_id
4987
                    $keywordCondition
4988
                    $extraFieldsConditions
4989
                    $sqlInjectWhere
4990
            UNION
4991
                SELECT DISTINCT
4992
                    s.id,
4993
                    s.name,
4994
                    $injectExtraFields
4995
                    s.access_start_date,
4996
                    s.access_end_date
4997
                FROM $tbl_session as s
4998
                INNER JOIN $tbl_session_course_user as session_course_user
4999
                ON
5000
                    s.id = session_course_user.session_id AND
5001
                    session_course_user.user_id = $coach_id AND
5002
                    session_course_user.status = 2
5003
                INNER JOIN $tbl_session_rel_access_url session_rel_url
5004
                ON (s.id = session_rel_url.session_id)
5005
                $sqlInjectJoins
5006
                WHERE
5007
                    access_url_id = $access_url_id
5008
                    $keywordCondition
5009
                    $extraFieldsConditions
5010
                    $sqlInjectWhere
5011
            ) as sessions $limitCondition $orderBy
5012
            ";
5013
5014
        $rs = Database::query($sql);
5015
        if ($getCount) {
5016
            $row = Database::fetch_array($rs);
5017
5018
            return $row['count'];
5019
        }
5020
5021
        $sessions = [];
5022
        while ($row = Database::fetch_array($rs)) {
5023
            if ($row['access_start_date'] === '0000-00-00 00:00:00') {
5024
                $row['access_start_date'] = null;
5025
            }
5026
5027
            $sessions[$row['id']] = $row;
5028
        }
5029
5030
        if (!empty($sessions)) {
5031
            foreach ($sessions as &$session) {
5032
                if (empty($session['access_start_date'])) {
5033
                    $session['status'] = get_lang('SessionActive');
5034
                } else {
5035
                    $time_start = api_strtotime($session['access_start_date'], 'UTC');
5036
                    $time_end = api_strtotime($session['access_end_date'], 'UTC');
5037
                    if ($time_start < time() && time() < $time_end) {
5038
                        $session['status'] = get_lang('SessionActive');
5039
                    } else {
5040
                        if (time() < $time_start) {
5041
                            $session['status'] = get_lang('SessionFuture');
5042
                        } else {
5043
                            if (time() > $time_end) {
5044
                                $session['status'] = get_lang('SessionPast');
5045
                            }
5046
                        }
5047
                    }
5048
                }
5049
            }
5050
        }
5051
5052
        return $sessions;
5053
    }
5054
5055
    /**
5056
     * Get courses list from a session.
5057
     *
5058
     * @param    int        Session id
5059
     *
5060
     * @return array Courses list
5061
     */
5062
    public static function get_courses_list_from_session($session_id)
5063
    {
5064
        $session_id = (int) $session_id;
5065
5066
        // table definition
5067
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
5068
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
5069
5070
        $sql = "SELECT DISTINCT code, c_id
5071
                FROM $tbl_session_course sc
5072
                INNER JOIN $courseTable c
5073
                ON sc.c_id = c.id
5074
                WHERE session_id= $session_id";
5075
5076
        $result = Database::query($sql);
5077
5078
        $courses = [];
5079
        while ($row = Database::fetch_array($result)) {
5080
            $courses[$row['code']] = $row;
5081
        }
5082
5083
        return $courses;
5084
    }
5085
5086
    /**
5087
     * Count the number of documents that an user has uploaded to a course.
5088
     *
5089
     * @param    int|array   Student id(s)
5090
     * @param    string      Course code
5091
     * @param    int         Session id (optional),
5092
     * if param $session_id is null(default)
5093
     * return count of assignments including sessions, 0 = session is not filtered
5094
     *
5095
     * @return int Number of documents
5096
     */
5097
    public static function count_student_uploaded_documents(
5098
        $student_id,
5099
        $course_code,
5100
        $session_id = null
5101
    ) {
5102
        // get the information of the course
5103
        $a_course = api_get_course_info($course_code);
5104
        if (!empty($a_course)) {
5105
            // table definition
5106
            $tbl_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
5107
            $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
5108
            $course_id = $a_course['real_id'];
5109
            if (is_array($student_id)) {
5110
                $studentList = array_map('intval', $student_id);
5111
                $condition_user = " AND ip.insert_user_id IN ('".implode(',', $studentList)."') ";
5112
            } else {
5113
                $student_id = (int) $student_id;
5114
                $condition_user = " AND ip.insert_user_id = '$student_id' ";
5115
            }
5116
5117
            $condition_session = null;
5118
            if (isset($session_id)) {
5119
                $session_id = (int) $session_id;
5120
                $condition_session = " AND pub.session_id = $session_id ";
5121
            }
5122
5123
            $sql = "SELECT count(ip.tool) AS count
5124
                    FROM $tbl_item_property ip
5125
                    INNER JOIN $tbl_document pub
5126
                    ON (ip.ref = pub.iid AND ip.c_id = pub.c_id)
5127
                    WHERE
5128
                        ip.c_id  = $course_id AND
5129
                        pub.c_id  = $course_id AND
5130
                        pub.filetype ='file' AND
5131
                        ip.tool = 'document'
5132
                        $condition_user $condition_session ";
5133
            $rs = Database::query($sql);
5134
            $row = Database::fetch_array($rs, 'ASSOC');
5135
5136
            return $row['count'];
5137
        }
5138
5139
        return null;
5140
    }
5141
5142
    /**
5143
     * Count assignments per student.
5144
     *
5145
     * @param array|int $student_id
5146
     * @param string    $course_code
5147
     * @param int       $session_id  if param is null(default) return count of assignments including sessions,
5148
     *                               0 = session is not filtered
5149
     *
5150
     * @return int Count of assignments
5151
     */
5152
    public static function count_student_assignments(
5153
        $student_id,
5154
        $course_code = null,
5155
        $session_id = null
5156
    ) {
5157
        if (empty($student_id)) {
5158
            return 0;
5159
        }
5160
5161
        $conditions = [];
5162
5163
        // Get the information of the course
5164
        $a_course = api_get_course_info($course_code);
5165
        if (!empty($a_course)) {
5166
            $course_id = $a_course['real_id'];
5167
            $conditions[] = " ip.c_id  = $course_id AND pub.c_id  = $course_id ";
5168
        }
5169
5170
        // table definition
5171
        $tbl_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
5172
        $tbl_student_publication = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
5173
5174
        if (is_array($student_id)) {
5175
            $studentList = array_map('intval', $student_id);
5176
            $conditions[] = " ip.insert_user_id IN ('".implode("','", $studentList)."') ";
5177
        } else {
5178
            $student_id = (int) $student_id;
5179
            $conditions[] = " ip.insert_user_id = '$student_id' ";
5180
        }
5181
5182
        $conditions[] = ' pub.active <> 2 ';
5183
        $conditionToString = implode(' AND ', $conditions);
5184
        $sessionCondition = api_get_session_condition($session_id, true, false, 'pub.session_id');
5185
        $conditionToString .= $sessionCondition;
5186
5187
        $sql = "SELECT count(ip.tool) as count
5188
                FROM $tbl_item_property ip
5189
                INNER JOIN $tbl_student_publication pub
5190
                ON (ip.ref = pub.iid AND ip.c_id = pub.c_id)
5191
                WHERE
5192
                    ip.tool='work' AND
5193
                    $conditionToString";
5194
        $rs = Database::query($sql);
5195
        $row = Database::fetch_array($rs, 'ASSOC');
5196
5197
        return $row['count'];
5198
    }
5199
5200
    /**
5201
     * Count messages per student inside forum tool.
5202
     *
5203
     * @param int|array  Student id
5204
     * @param string     Course code
5205
     * @param int        Session id if null(default) return count of messages including sessions, 0 = session is not
5206
     *                           filtered
5207
     *
5208
     * @return int Count of messages
5209
     */
5210
    public static function count_student_messages($student_id, $courseCode = null, $session_id = null)
5211
    {
5212
        if (empty($student_id)) {
5213
            return 0;
5214
        }
5215
5216
        // Table definition.
5217
        $tbl_forum_post = Database::get_course_table(TABLE_FORUM_POST);
5218
        $tbl_forum = Database::get_course_table(TABLE_FORUM);
5219
5220
        $conditions = [];
5221
        if (is_array($student_id)) {
5222
            $studentList = array_map('intval', $student_id);
5223
            $conditions[] = " post.poster_id IN ('".implode("','", $studentList)."') ";
5224
        } else {
5225
            $student_id = (int) $student_id;
5226
            $conditions[] = " post.poster_id = '$student_id' ";
5227
        }
5228
5229
        $conditionsToString = implode('AND ', $conditions);
5230
5231
        if (empty($courseCode)) {
5232
            $sql = "SELECT count(poster_id) as count
5233
                    FROM $tbl_forum_post post
5234
                    INNER JOIN $tbl_forum forum
5235
                    ON (forum.forum_id = post.forum_id AND forum.c_id = post.c_id)
5236
                    WHERE $conditionsToString";
5237
5238
            $rs = Database::query($sql);
5239
            $row = Database::fetch_array($rs, 'ASSOC');
5240
5241
            return $row['count'];
5242
        }
5243
5244
        require_once api_get_path(SYS_CODE_PATH).'forum/forumfunction.inc.php';
5245
5246
        $courseInfo = api_get_course_info($courseCode);
5247
5248
        $forums = [];
5249
        if (!empty($courseInfo)) {
5250
            $forums = get_forums('', $courseCode, true, $session_id);
5251
            $course_id = $courseInfo['real_id'];
5252
            $conditions[] = " post.c_id  = $course_id ";
5253
        }
5254
5255
        if (!empty($forums)) {
5256
            $idList = array_column($forums, 'forum_id');
5257
            $idListToString = implode("', '", $idList);
5258
            $conditions[] = " post.forum_id  IN ('$idListToString')";
5259
        }
5260
5261
        $conditionsToString = implode('AND ', $conditions);
5262
        $sql = "SELECT count(poster_id) as count
5263
                FROM $tbl_forum_post post
5264
                WHERE $conditionsToString";
5265
5266
        $rs = Database::query($sql);
5267
        $row = Database::fetch_array($rs, 'ASSOC');
5268
        $count = $row['count'];
5269
5270
        return $count;
5271
    }
5272
5273
    /**
5274
     * This function counts the number of post by course.
5275
     *
5276
     * @param string $course_code
5277
     * @param int    $session_id  (optional), if is null(default) it'll return results including sessions,
5278
     *                            0 = session is not filtered
5279
     * @param int    $groupId
5280
     *
5281
     * @return int The number of post by course
5282
     */
5283
    public static function count_number_of_posts_by_course($course_code, $session_id = null, $groupId = 0)
5284
    {
5285
        $courseInfo = api_get_course_info($course_code);
5286
        if (!empty($courseInfo)) {
5287
            $tbl_posts = Database::get_course_table(TABLE_FORUM_POST);
5288
            $tbl_forums = Database::get_course_table(TABLE_FORUM);
5289
5290
            $condition_session = '';
5291
            if (isset($session_id)) {
5292
                $session_id = (int) $session_id;
5293
                $condition_session = api_get_session_condition(
5294
                    $session_id,
5295
                    true,
5296
                    false,
5297
                    'f.session_id'
5298
                );
5299
            }
5300
5301
            $course_id = $courseInfo['real_id'];
5302
            $groupId = (int) $groupId;
5303
            if (!empty($groupId)) {
5304
                $groupCondition = " i.to_group_id = $groupId ";
5305
            } else {
5306
                $groupCondition = ' (i.to_group_id = 0 OR i.to_group_id IS NULL) ';
5307
            }
5308
5309
            $item = Database::get_course_table(TABLE_ITEM_PROPERTY);
5310
            $sql = "SELECT count(*) FROM $tbl_posts p
5311
                    INNER JOIN $tbl_forums f
5312
                    ON f.forum_id = p.forum_id AND p.c_id = f.c_id
5313
                    INNER JOIN $item i
5314
                    ON (tool = '".TOOL_FORUM."' AND f.c_id = i.c_id AND f.iid = i.ref)
5315
                    WHERE
5316
                        p.c_id = $course_id AND
5317
                        f.c_id = $course_id AND
5318
                        $groupCondition
5319
                        $condition_session
5320
                    ";
5321
            $result = Database::query($sql);
5322
            $row = Database::fetch_row($result);
5323
            $count = $row[0];
5324
5325
            return $count;
5326
        }
5327
5328
        return 0;
5329
    }
5330
5331
    /**
5332
     * This function counts the number of threads by course.
5333
     *
5334
     * @param      string     Course code
5335
     * @param    int        Session id (optional),
5336
     * if param $session_id is null(default) it'll return results including
5337
     * sessions, 0 = session is not filtered
5338
     * @param int $groupId
5339
     *
5340
     * @return int The number of threads by course
5341
     */
5342
    public static function count_number_of_threads_by_course(
5343
        $course_code,
5344
        $session_id = null,
5345
        $groupId = 0
5346
    ) {
5347
        $course_info = api_get_course_info($course_code);
5348
        if (empty($course_info)) {
5349
            return null;
5350
        }
5351
5352
        $course_id = $course_info['real_id'];
5353
        $tbl_threads = Database::get_course_table(TABLE_FORUM_THREAD);
5354
        $tbl_forums = Database::get_course_table(TABLE_FORUM);
5355
5356
        $condition_session = '';
5357
        if (isset($session_id)) {
5358
            $session_id = (int) $session_id;
5359
            $condition_session = ' AND f.session_id = '.$session_id;
5360
        }
5361
5362
        $groupId = (int) $groupId;
5363
5364
        if (!empty($groupId)) {
5365
            $groupCondition = " i.to_group_id = $groupId ";
5366
        } else {
5367
            $groupCondition = " (i.to_group_id = 0 OR i.to_group_id IS NULL) ";
5368
        }
5369
5370
        $item = Database::get_course_table(TABLE_ITEM_PROPERTY);
5371
        $sql = "SELECT count(*)
5372
                FROM $tbl_threads t
5373
                INNER JOIN $tbl_forums f
5374
                ON f.iid = t.forum_id AND f.c_id = t.c_id
5375
                INNER JOIN $item i
5376
                ON (
5377
                    tool = '".TOOL_FORUM_THREAD."' AND
5378
                    f.c_id = i.c_id AND
5379
                    t.iid = i.ref
5380
                )
5381
                WHERE
5382
                    t.c_id = $course_id AND
5383
                    f.c_id = $course_id AND
5384
                    $groupCondition
5385
                    $condition_session
5386
                ";
5387
5388
        $result = Database::query($sql);
5389
        if (Database::num_rows($result)) {
5390
            $row = Database::fetch_row($result);
5391
            $count = $row[0];
5392
5393
            return $count;
5394
        }
5395
5396
        return 0;
5397
    }
5398
5399
    /**
5400
     * This function counts the number of forums by course.
5401
     *
5402
     * @param      string     Course code
5403
     * @param    int        Session id (optional),
5404
     * if param $session_id is null(default) it'll return results
5405
     * including sessions, 0 = session is not filtered
5406
     * @param int $groupId
5407
     *
5408
     * @return int The number of forums by course
5409
     */
5410
    public static function count_number_of_forums_by_course(
5411
        $course_code,
5412
        $session_id = null,
5413
        $groupId = 0
5414
    ) {
5415
        $course_info = api_get_course_info($course_code);
5416
        if (empty($course_info)) {
5417
            return null;
5418
        }
5419
        $course_id = $course_info['real_id'];
5420
5421
        $condition_session = '';
5422
        if (isset($session_id)) {
5423
            $session_id = (int) $session_id;
5424
            $condition_session = ' AND f.session_id = '.$session_id;
5425
        }
5426
5427
        $groupId = (int) $groupId;
5428
        if (!empty($groupId)) {
5429
            $groupCondition = " i.to_group_id = $groupId ";
5430
        } else {
5431
            $groupCondition = " (i.to_group_id = 0 OR i.to_group_id IS NULL) ";
5432
        }
5433
5434
        $tbl_forums = Database::get_course_table(TABLE_FORUM);
5435
        $item = Database::get_course_table(TABLE_ITEM_PROPERTY);
5436
5437
        $sql = "SELECT count(*)
5438
                FROM $tbl_forums f
5439
                INNER JOIN $item i
5440
                ON f.c_id = i.c_id AND f.iid = i.ref AND tool = '".TOOL_FORUM."'
5441
                WHERE
5442
                    f.c_id = $course_id AND
5443
                    $groupCondition
5444
                    $condition_session
5445
                ";
5446
        $result = Database::query($sql);
5447
        if (Database::num_rows($result)) {
5448
            $row = Database::fetch_row($result);
5449
            $count = $row[0];
5450
5451
            return $count;
5452
        }
5453
5454
        return 0;
5455
    }
5456
5457
    /**
5458
     * This function counts the chat last connections by course in x days.
5459
     *
5460
     * @param      string     Course code
5461
     * @param      int     Last x days
5462
     * @param    int        Session id (optional)
5463
     *
5464
     * @return int Chat last connections by course in x days
5465
     */
5466
    public static function chat_connections_during_last_x_days_by_course(
5467
        $course_code,
5468
        $last_days,
5469
        $session_id = 0
5470
    ) {
5471
        $course_info = api_get_course_info($course_code);
5472
        if (empty($course_info)) {
5473
            return null;
5474
        }
5475
        $course_id = $course_info['real_id'];
5476
5477
        // Protect data
5478
        $last_days = (int) $last_days;
5479
        $session_id = (int) $session_id;
5480
5481
        $tbl_stats_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
5482
        $now = api_get_utc_datetime();
5483
5484
        $sql = "SELECT count(*) FROM $tbl_stats_access
5485
                WHERE
5486
                    DATE_SUB('$now',INTERVAL $last_days DAY) <= access_date AND
5487
                    c_id = '$course_id' AND
5488
                    access_tool='".TOOL_CHAT."' AND
5489
                    access_session_id = '$session_id' ";
5490
        $result = Database::query($sql);
5491
        if (Database::num_rows($result)) {
5492
            $row = Database::fetch_row($result);
5493
            $count = $row[0];
5494
5495
            return $count;
5496
        }
5497
5498
        return 0;
5499
    }
5500
5501
    /**
5502
     * This function gets the last student's connection in chat.
5503
     *
5504
     * @param      int     Student id
5505
     * @param      string     Course code
5506
     * @param    int        Session id (optional)
5507
     *
5508
     * @return string datetime formatted without day (e.g: February 23, 2010 10:20:50 )
5509
     */
5510
    public static function chat_last_connection(
5511
        $student_id,
5512
        $courseId,
5513
        $session_id = 0
5514
    ) {
5515
        $student_id = (int) $student_id;
5516
        $courseId = (int) $courseId;
5517
        $session_id = (int) $session_id;
5518
        $date_time = '';
5519
5520
        // table definition
5521
        $tbl_stats_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
5522
        $sql = "SELECT access_date
5523
                FROM $tbl_stats_access
5524
                WHERE
5525
                     access_tool='".TOOL_CHAT."' AND
5526
                     access_user_id='$student_id' AND
5527
                     c_id = $courseId AND
5528
                     access_session_id = '$session_id'
5529
                ORDER BY access_date DESC limit 1";
5530
        $rs = Database::query($sql);
5531
        if (Database::num_rows($rs) > 0) {
5532
            $row = Database::fetch_array($rs);
5533
            $date_time = api_convert_and_format_date(
5534
                $row['access_date'],
5535
                null,
5536
                date_default_timezone_get()
5537
            );
5538
        }
5539
5540
        return $date_time;
5541
    }
5542
5543
    /**
5544
     * Get count student's visited links.
5545
     *
5546
     * @param int $student_id Student id
5547
     * @param int $courseId
5548
     * @param int $session_id Session id (optional)
5549
     *
5550
     * @return int count of visited links
5551
     */
5552
    public static function count_student_visited_links($student_id, $courseId, $session_id = 0)
5553
    {
5554
        $student_id = (int) $student_id;
5555
        $courseId = (int) $courseId;
5556
        $session_id = (int) $session_id;
5557
5558
        // table definition
5559
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
5560
5561
        $sql = 'SELECT 1
5562
                FROM '.$table.'
5563
                WHERE
5564
                    links_user_id= '.$student_id.' AND
5565
                    c_id = "'.$courseId.'" AND
5566
                    links_session_id = '.$session_id.' ';
5567
5568
        $rs = Database::query($sql);
5569
5570
        return Database::num_rows($rs);
5571
    }
5572
5573
    /**
5574
     * Get count student downloaded documents.
5575
     *
5576
     * @param    int        Student id
5577
     * @param int $courseId
5578
     * @param    int        Session id (optional)
5579
     *
5580
     * @return int Count downloaded documents
5581
     */
5582
    public static function count_student_downloaded_documents($student_id, $courseId, $session_id = 0)
5583
    {
5584
        $student_id = (int) $student_id;
5585
        $courseId = (int) $courseId;
5586
        $session_id = (int) $session_id;
5587
5588
        // table definition
5589
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
5590
5591
        $sql = 'SELECT 1
5592
                FROM '.$table.'
5593
                WHERE down_user_id = '.$student_id.'
5594
                AND c_id  = "'.$courseId.'"
5595
                AND down_session_id = '.$session_id.' ';
5596
        $rs = Database::query($sql);
5597
5598
        return Database::num_rows($rs);
5599
    }
5600
5601
    /**
5602
     * Get course list inside a session from a student.
5603
     *
5604
     * @param int $user_id    Student id
5605
     * @param int $id_session Session id (optional)
5606
     *
5607
     * @return array Courses list
5608
     */
5609
    public static function get_course_list_in_session_from_student($user_id, $id_session = 0)
5610
    {
5611
        $user_id = intval($user_id);
5612
        $id_session = intval($id_session);
5613
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5614
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
5615
5616
        $sql = "SELECT c.code
5617
                FROM $tbl_session_course_user sc
5618
                INNER JOIN $courseTable c
5619
                WHERE
5620
                    user_id= $user_id  AND
5621
                    session_id = $id_session";
5622
        $result = Database::query($sql);
5623
        $courses = [];
5624
        while ($row = Database::fetch_array($result)) {
5625
            $courses[$row['code']] = $row['code'];
5626
        }
5627
5628
        return $courses;
5629
    }
5630
5631
    /**
5632
     * Get inactive students in course.
5633
     *
5634
     * @param int        $courseId
5635
     * @param string|int $since      Since login course date (optional, default = 'never')
5636
     * @param int        $session_id (optional)
5637
     *
5638
     * @return array Inactive users
5639
     */
5640
    public static function getInactiveStudentsInCourse(
5641
        $courseId,
5642
        $since = 'never',
5643
        $session_id = 0,
5644
        $userActive = null
5645
    ) {
5646
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5647
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5648
        $table_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5649
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
5650
        $now = api_get_utc_datetime();
5651
        $courseId = (int) $courseId;
5652
        $session_id = (int) $session_id;
5653
5654
        if (empty($courseId)) {
5655
            return false;
5656
        }
5657
5658
        if ($since === 'never') {
5659
            if (empty($session_id)) {
5660
                $sql = 'SELECT course_user.user_id
5661
                        FROM '.$table_course_rel_user.' course_user
5662
                        LEFT JOIN '.$tbl_track_login.' stats_login
5663
                        ON course_user.user_id = stats_login.user_id AND
5664
                        relation_type<>'.COURSE_RELATION_TYPE_RRHH.'
5665
                        INNER JOIN '.$tableCourse.' c
5666
                        ON (c.id = course_user.c_id)
5667
                        WHERE
5668
                            course_user.c_id = '.$courseId.' AND
5669
                            stats_login.login_course_date IS NULL
5670
                        GROUP BY course_user.user_id';
5671
            } else {
5672
                $sql = 'SELECT session_course_user.user_id
5673
                        FROM '.$tbl_session_course_user.' session_course_user
5674
                        LEFT JOIN '.$tbl_track_login.' stats_login
5675
                        ON session_course_user.user_id = stats_login.user_id
5676
                        INNER JOIN '.$tableCourse.' c
5677
                        ON (c.id = session_course_user.c_id)
5678
                        WHERE
5679
                            session_course_user.c_id = '.$courseId.' AND
5680
                            stats_login.login_course_date IS NULL
5681
                        GROUP BY session_course_user.user_id';
5682
            }
5683
        } else {
5684
            $since = (int) $since;
5685
            if (empty($session_id)) {
5686
                $inner = 'INNER JOIN '.$table_course_rel_user.' course_user
5687
                          ON course_user.user_id = stats_login.user_id AND course_user.c_id = c.id ';
5688
            } else {
5689
                $inner = 'INNER JOIN '.$tbl_session_course_user.' session_course_user
5690
                          ON
5691
                            c.id = session_course_user.c_id AND
5692
                            session_course_user.session_id = '.$session_id.' AND
5693
                            session_course_user.user_id = stats_login.user_id ';
5694
            }
5695
5696
            $sql = 'SELECT
5697
                    stats_login.user_id,
5698
                    MAX(login_course_date) max_date
5699
                FROM '.$tbl_track_login.' stats_login
5700
                INNER JOIN '.$tableCourse.' c
5701
                ON (c.id = stats_login.c_id)
5702
                '.$inner.'
5703
                WHERE c.id = '.$courseId.'
5704
                GROUP BY stats_login.user_id
5705
                HAVING DATE_SUB("'.$now.'", INTERVAL '.$since.' DAY) > max_date ';
5706
        }
5707
5708
        $rs = Database::query($sql);
5709
5710
        $allow = api_get_plugin_setting('pausetraining', 'tool_enable') === 'true';
5711
        $allowPauseFormation = api_get_plugin_setting('pausetraining', 'allow_users_to_edit_pause_formation') === 'true';
5712
5713
        $extraFieldValue = new ExtraFieldValue('user');
5714
        $users = [];
5715
        while ($user = Database::fetch_array($rs)) {
5716
            $userId = $user['user_id'];
5717
            if (isset($userActive)) {
5718
                $userActive = (int) $userActive;
5719
                $uInfo = api_get_user_info($userId);
5720
                if ((int) $uInfo['active'] !== $userActive) {
5721
                    continue;
5722
                }
5723
            }
5724
            if ($allow && $allowPauseFormation) {
5725
                $pause = $extraFieldValue->get_values_by_handler_and_field_variable($userId, 'pause_formation');
5726
                if (!empty($pause) && isset($pause['value']) && 1 == $pause['value']) {
5727
                    // Skip user because he paused his formation.
5728
                    continue;
5729
                }
5730
            }
5731
5732
            $users[] = $userId;
5733
        }
5734
5735
        return $users;
5736
    }
5737
5738
    /**
5739
     * get count clicks about tools most used by course.
5740
     *
5741
     * @param int $courseId
5742
     * @param    int        Session id (optional),
5743
     * if param $session_id is null(default) it'll return results
5744
     * including sessions, 0 = session is not filtered
5745
     *
5746
     * @return array tools data
5747
     */
5748
    public static function get_tools_most_used_by_course($courseId, $session_id = null)
5749
    {
5750
        $courseId = (int) $courseId;
5751
        $data = [];
5752
        $TABLETRACK_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
5753
        $condition_session = '';
5754
        if (isset($session_id)) {
5755
            $session_id = (int) $session_id;
5756
            $condition_session = ' AND access_session_id = '.$session_id;
5757
        }
5758
        $sql = "SELECT
5759
                    access_tool,
5760
                    COUNT(DISTINCT access_user_id),
5761
                    count(access_tool) as count_access_tool
5762
                FROM $TABLETRACK_ACCESS
5763
                WHERE
5764
                    access_tool IS NOT NULL AND
5765
                    access_tool != '' AND
5766
                    c_id = '$courseId'
5767
                    $condition_session
5768
                GROUP BY access_tool
5769
                ORDER BY count_access_tool DESC
5770
                LIMIT 0, 3";
5771
        $rs = Database::query($sql);
5772
        if (Database::num_rows($rs) > 0) {
5773
            while ($row = Database::fetch_array($rs)) {
5774
                $data[] = $row;
5775
            }
5776
        }
5777
5778
        return $data;
5779
    }
5780
5781
    /**
5782
     * get documents most downloaded by course.
5783
     *
5784
     * @param      string     Course code
5785
     * @param    int        Session id (optional),
5786
     * if param $session_id is null(default) it'll return results including
5787
     * sessions, 0 = session is not filtered
5788
     * @param    int        Limit (optional, default = 0, 0 = without limit)
5789
     *
5790
     * @return array documents downloaded
5791
     */
5792
    public static function get_documents_most_downloaded_by_course(
5793
        $course_code,
5794
        $session_id = 0,
5795
        $limit = 0
5796
    ) {
5797
        $courseId = api_get_course_int_id($course_code);
5798
        $data = [];
5799
5800
        $TABLETRACK_DOWNLOADS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
5801
        $condition_session = '';
5802
        $session_id = intval($session_id);
5803
        if (!empty($session_id)) {
5804
            $condition_session = ' AND down_session_id = '.$session_id;
5805
        }
5806
        $sql = "SELECT
5807
                    down_doc_path,
5808
                    COUNT(DISTINCT down_user_id),
5809
                    COUNT(down_doc_path) as count_down
5810
                FROM $TABLETRACK_DOWNLOADS
5811
                WHERE c_id = $courseId
5812
                    $condition_session
5813
                GROUP BY down_doc_path
5814
                ORDER BY count_down DESC
5815
                LIMIT 0,  $limit";
5816
        $rs = Database::query($sql);
5817
5818
        if (Database::num_rows($rs) > 0) {
5819
            while ($row = Database::fetch_array($rs)) {
5820
                $data[] = $row;
5821
            }
5822
        }
5823
5824
        return $data;
5825
    }
5826
5827
    /**
5828
     * get links most visited by course.
5829
     *
5830
     * @param      string     Course code
5831
     * @param    int        Session id (optional),
5832
     * if param $session_id is null(default) it'll
5833
     * return results including sessions, 0 = session is not filtered
5834
     *
5835
     * @return array links most visited
5836
     */
5837
    public static function get_links_most_visited_by_course($course_code, $session_id = null)
5838
    {
5839
        $course_code = Database::escape_string($course_code);
5840
        $course_info = api_get_course_info($course_code);
5841
        $course_id = $course_info['real_id'];
5842
        $data = [];
5843
5844
        $TABLETRACK_LINKS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
5845
        $TABLECOURSE_LINKS = Database::get_course_table(TABLE_LINK);
5846
5847
        $condition_session = '';
5848
        if (isset($session_id)) {
5849
            $session_id = intval($session_id);
5850
            $condition_session = ' AND cl.session_id = '.$session_id;
5851
        }
5852
5853
        $sql = "SELECT cl.title, cl.url,count(DISTINCT sl.links_user_id), count(cl.title) as count_visits
5854
                FROM $TABLETRACK_LINKS AS sl, $TABLECOURSE_LINKS AS cl
5855
                WHERE
5856
                    cl.c_id = $course_id AND
5857
                    sl.links_link_id = cl.id AND
5858
                    sl.c_id = $course_id
5859
                    $condition_session
5860
                GROUP BY cl.title, cl.url
5861
                ORDER BY count_visits DESC
5862
                LIMIT 0, 3";
5863
        $rs = Database::query($sql);
5864
        if (Database::num_rows($rs) > 0) {
5865
            while ($row = Database::fetch_array($rs)) {
5866
                $data[] = $row;
5867
            }
5868
        }
5869
5870
        return $data;
5871
    }
5872
5873
    /**
5874
     * Shows the user progress (when clicking in the Progress tab).
5875
     *
5876
     * @param int    $user_id
5877
     * @param int    $session_id
5878
     * @param string $extra_params
5879
     * @param bool   $show_courses
5880
     * @param bool   $showAllSessions
5881
     * @param bool   $returnArray
5882
     *
5883
     * @return string|array
5884
     */
5885
    public static function showUserProgress(
5886
        $user_id,
5887
        $session_id = 0,
5888
        $extra_params = '',
5889
        $show_courses = true,
5890
        $showAllSessions = true,
5891
        $returnArray = false,
5892
        $showGraph = true
5893
    ) {
5894
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
5895
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
5896
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5897
        $tbl_access_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
5898
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5899
        $tbl_access_rel_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
5900
5901
        $trackingColumns = [
5902
            'course_session' => [
5903
                'course_title' => true,
5904
                'published_exercises' => true,
5905
                'new_exercises' => true,
5906
                'my_average' => true,
5907
                'average_exercise_result' => true,
5908
                'time_spent' => true,
5909
                'lp_progress' => true,
5910
                'score' => true,
5911
                'best_score' => true,
5912
                'last_connection' => true,
5913
                'details' => true,
5914
            ],
5915
        ];
5916
5917
        $trackingColumnsConfig = api_get_configuration_value('tracking_columns');
5918
        if (!empty($trackingColumnsConfig)) {
5919
            $trackingColumns = $trackingColumnsConfig;
5920
        }
5921
5922
        $user_id = (int) $user_id;
5923
        $session_id = (int) $session_id;
5924
        $urlId = api_get_current_access_url_id();
5925
5926
        if (api_is_multiple_url_enabled()) {
5927
            $sql = "SELECT c.id, c.code, title
5928
                    FROM $tbl_course_user cu
5929
                    INNER JOIN $tbl_course c
5930
                    ON (cu.c_id = c.id)
5931
                    INNER JOIN $tbl_access_rel_course a
5932
                    ON (a.c_id = c.id)
5933
                    WHERE
5934
                        cu.user_id = $user_id AND
5935
                        relation_type<> ".COURSE_RELATION_TYPE_RRHH." AND
5936
                        access_url_id = $urlId
5937
                    ORDER BY title";
5938
        } else {
5939
            $sql = "SELECT c.id, c.code, title
5940
                    FROM $tbl_course_user u
5941
                    INNER JOIN $tbl_course c ON (c_id = c.id)
5942
                    WHERE
5943
                        u.user_id= $user_id AND
5944
                        relation_type <> ".COURSE_RELATION_TYPE_RRHH."
5945
                    ORDER BY title";
5946
        }
5947
5948
        $rs = Database::query($sql);
5949
        $courses = $course_in_session = $temp_course_in_session = [];
5950
        $courseIdList = [];
5951
        while ($row = Database::fetch_array($rs, 'ASSOC')) {
5952
            $courses[$row['code']] = $row['title'];
5953
            $courseIdList[] = $row['id'];
5954
        }
5955
5956
        $orderBy = ' ORDER BY display_end_date DESC, name ';
5957
        $extraInnerJoin = null;
5958
5959
        if (SessionManager::orderCourseIsEnabled() && !empty($session_id)) {
5960
            $orderBy = ' ORDER BY s.id, src.position ';
5961
            $tableSessionRelCourse = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
5962
            $extraInnerJoin = " INNER JOIN $tableSessionRelCourse src
5963
                                ON (cu.c_id = src.c_id AND src.session_id = $session_id) ";
5964
        }
5965
5966
        $sessionCondition = '';
5967
        if (!empty($session_id)) {
5968
            $sessionCondition = " AND s.id = $session_id";
5969
        }
5970
5971
        // Get the list of sessions where the user is subscribed as student
5972
        if (api_is_multiple_url_enabled()) {
5973
            $sql = "SELECT DISTINCT c.code, s.id as session_id, name
5974
                    FROM $tbl_session_course_user cu
5975
                    INNER JOIN $tbl_access_rel_session a
5976
                    ON (a.session_id = cu.session_id)
5977
                    INNER JOIN $tbl_session s
5978
                    ON (s.id = a.session_id)
5979
                    INNER JOIN $tbl_course c
5980
                    ON (c.id = cu.c_id)
5981
                    $extraInnerJoin
5982
                    WHERE
5983
                        cu.user_id = $user_id AND
5984
                        access_url_id = ".$urlId."
5985
                        $sessionCondition
5986
                    $orderBy ";
5987
        } else {
5988
            $sql = "SELECT DISTINCT c.code, s.id as session_id, name
5989
                    FROM $tbl_session_course_user cu
5990
                    INNER JOIN $tbl_session s
5991
                    ON (s.id = cu.session_id)
5992
                    INNER JOIN $tbl_course c
5993
                    ON (c.id = cu.c_id)
5994
                    $extraInnerJoin
5995
                    WHERE
5996
                        cu.user_id = $user_id
5997
                        $sessionCondition
5998
                    $orderBy ";
5999
        }
6000
6001
        $rs = Database::query($sql);
6002
        $simple_session_array = [];
6003
        while ($row = Database::fetch_array($rs, 'ASSOC')) {
6004
            $course_info = api_get_course_info($row['code']);
6005
            $temp_course_in_session[$row['session_id']]['course_list'][$course_info['real_id']] = $course_info;
6006
            $temp_course_in_session[$row['session_id']]['name'] = $row['name'];
6007
            $simple_session_array[$row['session_id']] = $row['name'];
6008
        }
6009
6010
        foreach ($simple_session_array as $my_session_id => $session_name) {
6011
            $course_list = $temp_course_in_session[$my_session_id]['course_list'];
6012
            $my_course_data = [];
6013
            foreach ($course_list as $courseId => $course_data) {
6014
                $my_course_data[$courseId] = $course_data['title'];
6015
            }
6016
6017
            if (empty($session_id)) {
6018
                $my_course_data = utf8_sort($my_course_data);
6019
            }
6020
6021
            $final_course_data = [];
6022
            foreach ($my_course_data as $course_id => $value) {
6023
                if (isset($course_list[$course_id])) {
6024
                    $final_course_data[$course_id] = $course_list[$course_id];
6025
                }
6026
            }
6027
            $course_in_session[$my_session_id]['course_list'] = $final_course_data;
6028
            $course_in_session[$my_session_id]['name'] = $session_name;
6029
        }
6030
6031
        if ($returnArray) {
6032
            $course_in_session[0] = $courseIdList;
6033
6034
            return $course_in_session;
6035
        }
6036
6037
        $html = '';
6038
        // Course list.
6039
        if ($show_courses) {
6040
            if (!empty($courses)) {
6041
                $html .= Display::page_header(
6042
                    Display::return_icon('course.png', get_lang('MyCourses')).PHP_EOL.get_lang('MyCourses')
6043
                );
6044
6045
                $columns = [
6046
                    'course_title' => get_lang('Course'),
6047
                    'time_spent' => get_lang('TimeSpentInTheCourse'),
6048
                    'progress' => get_lang('Progress'),
6049
                    'best_score_in_lp' => get_lang('BestScoreInLearningPath'),
6050
                    'best_score_not_in_lp' => get_lang('BestScoreNotInLearningPath'),
6051
                    'latest_login' => get_lang('LastConnexion'),
6052
                    'details' => get_lang('Details'),
6053
                ];
6054
                $availableColumns = [];
6055
                if (isset($trackingColumns['my_progress_courses'])) {
6056
                    $availableColumns = $trackingColumns['my_progress_courses'];
6057
                }
6058
6059
                $columns = array_filter(
6060
                    $columns,
6061
                    function ($column, $columnKey) use ($availableColumns) {
6062
                        if (isset($availableColumns[$columnKey]) && $availableColumns[$columnKey] == false) {
6063
                            return false;
6064
                        }
6065
6066
                        return true;
6067
                    },
6068
                    ARRAY_FILTER_USE_BOTH
6069
                );
6070
6071
                $coursesTable = new SortableTableFromArray([], 0, 0, 'courses');
6072
                $coursesTable->setHeaders($columns);
6073
6074
                foreach ($courses as $course_code => $course_title) {
6075
                    $courseInfo = api_get_course_info($course_code);
6076
                    $courseId = $courseInfo['real_id'];
6077
                    $lpShowMaxProgress = api_get_configuration_value('lp_show_max_progress_instead_of_average');
6078
                    if (api_get_configuration_value('lp_show_max_progress_or_average_enable_course_level_redefinition')) {
6079
                        $lpShowProgressCourseSetting = api_get_course_setting('lp_show_max_or_average_progress', $courseInfo, true);
6080
                        if (in_array($lpShowProgressCourseSetting, ['max', 'average'])) {
6081
                            $lpShowMaxProgress = ('max' === $lpShowProgressCourseSetting);
6082
                        }
6083
                    }
6084
6085
                    $total_time_login = self::get_time_spent_on_the_course(
6086
                        $user_id,
6087
                        $courseId
6088
                    );
6089
                    $time = api_time_to_hms($total_time_login);
6090
                    $progress = self::get_avg_student_progress(
6091
                        $user_id,
6092
                        $course_code,
6093
                        [],
6094
                        null,
6095
                        false,
6096
                        false,
6097
                        $lpShowMaxProgress
6098
                    );
6099
                    $bestScore = self::get_avg_student_score(
6100
                        $user_id,
6101
                        $course_code,
6102
                        [],
6103
                        null,
6104
                        false,
6105
                        false,
6106
                        true
6107
                    );
6108
6109
                    $exerciseList = ExerciseLib::get_all_exercises(
6110
                        $courseInfo,
6111
                        0,
6112
                        false,
6113
                        null,
6114
                        false,
6115
                        1
6116
                    );
6117
6118
                    $bestScoreAverageNotInLP = 0;
6119
                    if (!empty($exerciseList)) {
6120
                        foreach ($exerciseList as $exerciseData) {
6121
                            $results = Event::get_best_exercise_results_by_user(
6122
                                $exerciseData['iid'],
6123
                                $courseInfo['real_id'],
6124
                                0,
6125
                                $user_id
6126
                            );
6127
                            $best = 0;
6128
                            if (!empty($results)) {
6129
                                foreach ($results as $result) {
6130
                                    if (!empty($result['exe_weighting'])) {
6131
                                        $score = $result['exe_result'] / $result['exe_weighting'];
6132
                                        if ($score > $best) {
6133
                                            $best = $score;
6134
                                        }
6135
                                    }
6136
                                }
6137
                            }
6138
                            $bestScoreAverageNotInLP += $best;
6139
                        }
6140
                        $bestScoreAverageNotInLP = round($bestScoreAverageNotInLP / count($exerciseList) * 100, 2);
6141
                    }
6142
6143
                    $last_connection = self::get_last_connection_date_on_the_course($user_id, $courseInfo);
6144
6145
                    if (is_null($progress) || empty($progress)) {
6146
                        $progress = '0%';
6147
                    } else {
6148
                        $progress = $progress.'%';
6149
                    }
6150
6151
                    $filterByCourse = isset($_GET['course']) && $course_code == $_GET['course']
6152
                        && empty($_GET['session_id']);
6153
6154
                    $url = api_get_course_url($course_code, $session_id);
6155
                    $course_url = Display::url($course_title, $url, ['target' => SESSION_LINK_TARGET]);
6156
                    $bestScoreResult = empty($bestScore) ? '-' : sprintf(get_lang('XPercent'), $bestScore);
6157
                    $bestScoreNotInLP = empty($bestScoreAverageNotInLP)
6158
                        ? '-'
6159
                        : sprintf(get_lang('XPercent'), $bestScoreAverageNotInLP);
6160
6161
                    $detailsLink = '';
6162
                    if ($filterByCourse) {
6163
                        $detailsLink .= '<a href="#course_session_data">';
6164
                        $detailsLink .= Display::return_icon('2rightarrow_na.png', get_lang('Details'));
6165
                        $detailsLink .= '</a>';
6166
                    } else {
6167
                        $detailsLink .= '<a href="'.api_get_self().'?course='.$course_code.$extra_params.'#course_session_data">';
6168
                        $detailsLink .= Display::return_icon('2rightarrow.png', get_lang('Details'));
6169
                        $detailsLink .= '</a>';
6170
                    }
6171
6172
                    $result = array_filter(
6173
                        [
6174
                            'course_title' => $course_url,
6175
                            'time_spent' => $time,
6176
                            'progress' => $progress,
6177
                            'best_score_in_lp' => $bestScoreResult,
6178
                            'best_score_not_in_lp' => $bestScoreNotInLP,
6179
                            'latest_login' => $last_connection,
6180
                            'details' => $detailsLink,
6181
                        ],
6182
                        function ($data, $columnKey) {
6183
                            if (isset($availableColumns[$columnKey]) && $availableColumns[$columnKey] == false) {
6184
                                return false;
6185
                            }
6186
6187
                            return true;
6188
                        },
6189
                        ARRAY_FILTER_USE_BOTH
6190
                    );
6191
6192
                    $coursesTable->addRow(
6193
                        array_values($result),
6194
                        ['style' => $filterByCourse ? 'background-color: #FBF09D;' : '']
6195
                    );
6196
                }
6197
6198
                $html .= Display::div($coursesTable->toHtml(), ['class' => 'table-responsive']);
6199
            }
6200
        }
6201
6202
        $allowCareerUser = api_get_configuration_value('allow_career_users');
6203
6204
        // Session list.
6205
        $visibleSessions = [];
6206
        if (!empty($course_in_session)) {
6207
            $main_session_graph = '';
6208
            // Load graphics only when calling to an specific session
6209
            $all_exercise_graph_name_list = [];
6210
            $my_results = [];
6211
            $all_exercise_graph_list = [];
6212
            $all_exercise_start_time = [];
6213
            foreach ($course_in_session as $my_session_id => $session_data) {
6214
                $course_list = $session_data['course_list'];
6215
                $user_count = count(SessionManager::get_users_by_session($my_session_id));
6216
                $exercise_graph_name_list = [];
6217
                $exercise_graph_list = [];
6218
6219
                $visibility = api_get_session_visibility($my_session_id, null, false, $user_id);
6220
6221
                if (SESSION_AVAILABLE === $visibility) {
6222
                    $visibleSessions[] = $my_session_id;
6223
                }
6224
6225
                foreach ($course_list as $course_data) {
6226
                    $exercise_list = ExerciseLib::get_all_exercises(
6227
                        $course_data,
6228
                        $my_session_id,
6229
                        false,
6230
                        null,
6231
                        false,
6232
                        1
6233
                    );
6234
6235
                    foreach ($exercise_list as $exercise_data) {
6236
                        $exercise_obj = new Exercise($course_data['real_id']);
6237
                        $exercise_obj->read($exercise_data['iid']);
6238
                        // Exercise is not necessary to be visible to show results check the result_disable configuration instead
6239
                        // $visible_return = $exercise_obj->is_visible();
6240
                        if ($exercise_data['results_disabled'] == 0 || $exercise_data['results_disabled'] == 2) {
6241
                            $best_average = (int)
6242
                                ExerciseLib::get_best_average_score_by_exercise(
6243
                                    $exercise_data['iid'],
6244
                                    $course_data['real_id'],
6245
                                    $my_session_id,
6246
                                    $user_count
6247
                                )
6248
                            ;
6249
6250
                            $exercise_graph_list[] = $best_average;
6251
                            $all_exercise_graph_list[] = $best_average;
6252
6253
                            $user_result_data = ExerciseLib::get_best_attempt_by_user(
6254
                                api_get_user_id(),
6255
                                $exercise_data['iid'],
6256
                                $course_data['real_id'],
6257
                                $my_session_id
6258
                            );
6259
6260
                            $score = 0;
6261
                            if (!empty($user_result_data['exe_weighting']) && intval($user_result_data['exe_weighting']) != 0) {
6262
                                $score = intval($user_result_data['exe_result'] / $user_result_data['exe_weighting'] * 100);
6263
                            }
6264
                            $time = api_strtotime($exercise_data['start_time']) ? api_strtotime($exercise_data['start_time'], 'UTC') : 0;
6265
                            $all_exercise_start_time[] = $time;
6266
                            $my_results[] = $score;
6267
                            if (count($exercise_list) <= 10) {
6268
                                $title = cut($course_data['title'], 30)." \n ".cut($exercise_data['title'], 30);
6269
                                $exercise_graph_name_list[] = $title;
6270
                                $all_exercise_graph_name_list[] = $title;
6271
                            } else {
6272
                                // if there are more than 10 results, space becomes difficult to find,
6273
                                // so only show the title of the exercise, not the tool
6274
                                $title = cut($exercise_data['title'], 30);
6275
                                $exercise_graph_name_list[] = $title;
6276
                                $all_exercise_graph_name_list[] = $title;
6277
                            }
6278
                        }
6279
                    }
6280
                }
6281
            }
6282
6283
            // Complete graph
6284
            if (!empty($my_results) && !empty($all_exercise_graph_list)) {
6285
                asort($all_exercise_start_time);
6286
6287
                // Fix exams order
6288
                $final_all_exercise_graph_name_list = [];
6289
                $my_results_final = [];
6290
                $final_all_exercise_graph_list = [];
6291
6292
                foreach ($all_exercise_start_time as $key => $time) {
6293
                    $label_time = '';
6294
                    if (!empty($time)) {
6295
                        $label_time = date('d-m-y', $time);
6296
                    }
6297
                    $final_all_exercise_graph_name_list[] = $all_exercise_graph_name_list[$key].' '.$label_time;
6298
                    $my_results_final[] = $my_results[$key];
6299
                    $final_all_exercise_graph_list[] = $all_exercise_graph_list[$key];
6300
                }
6301
                if ($showGraph) {
6302
                    $main_session_graph = '<div class="row"><div class="col-md-10 col-md-offset-1">'
6303
                        .self::generate_session_exercise_graph(
6304
                            $final_all_exercise_graph_name_list,
6305
                            $my_results_final,
6306
                            $final_all_exercise_graph_list
6307
                        )
6308
                        .'</div></div>';
6309
                }
6310
            }
6311
6312
            $sessionIcon = Display::return_icon('session.png', get_lang('Sessions'));
6313
6314
            $anchor = Display::url('', '', ['name' => 'course_session_header']);
6315
            $html .= $anchor.Display::page_header(
6316
                $sessionIcon.PHP_EOL.get_lang('Sessions')
6317
            );
6318
6319
            $sessionsTable = new SortableTableFromArray([], 0, 0, 'sessions');
6320
            $sessionsTable->setHeaders(
6321
                [
6322
                    get_lang('Session'),
6323
                    get_lang('PublishedExercises'),
6324
                    get_lang('NewExercises'),
6325
                    get_lang('AverageExerciseResult'),
6326
                    get_lang('Details'),
6327
                ]
6328
            );
6329
6330
            foreach ($course_in_session as $my_session_id => $session_data) {
6331
                $course_list = $session_data['course_list'];
6332
                $session_name = $session_data['name'];
6333
                if ($showAllSessions == false) {
6334
                    if (isset($session_id) && !empty($session_id)) {
6335
                        if ($session_id != $my_session_id) {
6336
                            continue;
6337
                        }
6338
                    }
6339
                }
6340
6341
                $all_exercises = 0;
6342
                $all_unanswered_exercises_by_user = 0;
6343
                $all_average = 0;
6344
                $stats_array = [];
6345
6346
                foreach ($course_list as $course_data) {
6347
                    // All exercises in the course @todo change for a real count
6348
                    $exercises = ExerciseLib::get_all_exercises($course_data, $my_session_id);
6349
                    $count_exercises = 0;
6350
                    if (is_array($exercises) && !empty($exercises)) {
6351
                        $count_exercises = count($exercises);
6352
                    }
6353
6354
                    // Count of user results
6355
                    $done_exercises = null;
6356
                    $courseInfo = api_get_course_info($course_data['code']);
6357
6358
                    $answered_exercises = 0;
6359
                    if (!empty($exercises)) {
6360
                        foreach ($exercises as $exercise_item) {
6361
                            $attempts = Event::count_exercise_attempts_by_user(
6362
                                api_get_user_id(),
6363
                                $exercise_item['iid'],
6364
                                $courseInfo['real_id'],
6365
                                $my_session_id
6366
                            );
6367
                            if ($attempts > 1) {
6368
                                $answered_exercises++;
6369
                            }
6370
                        }
6371
                    }
6372
6373
                    // Average
6374
                    $average = ExerciseLib::get_average_score_by_course(
6375
                        $courseInfo['real_id'],
6376
                        $my_session_id
6377
                    );
6378
                    $all_exercises += $count_exercises;
6379
                    $all_unanswered_exercises_by_user += $count_exercises - $answered_exercises;
6380
                    $all_average += $average;
6381
                }
6382
6383
                if (!empty($course_list)) {
6384
                    $all_average = $all_average / count($course_list);
6385
                }
6386
6387
                $filterBySession = isset($_GET['session_id']) && $my_session_id == $_GET['session_id'];
6388
6389
                $url = api_get_path(WEB_CODE_PATH)."session/index.php?session_id={$my_session_id}";
6390
6391
                $sessionsTable->addRow(
6392
                    [
6393
                        Display::url($session_name, $url, ['target' => SESSION_LINK_TARGET]),
6394
                        $all_exercises,
6395
                        $all_unanswered_exercises_by_user,
6396
                        ExerciseLib::convert_to_percentage($all_average),
6397
                        Display::url(
6398
                            Display::return_icon(
6399
                                $filterBySession ? '2rightarrow_na.png' : '2rightarrow.png',
6400
                                get_lang('Details')
6401
                            ),
6402
                            api_get_self().'?session_id='.$my_session_id.'#course_session_list'
6403
                        ),
6404
                    ],
6405
                    ['style' => $filterBySession ? 'background-color: #FBF09D;' : '']
6406
                );
6407
            }
6408
6409
            if ($allowCareerUser) {
6410
                $diagrams = '';
6411
                if (!empty($visibleSessions)) {
6412
                    $diagrams .= SessionManager::getCareerDiagramPerSessionList($visibleSessions, $user_id);
6413
                }
6414
                $html .= $diagrams.MyStudents::userCareersTable($user_id);
6415
            }
6416
6417
            $html .= Display::div($sessionsTable->toHtml(), ['class' => 'table-responsive']);
6418
6419
            if ($showGraph) {
6420
                $html .= Display::div(
6421
                    $main_session_graph,
6422
                    [
6423
                        'id' => 'session_graph',
6424
                        'class' => 'chart-session',
6425
                    ]
6426
                );
6427
            }
6428
6429
            // Checking selected session.
6430
            if (isset($_GET['session_id'])) {
6431
                $session_id_from_get = (int) $_GET['session_id'];
6432
                $session_data = $course_in_session[$session_id_from_get];
6433
                $course_list = $session_data['course_list'];
6434
6435
                $html .= '<a name="course_session_list"></a>';
6436
                $html .= Display::page_subheader($session_data['name'], get_lang('CourseList'));
6437
6438
                $columnHeaders = array_filter(
6439
                    [
6440
                        'course_title' => get_lang('Course'),
6441
                        'published_exercises' => get_lang('PublishedExercises'),
6442
                        'new_exercises' => get_lang('NewExercises'),
6443
                        'my_average' => get_lang('MyAverage'),
6444
                        'average_exercise_result' => get_lang('AverageExerciseResult'),
6445
                        'time_spent' => get_lang('TimeSpentInTheCourse'),
6446
                        'lp_progress' => get_lang('LPProgress'),
6447
                        'score' => get_lang('Score')
6448
                            .Display::return_icon('info3.gif', get_lang('ScormAndLPTestTotalAverage')),
6449
                        'best_score' => get_lang('BestScore'),
6450
                        'last_connection' => get_lang('LastConnexion'),
6451
                        'details' => get_lang('Details'),
6452
                    ],
6453
                    function ($column, $key) use ($trackingColumns) {
6454
                        if (isset($trackingColumns['course_session'])
6455
                            && in_array($key, $trackingColumns['course_session'])
6456
                            && $trackingColumns['course_session'][$key]
6457
                        ) {
6458
                            return true;
6459
                        }
6460
6461
                        return false;
6462
                    },
6463
                    ARRAY_FILTER_USE_BOTH
6464
                );
6465
6466
                $sessionCoursesTable = new SortableTableFromArray([], 0, 0, 'session_courses');
6467
                $sessionCoursesTable->setHeaders($columnHeaders);
6468
6469
                foreach ($course_list as $course_data) {
6470
                    $course_code = $course_data['code'];
6471
                    $course_title = $course_data['title'];
6472
                    $courseId = $course_data['real_id'];
6473
6474
                    // All exercises in the course @todo change for a real count
6475
                    $exercises = ExerciseLib::get_all_exercises(
6476
                        $course_data,
6477
                        $session_id_from_get
6478
                    );
6479
                    $count_exercises = 0;
6480
                    if (!empty($exercises)) {
6481
                        $count_exercises = count($exercises);
6482
                    }
6483
                    $answered_exercises = 0;
6484
                    foreach ($exercises as $exercise_item) {
6485
                        $attempts = Event::count_exercise_attempts_by_user(
6486
                            api_get_user_id(),
6487
                            $exercise_item['iid'],
6488
                            $courseId,
6489
                            $session_id_from_get
6490
                        );
6491
                        if ($attempts > 1) {
6492
                            $answered_exercises++;
6493
                        }
6494
                    }
6495
6496
                    $unanswered_exercises = $count_exercises - $answered_exercises;
6497
6498
                    // Average
6499
                    $average = ExerciseLib::get_average_score_by_course(
6500
                        $courseId,
6501
                        $session_id_from_get
6502
                    );
6503
                    $my_average = ExerciseLib::get_average_score_by_course_by_user(
6504
                        api_get_user_id(),
6505
                        $courseId,
6506
                        $session_id_from_get
6507
                    );
6508
6509
                    $bestScore = self::get_avg_student_score(
6510
                        $user_id,
6511
                        $course_code,
6512
                        [],
6513
                        $session_id_from_get,
6514
                        false,
6515
                        false,
6516
                        true
6517
                    );
6518
6519
                    $stats_array[$course_code] = [
6520
                        'exercises' => $count_exercises,
6521
                        'unanswered_exercises_by_user' => $unanswered_exercises,
6522
                        'done_exercises' => $done_exercises,
6523
                        'average' => $average,
6524
                        'my_average' => $my_average,
6525
                        'best_score' => $bestScore,
6526
                    ];
6527
6528
                    $last_connection = self::get_last_connection_date_on_the_course(
6529
                        $user_id,
6530
                        $course_data,
6531
                        $session_id_from_get
6532
                    );
6533
6534
                    $progress = self::get_avg_student_progress(
6535
                        $user_id,
6536
                        $course_code,
6537
                        [],
6538
                        $session_id_from_get,
6539
                        false,
6540
                        false,
6541
                        $lpShowMaxProgress
6542
                    );
6543
6544
                    $total_time_login = self::get_time_spent_on_the_course(
6545
                        $user_id,
6546
                        $courseId,
6547
                        $session_id_from_get
6548
                    );
6549
                    $time = api_time_to_hms($total_time_login);
6550
6551
                    $percentage_score = self::get_avg_student_score(
6552
                        $user_id,
6553
                        $course_code,
6554
                        [],
6555
                        $session_id_from_get
6556
                    );
6557
                    $courseCodeFromGet = $_GET['course'] ?? null;
6558
6559
                    $filterByCourse = $course_code == $courseCodeFromGet && $_GET['session_id'] == $session_id_from_get;
6560
6561
                    $url = api_get_course_url($course_code, $session_id_from_get);
6562
                    $course_url = Display::url(
6563
                        $course_title,
6564
                        $url,
6565
                        ['target' => SESSION_LINK_TARGET]
6566
                    );
6567
6568
                    if (is_numeric($progress)) {
6569
                        $progress = $progress.'%';
6570
                    } else {
6571
                        $progress = '0%';
6572
                    }
6573
                    if (is_numeric($percentage_score)) {
6574
                        $percentage_score = $percentage_score.'%';
6575
                    } else {
6576
                        $percentage_score = '0%';
6577
                    }
6578
6579
                    if (is_numeric($stats_array[$course_code]['best_score'])) {
6580
                        $bestScore = $stats_array[$course_code]['best_score'].'%';
6581
                    } else {
6582
                        $bestScore = '-';
6583
                    }
6584
6585
                    if (empty($last_connection) || is_bool($last_connection)) {
6586
                        $last_connection = '';
6587
                    }
6588
6589
                    if ($course_code == $courseCodeFromGet
6590
                        && $_GET['session_id'] == $session_id_from_get
6591
                    ) {
6592
                        $details = Display::url(
6593
                            Display::return_icon('2rightarrow_na.png', get_lang('Details')),
6594
                            '#course_session_data'
6595
                        );
6596
                    } else {
6597
                        $url = api_get_self().'?course='.$course_code.'&session_id='.$session_id_from_get.$extra_params.'#course_session_data';
6598
                        $details = Display::url(
6599
                            Display::return_icon(
6600
                                '2rightarrow.png',
6601
                                get_lang('Details')
6602
                            ),
6603
                            $url
6604
                        );
6605
                    }
6606
6607
                    $data = array_filter(
6608
                        [
6609
                            'course_title' => $course_url,
6610
                            'published_exercises' => $stats_array[$course_code]['exercises'], // exercise available
6611
                            'new_exercises' => $stats_array[$course_code]['unanswered_exercises_by_user'],
6612
                            'my_average' => ExerciseLib::convert_to_percentage($stats_array[$course_code]['my_average']),
6613
                            'average_exercise_result' => $stats_array[$course_code]['average'] == 0 ? '-' : '('.ExerciseLib::convert_to_percentage($stats_array[$course_code]['average']).')',
6614
                            'time_spent' => $time,
6615
                            'lp_progress' => $progress,
6616
                            'score' => $percentage_score,
6617
                            'best_score' => $bestScore,
6618
                            'last_connection' => $last_connection,
6619
                            'details' => $details,
6620
                        ],
6621
                        function ($value, $key) use ($trackingColumns) {
6622
                            if (in_array($key, $trackingColumns['course_session']) && $trackingColumns['course_session'][$key]) {
6623
                                return true;
6624
                            }
6625
6626
                            return false;
6627
                        },
6628
                        ARRAY_FILTER_USE_BOTH
6629
                    );
6630
6631
                    $sessionCoursesTable->addRow(
6632
                        array_values($data),
6633
                        ['style' => $filterByCourse ? 'background-color: #FBF09D;' : '']
6634
                    );
6635
                }
6636
6637
                $html .= Display::div($sessionCoursesTable->toHtml(), ['class' => 'table-responsive']);
6638
            }
6639
        }
6640
6641
        $pluginCalendar = api_get_plugin_setting('learning_calendar', 'enabled') === 'true';
6642
        if ($pluginCalendar) {
6643
            $course_in_session[0] = $courseIdList;
6644
            $plugin = LearningCalendarPlugin::create();
6645
            $html .= $plugin->getUserStatsPanel($user_id, $course_in_session);
6646
        }
6647
6648
        return $html;
6649
    }
6650
6651
    /**
6652
     * Shows the user detail progress (when clicking in the details link).
6653
     *
6654
     * @param int    $user_id
6655
     * @param string $course_code
6656
     * @param int    $session_id
6657
     */
6658
    public static function show_course_detail($user_id, $course_code, $session_id, $isAllowedToEdit = true): string
6659
    {
6660
        if (empty($user_id) || empty($course_code)) {
6661
            return '';
6662
        }
6663
6664
        $course_info = api_get_course_info($course_code);
6665
        if (empty($course_info)) {
6666
            return '';
6667
        }
6668
6669
        $user_id = (int) $user_id;
6670
        $user = api_get_user_entity($user_id);
6671
        $session_id = (int) $session_id;
6672
6673
        $html = '<a name="course_session_data"></a>';
6674
        $html .= Display::page_subheader2($course_info['title']);
6675
        // Show exercise results of invisible exercises? see BT#4091
6676
        $quizzesHtml = self::generateQuizzesTable($course_info, $session_id);
6677
        // LP table results
6678
        $learningPathsHtml = self::generateLearningPathsTable($user, $course_info, $session_id, $isAllowedToEdit);
6679
        $skillsHtml = self::displayUserSkills($user_id, $course_info['id'], $session_id);
6680
6681
        $toolsHtml = [
6682
            'quizzes' => $quizzesHtml,
6683
            'learning_paths' => $learningPathsHtml,
6684
            'skills' => $skillsHtml,
6685
        ];
6686
6687
        $toolsOrder = api_get_configuration_value('my_progress_course_tools_order');
6688
6689
        if (empty($toolsOrder)) {
6690
            $html .= implode(PHP_EOL, $toolsHtml);
6691
        } else {
6692
            foreach ($toolsOrder['order'] as $tool) {
6693
                $html .= $toolsHtml[$tool].PHP_EOL;
6694
            }
6695
        }
6696
6697
        return $html;
6698
    }
6699
6700
    /**
6701
     * Generates an histogram.
6702
     *
6703
     * @param array $names      list of exercise names
6704
     * @param array $my_results my results 0 to 100
6705
     * @param array $average    average scores 0-100
6706
     *
6707
     * @return string
6708
     */
6709
    public static function generate_session_exercise_graph($names, $my_results, $average)
6710
    {
6711
        $html = api_get_js('chartjs/Chart.js');
6712
        $canvas = Display::tag('canvas', '', ['id' => 'session_graph_chart']);
6713
        $html .= Display::tag('div', $canvas, ['style' => 'width:100%']);
6714
        $jsStr = " var data = {
6715
                       labels:".json_encode($names).",
6716
                       datasets: [
6717
                       {
6718
                         label: '".get_lang('MyResults')."',
6719
                         backgroundColor: 'rgb(255, 99, 132)',
6720
                         stack: 'Stack1',
6721
                         data: ".json_encode($my_results).",
6722
                        },
6723
                        {
6724
                         label: '".get_lang('AverageScore')."',
6725
                         backgroundColor: 'rgb(75, 192, 192)',
6726
                         stack: 'Stack2',
6727
                         data: ".json_encode($average).",
6728
                        },
6729
                        ],
6730
                    };
6731
                    var ctx = document.getElementById('session_graph_chart').getContext('2d');
6732
                    var myBarChart = new Chart(ctx, {
6733
                    type: 'bar',
6734
                    data: data,
6735
                    options: {
6736
                            title: {
6737
                                    display: true,
6738
                                    text: '".get_lang('ExercisesInTimeProgressChart')."'
6739
                            },
6740
                            tooltips: {
6741
                                    mode: 'index',
6742
                                    intersect: false
6743
                            },
6744
                            responsive: true,
6745
                            scales: {
6746
                                yAxes: [{
6747
                                    ticks: {
6748
                                        // Include a dollar sign in the ticks
6749
                                        callback: function(value, index, values) {
6750
                                            return value + '%';
6751
                                        }
6752
                                    }
6753
                                }]
6754
                            }
6755
                    }
6756
                });";
6757
        $html .= Display::tag('script', $jsStr);
6758
6759
        return $html;
6760
    }
6761
6762
    /**
6763
     * Returns a thumbnail of the function generate_exercise_result_graph.
6764
     *
6765
     * @param array $attempts
6766
     */
6767
    public static function generate_exercise_result_thumbnail_graph($attempts)
6768
    {
6769
        // $exercise_title = $attempts['title'];
6770
        $attempts = $attempts['data'];
6771
        $my_exercise_result_array = $exercise_result = [];
6772
        if (empty($attempts)) {
6773
            return null;
6774
        }
6775
6776
        foreach ($attempts as $attempt) {
6777
            if (api_get_user_id() == $attempt['exe_user_id']) {
6778
                if ($attempt['exe_weighting'] != 0) {
6779
                    $my_exercise_result_array[] = $attempt['exe_result'] / $attempt['exe_weighting'];
6780
                }
6781
            } else {
6782
                if ($attempt['exe_weighting'] != 0) {
6783
                    $exercise_result[] = $attempt['exe_result'] / $attempt['exe_weighting'];
6784
                }
6785
            }
6786
        }
6787
6788
        // Getting best result
6789
        rsort($my_exercise_result_array);
6790
        $my_exercise_result = 0;
6791
        if (isset($my_exercise_result_array[0])) {
6792
            $my_exercise_result = $my_exercise_result_array[0] * 100;
6793
        }
6794
6795
        $max = 100;
6796
        $pieces = 5;
6797
        $part = round($max / $pieces);
6798
        $x_axis = [];
6799
        $final_array = [];
6800
        $my_final_array = [];
6801
6802
        for ($i = 1; $i <= $pieces; $i++) {
6803
            $sum = 1;
6804
            if ($i == 1) {
6805
                $sum = 0;
6806
            }
6807
            $min = ($i - 1) * $part + $sum;
6808
            $max = $i * $part;
6809
            $x_axis[] = $min." - ".$max;
6810
            $count = 0;
6811
            foreach ($exercise_result as $result) {
6812
                $percentage = $result * 100;
6813
                if ($percentage >= $min && $percentage <= $max) {
6814
                    // echo ' is > ';
6815
                    $count++;
6816
                }
6817
            }
6818
            // echo '<br />';
6819
            $final_array[] = $count;
6820
6821
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
6822
                $my_final_array[] = 1;
6823
            } else {
6824
                $my_final_array[] = 0;
6825
            }
6826
        }
6827
6828
        // Fix to remove the data of the user with my data
6829
        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...
6830
            if (!empty($my_final_array[$i])) {
6831
                $my_final_array[$i] = $final_array[$i] + 1; // Add my result
6832
                $final_array[$i] = 0;
6833
            }
6834
        }
6835
6836
        // Dataset definition
6837
        $dataSet = new pData();
6838
        $dataSet->addPoints($final_array, 'Serie1');
6839
        $dataSet->addPoints($my_final_array, 'Serie2');
6840
        $dataSet->normalize(100, "%");
6841
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true);
6842
6843
        // Cache definition
6844
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
6845
        $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
6846
        $chartHash = $myCache->getHash($dataSet);
6847
        if ($myCache->isInCache($chartHash)) {
6848
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6849
            $myCache->saveFromCache($chartHash, $imgPath);
6850
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6851
        } else {
6852
            /* Create the pChart object */
6853
            $widthSize = 80;
6854
            $heightSize = 35;
6855
            $fontSize = 2;
6856
6857
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
6858
6859
            /* Turn of Antialiasing */
6860
            $myPicture->Antialias = false;
6861
6862
            /* Add a border to the picture */
6863
            $myPicture->drawRectangle(
6864
                0,
6865
                0,
6866
                $widthSize - 1,
6867
                $heightSize - 1,
6868
                ['R' => 0, 'G' => 0, 'B' => 0]
6869
            );
6870
6871
            /* Set the default font */
6872
            $myPicture->setFontProperties(
6873
                [
6874
                    'FontName' => api_get_path(
6875
                        SYS_FONTS_PATH
6876
                    ).'opensans/OpenSans-Regular.ttf',
6877
                    'FontSize' => $fontSize,
6878
                ]
6879
            );
6880
6881
            /* Do not write the chart title */
6882
            /* Define the chart area */
6883
            $myPicture->setGraphArea(5, 5, $widthSize - 5, $heightSize - 5);
6884
6885
            /* Draw the scale */
6886
            $scaleSettings = [
6887
                'GridR' => 200,
6888
                'GridG' => 200,
6889
                'GridB' => 200,
6890
                'DrawSubTicks' => true,
6891
                'CycleBackground' => true,
6892
                'Mode' => SCALE_MODE_MANUAL,
6893
                'ManualScale' => [
6894
                    '0' => [
6895
                        'Min' => 0,
6896
                        'Max' => 100,
6897
                    ],
6898
                ],
6899
            ];
6900
            $myPicture->drawScale($scaleSettings);
6901
6902
            /* Turn on shadow computing */
6903
            $myPicture->setShadow(
6904
                true,
6905
                [
6906
                    'X' => 1,
6907
                    'Y' => 1,
6908
                    'R' => 0,
6909
                    'G' => 0,
6910
                    'B' => 0,
6911
                    'Alpha' => 10,
6912
                ]
6913
            );
6914
6915
            /* Draw the chart */
6916
            $myPicture->setShadow(
6917
                true,
6918
                [
6919
                    'X' => 1,
6920
                    'Y' => 1,
6921
                    'R' => 0,
6922
                    'G' => 0,
6923
                    'B' => 0,
6924
                    'Alpha' => 10,
6925
                ]
6926
            );
6927
            $settings = [
6928
                'DisplayValues' => true,
6929
                'DisplaySize' => $fontSize,
6930
                'DisplayR' => 0,
6931
                'DisplayG' => 0,
6932
                'DisplayB' => 0,
6933
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
6934
                'Gradient' => false,
6935
                'Surrounding' => 5,
6936
                'InnerSurrounding' => 5,
6937
            ];
6938
            $myPicture->drawStackedBarChart($settings);
6939
6940
            /* Save and write in cache */
6941
            $myCache->writeToCache($chartHash, $myPicture);
6942
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6943
            $myCache->saveFromCache($chartHash, $imgPath);
6944
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6945
        }
6946
6947
        return $imgPath;
6948
    }
6949
6950
    /**
6951
     * Generates a big graph with the number of best results.
6952
     *
6953
     * @param	array
6954
     */
6955
    public static function generate_exercise_result_graph($attempts)
6956
    {
6957
        $exercise_title = strip_tags($attempts['title']);
6958
        $attempts = $attempts['data'];
6959
        $my_exercise_result_array = $exercise_result = [];
6960
        if (empty($attempts)) {
6961
            return null;
6962
        }
6963
        foreach ($attempts as $attempt) {
6964
            if (api_get_user_id() == $attempt['exe_user_id']) {
6965
                if ($attempt['exe_weighting'] != 0) {
6966
                    $my_exercise_result_array[] = $attempt['exe_result'] / $attempt['exe_weighting'];
6967
                }
6968
            } else {
6969
                if ($attempt['exe_weighting'] != 0) {
6970
                    $exercise_result[] = $attempt['exe_result'] / $attempt['exe_weighting'];
6971
                }
6972
            }
6973
        }
6974
6975
        // Getting best result
6976
        rsort($my_exercise_result_array);
6977
        $my_exercise_result = 0;
6978
        if (isset($my_exercise_result_array[0])) {
6979
            $my_exercise_result = $my_exercise_result_array[0] * 100;
6980
        }
6981
6982
        $max = 100;
6983
        $pieces = 5;
6984
        $part = round($max / $pieces);
6985
        $x_axis = [];
6986
        $final_array = [];
6987
        $my_final_array = [];
6988
6989
        for ($i = 1; $i <= $pieces; $i++) {
6990
            $sum = 1;
6991
            if ($i == 1) {
6992
                $sum = 0;
6993
            }
6994
            $min = ($i - 1) * $part + $sum;
6995
            $max = $i * $part;
6996
            $x_axis[] = $min." - ".$max;
6997
            $count = 0;
6998
            foreach ($exercise_result as $result) {
6999
                $percentage = $result * 100;
7000
                if ($percentage >= $min && $percentage <= $max) {
7001
                    $count++;
7002
                }
7003
            }
7004
            $final_array[] = $count;
7005
7006
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
7007
                $my_final_array[] = 1;
7008
            } else {
7009
                $my_final_array[] = 0;
7010
            }
7011
        }
7012
7013
        // Fix to remove the data of the user with my data
7014
7015
        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...
7016
            if (!empty($my_final_array[$i])) {
7017
                $my_final_array[$i] = $final_array[$i] + 1; // Add my result
7018
                $final_array[$i] = 0;
7019
            }
7020
        }
7021
7022
        // Dataset definition
7023
        $dataSet = new pData();
7024
        $dataSet->addPoints($final_array, 'Serie1');
7025
        $dataSet->addPoints($my_final_array, 'Serie2');
7026
        $dataSet->addPoints($x_axis, 'Serie3');
7027
7028
        $dataSet->setSerieDescription('Serie1', get_lang('Score'));
7029
        $dataSet->setSerieDescription('Serie2', get_lang('MyResults'));
7030
        $dataSet->setAbscissa('Serie3');
7031
7032
        $dataSet->setXAxisName(get_lang('Score'));
7033
        $dataSet->normalize(100, "%");
7034
7035
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true);
7036
7037
        // Cache definition
7038
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
7039
        $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
7040
        $chartHash = $myCache->getHash($dataSet);
7041
7042
        if ($myCache->isInCache($chartHash)) {
7043
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
7044
            $myCache->saveFromCache($chartHash, $imgPath);
7045
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
7046
        } else {
7047
            /* Create the pChart object */
7048
            $widthSize = 480;
7049
            $heightSize = 250;
7050
            $fontSize = 8;
7051
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
7052
7053
            /* Turn of Antialiasing */
7054
            $myPicture->Antialias = false;
7055
7056
            /* Add a border to the picture */
7057
            $myPicture->drawRectangle(0, 0, $widthSize - 1, $heightSize - 1, ['R' => 0, 'G' => 0, 'B' => 0]);
7058
7059
            /* Set the default font */
7060
            $myPicture->setFontProperties(
7061
                [
7062
                    'FontName' => api_get_path(
7063
                        SYS_FONTS_PATH
7064
                    ).'opensans/OpenSans-Regular.ttf',
7065
                    'FontSize' => 10,
7066
                ]
7067
            );
7068
7069
            /* Write the chart title */
7070
            $myPicture->drawText(
7071
                250,
7072
                20,
7073
                $exercise_title,
7074
                [
7075
                    'FontSize' => 12,
7076
                    'Align' => TEXT_ALIGN_BOTTOMMIDDLE,
7077
                ]
7078
            );
7079
7080
            /* Define the chart area */
7081
            $myPicture->setGraphArea(50, 50, $widthSize - 20, $heightSize - 30);
7082
7083
            /* Draw the scale */
7084
            $scaleSettings = [
7085
                'GridR' => 200,
7086
                'GridG' => 200,
7087
                'GridB' => 200,
7088
                'DrawSubTicks' => true,
7089
                'CycleBackground' => true,
7090
                'Mode' => SCALE_MODE_MANUAL,
7091
                'ManualScale' => [
7092
                    '0' => [
7093
                        'Min' => 0,
7094
                        'Max' => 100,
7095
                    ],
7096
                ],
7097
            ];
7098
            $myPicture->drawScale($scaleSettings);
7099
7100
            /* Turn on shadow computing */
7101
            $myPicture->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]);
7102
7103
            /* Draw the chart */
7104
            $myPicture->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]);
7105
            $settings = [
7106
                'DisplayValues' => true,
7107
                'DisplaySize' => $fontSize,
7108
                'DisplayR' => 0,
7109
                'DisplayG' => 0,
7110
                'DisplayB' => 0,
7111
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
7112
                'Gradient' => false,
7113
                'Surrounding' => 30,
7114
                'InnerSurrounding' => 25,
7115
            ];
7116
            $myPicture->drawStackedBarChart($settings);
7117
7118
            $legendSettings = [
7119
                'Mode' => LEGEND_HORIZONTAL,
7120
                'Style' => LEGEND_NOBORDER,
7121
            ];
7122
            $myPicture->drawLegend($widthSize / 2, 30, $legendSettings);
7123
7124
            /* Write and save into cache */
7125
            $myCache->writeToCache($chartHash, $myPicture);
7126
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
7127
            $myCache->saveFromCache($chartHash, $imgPath);
7128
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
7129
        }
7130
7131
        return $imgPath;
7132
    }
7133
7134
    /**
7135
     * @param FormValidator $form
7136
     */
7137
    public static function setUserSearchForm($form, $displayExtraFields = false)
7138
    {
7139
        global $_configuration;
7140
        $form->addElement('text', 'keyword', get_lang('Keyword'));
7141
        $form->addElement(
7142
            'select',
7143
            'active',
7144
            get_lang('Status'),
7145
            [1 => get_lang('Active'), 0 => get_lang('Inactive')]
7146
        );
7147
7148
        $form->addElement(
7149
            'select',
7150
            'sleeping_days',
7151
            get_lang('InactiveDays'),
7152
            [
7153
                '',
7154
                1 => 1,
7155
                5 => 5,
7156
                15 => 15,
7157
                30 => 30,
7158
                60 => 60,
7159
                90 => 90,
7160
                120 => 120,
7161
            ]
7162
        );
7163
7164
        if ($displayExtraFields) {
7165
            $extraField = new ExtraField('user');
7166
            $extraField->addElements($form, 0, [], true, false, [], [], [], false, true);
7167
        }
7168
7169
        $form->addButtonSearch(get_lang('Search'));
7170
7171
        return $form;
7172
    }
7173
7174
    /**
7175
     * Get the progress of a exercise.
7176
     *
7177
     * @param int    $sessionId  The session ID (session.id)
7178
     * @param int    $courseId   The course ID (course.id)
7179
     * @param int    $exerciseId The quiz ID (c_quiz.id)
7180
     * @param string $date_from
7181
     * @param string $date_to
7182
     * @param array  $options    An array of options you can pass to the query (limit, where and order)
7183
     *
7184
     * @return array An array with the data of exercise(s) progress
7185
     */
7186
    public static function get_exercise_progress(
7187
        $sessionId = 0,
7188
        $courseId = 0,
7189
        $exerciseId = 0,
7190
        $date_from = null,
7191
        $date_to = null,
7192
        $options = []
7193
    ) {
7194
        $sessionId = intval($sessionId);
7195
        $courseId = intval($courseId);
7196
        $exerciseId = intval($exerciseId);
7197
        $date_from = Database::escape_string($date_from);
7198
        $date_to = Database::escape_string($date_to);
7199
        /*
7200
         * This method gets the data by blocks, as previous attempts at one single
7201
         * query made it take ages. The logic of query division is described below
7202
         */
7203
        // Get tables names
7204
        $tuser = Database::get_main_table(TABLE_MAIN_USER);
7205
        $tquiz = Database::get_course_table(TABLE_QUIZ_TEST);
7206
        $tquiz_answer = Database::get_course_table(TABLE_QUIZ_ANSWER);
7207
        $tquiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION);
7208
        $tquiz_rel_question = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
7209
        $ttrack_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
7210
        $ttrack_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
7211
7212
        $sessions = [];
7213
        $courses = [];
7214
        // if session ID is defined but course ID is empty, get all the courses
7215
        // from that session
7216
        if (!empty($sessionId) && empty($courseId)) {
7217
            // $courses is an array of course int id as index and course details hash as value
7218
            $courses = SessionManager::get_course_list_by_session_id($sessionId);
7219
            $sessions[$sessionId] = api_get_session_info($sessionId);
7220
        } elseif (empty($sessionId) && !empty($courseId)) {
7221
            // if, to the contrary, course is defined but not sessions, get the sessions that include this course
7222
            // $sessions is an array like: [0] => ('id' => 3, 'name' => 'Session 35'), [1] => () etc;
7223
            $course = api_get_course_info_by_id($courseId);
7224
            $sessionsTemp = SessionManager::get_session_by_course($courseId);
7225
            $courses[$courseId] = $course;
7226
            foreach ($sessionsTemp as $sessionItem) {
7227
                $sessions[$sessionItem['id']] = $sessionItem;
7228
            }
7229
        } elseif (!empty($courseId) && !empty($sessionId)) {
7230
            // none is empty
7231
            $course = api_get_course_info_by_id($courseId);
7232
            $courses[$courseId] = [$course['code']];
7233
            $courses[$courseId]['code'] = $course['code'];
7234
            $sessions[$sessionId] = api_get_session_info($sessionId);
7235
        } else {
7236
            // both are empty, not enough data, return an empty array
7237
            return [];
7238
        }
7239
        // Now we have two arrays of courses and sessions with enough data to proceed
7240
        // If no course could be found, we shouldn't return anything.
7241
        // Sessions can be empty (then we only return the pure-course-context results)
7242
        if (count($courses) < 1) {
7243
            return [];
7244
        }
7245
7246
        $data = [];
7247
        // The following loop is less expensive than what it seems:
7248
        // - if a course was defined, then we only loop through sessions
7249
        // - if a session was defined, then we only loop through courses
7250
        // - if a session and a course were defined, then we only loop once
7251
        foreach ($courses as $courseIdx => $courseData) {
7252
            $where = '';
7253
            $whereParams = [];
7254
            $whereSessionParams = '';
7255
            if (count($sessions > 0)) {
7256
                foreach ($sessions as $sessionIdx => $sessionData) {
7257
                    if (!empty($sessionIdx)) {
7258
                        $whereSessionParams .= $sessionIdx.',';
7259
                    }
7260
                }
7261
                $whereSessionParams = substr($whereSessionParams, 0, -1);
7262
            }
7263
7264
            if (!empty($exerciseId)) {
7265
                $exerciseId = intval($exerciseId);
7266
                $where .= ' AND q.id = %d ';
7267
                $whereParams[] = $exerciseId;
7268
            }
7269
7270
            /*
7271
             * This feature has been disabled for now, to avoid having to
7272
             * join two very large tables
7273
            //2 = show all questions (wrong and correct answered)
7274
            if ($answer != 2) {
7275
                $answer = intval($answer);
7276
                //$where .= ' AND qa.correct = %d';
7277
                //$whereParams[] = $answer;
7278
            }
7279
            */
7280
7281
            $limit = '';
7282
            if (!empty($options['limit'])) {
7283
                $limit = " LIMIT ".$options['limit'];
7284
            }
7285
7286
            if (!empty($options['where'])) {
7287
                $where .= ' AND '.Database::escape_string($options['where']);
7288
            }
7289
7290
            $order = '';
7291
            if (!empty($options['order'])) {
7292
                $order = " ORDER BY ".$options['order'];
7293
            }
7294
7295
            if (!empty($date_to) && !empty($date_from)) {
7296
                $where .= sprintf(" AND (te.start_date BETWEEN '%s 00:00:00' AND '%s 23:59:59')", $date_from, $date_to);
7297
            }
7298
7299
            $sql = "SELECT
7300
                te.session_id,
7301
                ta.id as attempt_id,
7302
                te.exe_user_id as user_id,
7303
                te.exe_id as exercise_attempt_id,
7304
                ta.question_id,
7305
                ta.answer as answer_id,
7306
                ta.tms as time,
7307
                te.exe_exo_id as quiz_id,
7308
                CONCAT ('c', q.c_id, '_e', q.iid) as exercise_id,
7309
                q.title as quiz_title,
7310
                qq.description as description
7311
                FROM $ttrack_exercises te
7312
                INNER JOIN $ttrack_attempt ta
7313
                ON ta.exe_id = te.exe_id
7314
                INNER JOIN $tquiz q
7315
                ON q.iid = te.exe_exo_id
7316
                INNER JOIN $tquiz_rel_question rq
7317
                ON rq.exercice_id = q.iid AND rq.c_id = q.c_id
7318
                INNER JOIN $tquiz_question qq
7319
                ON
7320
                    qq.iid = rq.question_id AND
7321
                    qq.position = rq.question_order AND
7322
                    ta.question_id = rq.question_id
7323
                WHERE
7324
                    te.c_id = $courseIdx ".(empty($whereSessionParams) ? '' : "AND te.session_id IN ($whereSessionParams)")."
7325
                    AND q.c_id = $courseIdx
7326
                    $where $order $limit";
7327
            $sql_query = vsprintf($sql, $whereParams);
7328
7329
            // Now browse through the results and get the data
7330
            $rs = Database::query($sql_query);
7331
            $userIds = [];
7332
            $questionIds = [];
7333
            $answerIds = [];
7334
            while ($row = Database::fetch_array($rs)) {
7335
                // only show if exercise is visible
7336
                if (api_get_item_visibility($courseData, 'quiz', $row['exercise_id'])) {
7337
                    $userIds[$row['user_id']] = $row['user_id'];
7338
                    $questionIds[$row['question_id']] = $row['question_id'];
7339
                    $answerIds[$row['question_id']][$row['answer_id']] = $row['answer_id'];
7340
                    $row['session'] = $sessions[$row['session_id']];
7341
                    $data[] = $row;
7342
                }
7343
            }
7344
            // Now fill questions data. Query all questions and answers for this test to avoid
7345
            $sqlQuestions = "SELECT tq.c_id, tq.iid as question_id, tq.question, tqa.id_auto,
7346
                            tqa.answer, tqa.correct, tq.position, tqa.id_auto as answer_id
7347
                            FROM $tquiz_question tq, $tquiz_answer tqa
7348
                            WHERE
7349
                                tqa.question_id = tq.iid AND
7350
                                tq.c_id = $courseIdx AND
7351
                                tq.id IN (".implode(',', $questionIds).")";
7352
7353
            $resQuestions = Database::query($sqlQuestions);
7354
            $answer = [];
7355
            $question = [];
7356
            while ($rowQuestion = Database::fetch_assoc($resQuestions)) {
7357
                $questionId = $rowQuestion['question_id'];
7358
                $answerId = $rowQuestion['answer_id'];
7359
                $answer[$questionId][$answerId] = [
7360
                    'position' => $rowQuestion['position'],
7361
                    'question' => $rowQuestion['question'],
7362
                    'answer' => $rowQuestion['answer'],
7363
                    'correct' => $rowQuestion['correct'],
7364
                ];
7365
                $question[$questionId]['question'] = $rowQuestion['question'];
7366
            }
7367
7368
            // Now fill users data
7369
            $sqlUsers = "SELECT user_id, username, lastname, firstname
7370
                         FROM $tuser
7371
                         WHERE user_id IN (".implode(',', $userIds).")";
7372
            $resUsers = Database::query($sqlUsers);
7373
            while ($rowUser = Database::fetch_assoc($resUsers)) {
7374
                $users[$rowUser['user_id']] = $rowUser;
7375
            }
7376
7377
            foreach ($data as $id => $row) {
7378
                $rowQuestId = $row['question_id'];
7379
                $rowAnsId = $row['answer_id'];
7380
                $data[$id]['session'] = $sessions[$row['session_id']]['name'];
7381
                $data[$id]['firstname'] = $users[$row['user_id']]['firstname'];
7382
                $data[$id]['lastname'] = $users[$row['user_id']]['lastname'];
7383
                $data[$id]['username'] = $users[$row['user_id']]['username'];
7384
                $data[$id]['answer'] = $answer[$rowQuestId][$rowAnsId]['answer'];
7385
                $data[$id]['correct'] = ($answer[$rowQuestId][$rowAnsId]['correct'] == 0 ? get_lang('No') : get_lang('Yes'));
7386
                $data[$id]['question'] = $question[$rowQuestId]['question'];
7387
                $data[$id]['question_id'] = $rowQuestId;
7388
                $data[$id]['description'] = $row['description'];
7389
            }
7390
7391
            /*
7392
            The minimum expected array structure at the end is:
7393
            attempt_id,
7394
            session name,
7395
            exercise_id,
7396
            quiz_title,
7397
            username,
7398
            lastname,
7399
            firstname,
7400
            time,
7401
            question_id,
7402
            question,
7403
            answer,
7404
            */
7405
        }
7406
7407
        return $data;
7408
    }
7409
7410
    /**
7411
     * @param string             $tool
7412
     * @param sessionEntity|null $session Optional
7413
     *
7414
     * @throws \Doctrine\ORM\NonUniqueResultException
7415
     *
7416
     * @return \Chamilo\CourseBundle\Entity\CStudentPublication|null
7417
     */
7418
    public static function getLastStudentPublication(
7419
        User $user,
7420
        $tool,
7421
        Course $course,
7422
        SessionEntity $session = null
7423
    ) {
7424
        return Database::getManager()
7425
            ->createQuery("
7426
                SELECT csp
7427
                FROM ChamiloCourseBundle:CStudentPublication csp
7428
                INNER JOIN ChamiloCourseBundle:CItemProperty cip
7429
                    WITH (
7430
                        csp.iid = cip.ref AND
7431
                        csp.session = cip.session AND
7432
                        csp.cId = cip.course AND
7433
                        csp.userId = cip.lasteditUserId
7434
                    )
7435
                WHERE
7436
                    cip.session = :session AND cip.course = :course AND cip.lasteditUserId = :user AND cip.tool = :tool
7437
                ORDER BY csp.iid DESC
7438
            ")
7439
            ->setMaxResults(1)
7440
            ->setParameters([
7441
                'tool' => $tool,
7442
                'session' => $session,
7443
                'course' => $course,
7444
                'user' => $user,
7445
            ])
7446
            ->getOneOrNullResult();
7447
    }
7448
7449
    /**
7450
     * Get the HTML code for show a block with the achieved user skill on course/session.
7451
     *
7452
     * @param int  $userId
7453
     * @param int  $courseId
7454
     * @param int  $sessionId
7455
     * @param bool $forceView forces the view of the skills, not checking for deeper access
7456
     *
7457
     * @return string
7458
     */
7459
    public static function displayUserSkills($userId, $courseId = 0, $sessionId = 0, $forceView = false)
7460
    {
7461
        if (Skill::isAllowed($userId, false) === false && $forceView == false) {
7462
            return '';
7463
        }
7464
        $skillManager = new Skill();
7465
        $html = $skillManager->getUserSkillsTable($userId, $courseId, $sessionId)['table'];
7466
7467
        return $html;
7468
    }
7469
7470
    /**
7471
     * @param int $userId
7472
     * @param int $courseId
7473
     * @param int $sessionId
7474
     *
7475
     * @return array
7476
     */
7477
    public static function getCalculateTime($userId, $courseId, $sessionId)
7478
    {
7479
        $userId = (int) $userId;
7480
        $courseId = (int) $courseId;
7481
        $sessionId = (int) $sessionId;
7482
7483
        if (empty($userId) || empty($courseId)) {
7484
            return [];
7485
        }
7486
7487
        $sql = "SELECT MIN(date_reg) min, MAX(date_reg) max
7488
                FROM track_e_access_complete
7489
                WHERE
7490
                    user_id = $userId AND
7491
                    c_id = $courseId AND
7492
                    session_id = $sessionId AND
7493
                    login_as = 0
7494
                ORDER BY date_reg ASC
7495
                LIMIT 1";
7496
        $rs = Database::query($sql);
7497
7498
        $firstConnection = '';
7499
        $lastConnection = '';
7500
        if (Database::num_rows($rs) > 0) {
7501
            $value = Database::fetch_array($rs);
7502
            $firstConnection = $value['min'];
7503
            $lastConnection = $value['max'];
7504
        }
7505
7506
        $sql = "SELECT * FROM track_e_access_complete
7507
                WHERE
7508
                    user_id = $userId AND
7509
                    c_id = $courseId AND
7510
                    session_id = $sessionId AND
7511
                    login_as = 0 AND current_id <> 0";
7512
7513
        $res = Database::query($sql);
7514
        $reg = [];
7515
        while ($row = Database::fetch_assoc($res)) {
7516
            $reg[$row['id']] = $row;
7517
            $reg[$row['id']]['date_reg'] = strtotime($row['date_reg']);
7518
        }
7519
7520
        $sessions = [];
7521
        foreach ($reg as $key => $value) {
7522
            $sessions[$value['current_id']][$value['tool']][] = $value;
7523
        }
7524
7525
        $quizTime = 0;
7526
        $result = [];
7527
        $totalTime = 0;
7528
        $lpTime = [];
7529
        $lpDetailTime = [];
7530
        foreach ($sessions as $listPerTool) {
7531
            $min = 0;
7532
            $max = 0;
7533
            $sessionDiff = 0;
7534
            foreach ($listPerTool as $tool => $results) {
7535
                $beforeItem = [];
7536
                foreach ($results as $item) {
7537
                    if (empty($beforeItem)) {
7538
                        $beforeItem = $item;
7539
                        if (empty($min)) {
7540
                            $min = $item['date_reg'];
7541
                        }
7542
7543
                        if (empty($max)) {
7544
                            $max = $item['date_reg'];
7545
                        }
7546
                        continue;
7547
                    }
7548
7549
                    $partialTime = $item['date_reg'] - $beforeItem['date_reg'];
7550
                    if ($item['date_reg'] > $max) {
7551
                        $max = $item['date_reg'];
7552
                    }
7553
7554
                    if (empty($min)) {
7555
                        $min = $item['date_reg'];
7556
                    }
7557
7558
                    if ($item['date_reg'] < $min) {
7559
                        $min = $item['date_reg'];
7560
                    }
7561
7562
                    switch ($tool) {
7563
                        case TOOL_AGENDA:
7564
                        case TOOL_FORUM:
7565
                        case TOOL_ANNOUNCEMENT:
7566
                        case TOOL_COURSE_DESCRIPTION:
7567
                        case TOOL_SURVEY:
7568
                        case TOOL_NOTEBOOK:
7569
                        case TOOL_GRADEBOOK:
7570
                        case TOOL_DROPBOX:
7571
                        case 'Reports':
7572
                        case 'Videoconference':
7573
                        case TOOL_LINK:
7574
                        case TOOL_CHAT:
7575
                        case 'course-main':
7576
                            if (!isset($result[$tool])) {
7577
                                $result[$tool] = 0;
7578
                            }
7579
                            $result[$tool] += $partialTime;
7580
                            break;
7581
                        case TOOL_LEARNPATH:
7582
                            if ($item['tool_id'] != $beforeItem['tool_id']) {
7583
                                break;
7584
                            }
7585
                            if (!isset($lpTime[$item['tool_id']])) {
7586
                                $lpTime[$item['tool_id']] = 0;
7587
                            }
7588
7589
                            // Saving the attempt id "action_details"
7590
                            if (!empty($item['tool_id'])) {
7591
                                if (!empty($item['tool_id_detail'])) {
7592
                                    if (!isset($lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']])) {
7593
                                        $lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']] = 0;
7594
                                    }
7595
                                    $lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']] += $partialTime;
7596
                                }
7597
                                $lpTime[$item['tool_id']] += $partialTime;
7598
                            }
7599
                            break;
7600
                        case TOOL_QUIZ:
7601
                            if (!isset($lpTime[$item['action_details']])) {
7602
                                $lpTime[$item['action_details']] = 0;
7603
                            }
7604
                            if ($beforeItem['action'] === 'learnpath_id') {
7605
                                $lpTime[$item['action_details']] += $partialTime;
7606
                            } else {
7607
                                $quizTime += $partialTime;
7608
                            }
7609
                            break;
7610
                    }
7611
                    $beforeItem = $item;
7612
                }
7613
            }
7614
7615
            $sessionDiff += $max - $min;
7616
            if ($sessionDiff > 0) {
7617
                $totalTime += $sessionDiff;
7618
            }
7619
        }
7620
7621
        $totalLp = 0;
7622
        foreach ($lpTime as $value) {
7623
            $totalLp += $value;
7624
        }
7625
7626
        $result['learnpath_detailed'] = $lpDetailTime;
7627
        $result[TOOL_LEARNPATH] = $lpTime;
7628
        $result[TOOL_QUIZ] = $quizTime;
7629
        $result['total_learnpath'] = $totalLp;
7630
        $result['total_time'] = $totalTime;
7631
        $result['number_connections'] = count($sessions);
7632
        $result['first'] = $firstConnection;
7633
        $result['last'] = $lastConnection;
7634
7635
        return $result;
7636
    }
7637
7638
    /**
7639
     * Gets the IP of a given user, using the last login before the given date.
7640
     *
7641
     * @param int User ID
7642
     * @param string Datetime
7643
     * @param bool Whether to return the IP as a link or just as an IP
7644
     * @param string If defined and return_as_link if true, will be used as the text to be shown as the link
7645
     *
7646
     * @return string IP address (or false on error)
7647
     *
7648
     * @assert (0,0) === false
7649
     */
7650
    public static function get_ip_from_user_event(
7651
        $user_id,
7652
        $event_date,
7653
        $return_as_link = false,
7654
        $body_replace = null
7655
    ) {
7656
        if (empty($user_id) || empty($event_date)) {
7657
            return false;
7658
        }
7659
        $user_id = intval($user_id);
7660
        $event_date = Database::escape_string($event_date);
7661
        $table_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
7662
        $sql_ip = "SELECT login_date, user_ip
7663
                   FROM $table_login
7664
                   WHERE login_user_id = $user_id AND login_date < '$event_date'
7665
                   ORDER BY login_date DESC LIMIT 1";
7666
        $ip = '';
7667
        $res_ip = Database::query($sql_ip);
7668
        if ($res_ip !== false && Database::num_rows($res_ip) > 0) {
7669
            $row_ip = Database::fetch_row($res_ip);
7670
            if ($return_as_link) {
7671
                $ip = Display::url(
7672
                    empty($body_replace) ? $row_ip[1] : $body_replace,
7673
                    'http://www.whatsmyip.org/ip-geo-location/?ip='.$row_ip[1],
7674
                    ['title' => get_lang('TraceIP'), 'target' => '_blank']
7675
                );
7676
            } else {
7677
                $ip = $row_ip[1];
7678
            }
7679
        }
7680
7681
        return $ip;
7682
    }
7683
7684
    /**
7685
     * @param int   $userId
7686
     * @param array $courseInfo
7687
     * @param int   $sessionId
7688
     *
7689
     * @return array
7690
     */
7691
    public static function getToolInformation(
7692
        $userId,
7693
        $courseInfo,
7694
        $sessionId = 0
7695
    ) {
7696
        $csvContent = [];
7697
        $courseToolInformation = '';
7698
        $headerTool = [
7699
            [get_lang('Title')],
7700
            [get_lang('CreatedAt')],
7701
            [get_lang('UpdatedAt')],
7702
        ];
7703
7704
        $headerListForCSV = [];
7705
        foreach ($headerTool as $item) {
7706
            $headerListForCSV[] = $item[0];
7707
        }
7708
7709
        $courseForumInformationArray = getForumCreatedByUser(
7710
            $userId,
7711
            $courseInfo,
7712
            $sessionId
7713
        );
7714
7715
        if (!empty($courseForumInformationArray)) {
7716
            $csvContent[] = [];
7717
            $csvContent[] = [get_lang('Forums')];
7718
            $csvContent[] = $headerListForCSV;
7719
            foreach ($courseForumInformationArray as $row) {
7720
                $csvContent[] = $row;
7721
            }
7722
7723
            $courseToolInformation .= Display::page_subheader2(
7724
                get_lang('Forums')
7725
            );
7726
            $courseToolInformation .= Display::return_sortable_table(
7727
                $headerTool,
7728
                $courseForumInformationArray
7729
            );
7730
        }
7731
7732
        $courseWorkInformationArray = getWorkCreatedByUser(
7733
            $userId,
7734
            $courseInfo['real_id'],
7735
            $sessionId
7736
        );
7737
7738
        if (!empty($courseWorkInformationArray)) {
7739
            $csvContent[] = null;
7740
            $csvContent[] = [get_lang('Works')];
7741
            $csvContent[] = $headerListForCSV;
7742
7743
            foreach ($courseWorkInformationArray as $row) {
7744
                $csvContent[] = $row;
7745
            }
7746
            $csvContent[] = null;
7747
7748
            $courseToolInformation .= Display::page_subheader2(
7749
                get_lang('Works')
7750
            );
7751
            $courseToolInformation .= Display::return_sortable_table(
7752
                $headerTool,
7753
                $courseWorkInformationArray
7754
            );
7755
        }
7756
7757
        $courseToolInformationTotal = null;
7758
        if (!empty($courseToolInformation)) {
7759
            $sessionTitle = null;
7760
            if (!empty($sessionId)) {
7761
                $sessionTitle = ' ('.api_get_session_name($sessionId).')';
7762
            }
7763
7764
            $courseToolInformationTotal .= Display::page_subheader(
7765
                $courseInfo['title'].$sessionTitle
7766
            );
7767
            $courseToolInformationTotal .= $courseToolInformation;
7768
        }
7769
7770
        return [
7771
            'array' => $csvContent,
7772
            'html' => $courseToolInformationTotal,
7773
        ];
7774
    }
7775
7776
    /**
7777
     * @param int $sessionId
7778
     *
7779
     * @return bool
7780
     */
7781
    public static function isAllowToTrack($sessionId)
7782
    {
7783
        return
7784
            api_is_platform_admin(true, true)
7785
            || SessionManager::user_is_general_coach(api_get_user_id(), $sessionId)
7786
            || api_is_allowed_to_create_course()
7787
            || api_is_course_tutor()
7788
            || api_is_course_admin();
7789
    }
7790
7791
    public static function getCourseLpProgress($userId, $sessionId)
7792
    {
7793
        $controller = new IndexManager(get_lang('MyCourses'));
7794
        $data = $controller->returnCoursesAndSessions($userId);
7795
        $courseList = $data['courses'];
7796
        $result = [];
7797
        if ($courseList) {
7798
            // $counter = 1;
7799
            foreach ($courseList as $course) {
7800
                $courseId = $course['course_id'];
7801
                $courseInfo = api_get_course_info_by_id($courseId);
7802
                if (empty($courseInfo)) {
7803
                    continue;
7804
                }
7805
                $courseCode = $courseInfo['code'];
7806
                $lpTimeList = self::getCalculateTime($userId, $courseId, $sessionId);
7807
7808
                // total progress
7809
                $list = new LearnpathList(
7810
                    $userId,
7811
                    $courseInfo,
7812
                    0,
7813
                    'lp.publicatedOn ASC',
7814
                    true,
7815
                    null,
7816
                    true
7817
                );
7818
7819
                $list = $list->get_flat_list();
7820
                $totalProgress = 0;
7821
                $totalTime = 0;
7822
                if (!empty($list)) {
7823
                    foreach ($list as $lp_id => $learnpath) {
7824
                        if (!$learnpath['lp_visibility']) {
7825
                            continue;
7826
                        }
7827
                        $lpProgress = self::get_avg_student_progress($userId, $courseCode, [$lp_id], $sessionId);
7828
                        $time = isset($lpTimeList[TOOL_LEARNPATH][$lp_id]) ? $lpTimeList[TOOL_LEARNPATH][$lp_id] : 0;
7829
                        if ($lpProgress == 100) {
7830
                            if (!empty($time)) {
7831
                                $timeInMinutes = $time / 60;
7832
                                $min = (int) learnpath::getAccumulateWorkTimePrerequisite($lp_id, $courseId);
7833
                                if ($timeInMinutes >= $min) {
7834
                                    $totalProgress++;
7835
                                }
7836
                            }
7837
                        }
7838
                        $totalTime += $time;
7839
                    }
7840
7841
                    if (!empty($totalProgress)) {
7842
                        $totalProgress = (float) api_number_format($totalProgress / count($list) * 100, 2);
7843
                    }
7844
                }
7845
7846
                $progress = self::get_avg_student_progress($userId, $courseCode, [], $sessionId);
7847
7848
                $result[] = [
7849
                    'module' => $courseInfo['name'],
7850
                    'progress' => $progress,
7851
                    'qualification' => $totalProgress,
7852
                    'activeTime' => $totalTime,
7853
                ];
7854
            }
7855
        }
7856
7857
        return $result;
7858
    }
7859
7860
    /**
7861
     * @param int $userId
7862
     * @param int $courseId
7863
     * @param int $sessionId
7864
     *
7865
     * @return int
7866
     */
7867
    public static function getNumberOfCourseAccessDates($userId, $courseId, $sessionId)
7868
    {
7869
        $tblTrackCourseAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
7870
        $sessionCondition = api_get_session_condition($sessionId);
7871
        $courseId = (int) $courseId;
7872
        $userId = (int) $userId;
7873
7874
        $sql = "SELECT COUNT(DISTINCT (DATE(login_course_date))) AS c
7875
                FROM $tblTrackCourseAccess
7876
                WHERE c_id = $courseId $sessionCondition AND user_id = $userId";
7877
7878
        $result = Database::fetch_assoc(Database::query($sql));
7879
7880
        return (int) $result['c'];
7881
    }
7882
7883
    public static function processUserDataMove(
7884
        $user_id,
7885
        $course_info,
7886
        $origin_session_id,
7887
        $new_session_id,
7888
        $update_database,
7889
        $debug = false
7890
    ) {
7891
        // Begin with the import process
7892
        $origin_course_code = $course_info['code'];
7893
        $course_id = $course_info['real_id'];
7894
        $user_id = (int) $user_id;
7895
        $origin_session_id = (int) $origin_session_id;
7896
        $new_session_id = (int) $new_session_id;
7897
        $session = api_get_session_entity($new_session_id);
7898
        $em = Database::getManager();
7899
7900
        $TABLETRACK_EXERCICES = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
7901
        $TBL_TRACK_ATTEMPT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
7902
        $attemptRecording = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
7903
        $TBL_TRACK_E_COURSE_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
7904
        $TBL_TRACK_E_LAST_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
7905
        $TBL_LP_VIEW = Database::get_course_table(TABLE_LP_VIEW);
7906
        $TBL_NOTEBOOK = Database::get_course_table(TABLE_NOTEBOOK);
7907
        $TBL_STUDENT_PUBLICATION = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
7908
        $TBL_STUDENT_PUBLICATION_ASSIGNMENT = Database::get_course_table(TABLE_STUDENT_PUBLICATION_ASSIGNMENT);
7909
        $TBL_ITEM_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
7910
7911
        $TBL_DROPBOX_FILE = Database::get_course_table(TABLE_DROPBOX_FILE);
7912
        $TBL_DROPBOX_POST = Database::get_course_table(TABLE_DROPBOX_POST);
7913
        $TBL_AGENDA = Database::get_course_table(TABLE_AGENDA);
7914
7915
        // 1. track_e_exercises
7916
        // ORIGINAL COURSE
7917
        $sql = "SELECT * FROM $TABLETRACK_EXERCICES
7918
                WHERE c_id = $course_id AND  session_id = $origin_session_id AND exe_user_id = $user_id ";
7919
        $res = Database::query($sql);
7920
        $list = [];
7921
        while ($row = Database::fetch_array($res, 'ASSOC')) {
7922
            $list[$row['exe_id']] = $row;
7923
        }
7924
7925
        $result_message = [];
7926
        $result_message_compare = [];
7927
        if (!empty($list)) {
7928
            foreach ($list as $exe_id => $data) {
7929
                if ($update_database) {
7930
                    $sql = "UPDATE $TABLETRACK_EXERCICES SET session_id = '$new_session_id' WHERE exe_id = $exe_id";
7931
                    Database::query($sql);
7932
7933
                    $sql = "UPDATE $TBL_TRACK_ATTEMPT SET session_id = '$new_session_id' WHERE exe_id = $exe_id";
7934
                    Database::query($sql);
7935
7936
                    $sql = "UPDATE $attemptRecording SET session_id = '$new_session_id' WHERE exe_id = $exe_id";
7937
                    Database::query($sql);
7938
7939
                    if (!isset($result_message[$TABLETRACK_EXERCICES])) {
7940
                        $result_message[$TABLETRACK_EXERCICES] = 0;
7941
                    }
7942
                    $result_message[$TABLETRACK_EXERCICES]++;
7943
                } else {
7944
                    if (!empty($data['orig_lp_id']) && !empty($data['orig_lp_item_id'])) {
7945
                        $result_message['TRACK_E_EXERCISES'][$exe_id] = $data;
7946
                    } else {
7947
                        $result_message['TRACK_E_EXERCISES_IN_LP'][$exe_id] = $data;
7948
                    }
7949
                }
7950
            }
7951
        }
7952
7953
        // DESTINY COURSE
7954
        if (!$update_database) {
7955
            $sql = "SELECT * FROM $TABLETRACK_EXERCICES
7956
                    WHERE
7957
                        c_id = $course_id AND
7958
                        session_id = $new_session_id AND
7959
                        exe_user_id = $user_id ";
7960
            $res = Database::query($sql);
7961
            $list = [];
7962
            while ($row = Database::fetch_array($res, 'ASSOC')) {
7963
                $list[$row['exe_id']] = $row;
7964
            }
7965
7966
            if (!empty($list)) {
7967
                foreach ($list as $exe_id => $data) {
7968
                    if ($update_database) {
7969
                        $sql = "UPDATE $TABLETRACK_EXERCICES
7970
                                SET session_id = '$new_session_id'
7971
                                WHERE exe_id = $exe_id";
7972
                        Database::query($sql);
7973
                        $result_message[$TABLETRACK_EXERCICES]++;
7974
                    } else {
7975
                        if (!empty($data['orig_lp_id']) && !empty($data['orig_lp_item_id'])) {
7976
                            $result_message_compare['TRACK_E_EXERCISES'][$exe_id] = $data;
7977
                        } else {
7978
                            $result_message_compare['TRACK_E_EXERCISES_IN_LP'][$exe_id] = $data;
7979
                        }
7980
                    }
7981
                }
7982
            }
7983
        }
7984
7985
        // 2.track_e_attempt, track_e_attempt_recording, track_e_downloads
7986
        // Nothing to do because there are not relationship with a session
7987
        // 3. track_e_course_access
7988
        $sql = "SELECT * FROM $TBL_TRACK_E_COURSE_ACCESS
7989
                WHERE c_id = $course_id AND session_id = $origin_session_id  AND user_id = $user_id ";
7990
        $res = Database::query($sql);
7991
        $list = [];
7992
        while ($row = Database::fetch_array($res, 'ASSOC')) {
7993
            $list[$row['course_access_id']] = $row;
7994
        }
7995
7996
        if (!empty($list)) {
7997
            foreach ($list as $id => $data) {
7998
                if ($update_database) {
7999
                    $sql = "UPDATE $TBL_TRACK_E_COURSE_ACCESS
8000
                            SET session_id = $new_session_id
8001
                            WHERE course_access_id = $id";
8002
                    if ($debug) {
8003
                        echo $sql;
8004
                    }
8005
                    Database::query($sql);
8006
                    if (!isset($result_message[$TBL_TRACK_E_COURSE_ACCESS])) {
8007
                        $result_message[$TBL_TRACK_E_COURSE_ACCESS] = 0;
8008
                    }
8009
                    $result_message[$TBL_TRACK_E_COURSE_ACCESS]++;
8010
                }
8011
            }
8012
        }
8013
8014
        // 4. track_e_lastaccess
8015
        $sql = "SELECT access_id FROM $TBL_TRACK_E_LAST_ACCESS
8016
                WHERE
8017
                    c_id = $course_id AND
8018
                    access_session_id = $origin_session_id AND
8019
                    access_user_id = $user_id ";
8020
        $res = Database::query($sql);
8021
        $list = [];
8022
        while ($row = Database::fetch_array($res, 'ASSOC')) {
8023
            $list[] = $row['access_id'];
8024
        }
8025
8026
        if (!empty($list)) {
8027
            foreach ($list as $id) {
8028
                if ($update_database) {
8029
                    $sql = "UPDATE $TBL_TRACK_E_LAST_ACCESS
8030
                            SET access_session_id = $new_session_id
8031
                            WHERE access_id = $id";
8032
                    if ($debug) {
8033
                        echo $sql;
8034
                    }
8035
                    Database::query($sql);
8036
                    if (!isset($result_message[$TBL_TRACK_E_LAST_ACCESS])) {
8037
                        $result_message[$TBL_TRACK_E_LAST_ACCESS] = 0;
8038
                    }
8039
                    $result_message[$TBL_TRACK_E_LAST_ACCESS]++;
8040
                }
8041
            }
8042
        }
8043
8044
        // 5. lp_item_view
8045
        // CHECK ORIGIN
8046
        $sql = "SELECT * FROM $TBL_LP_VIEW
8047
                WHERE user_id = $user_id AND session_id = $origin_session_id AND c_id = $course_id ";
8048
        $res = Database::query($sql);
8049
8050
        // Getting the list of LPs in the new session
8051
        $lp_list = new LearnpathList($user_id, $course_info, $new_session_id);
8052
        $flat_list = $lp_list->get_flat_list();
8053
        $list = [];
8054
        while ($row = Database::fetch_array($res, 'ASSOC')) {
8055
            // Checking if the LP exist in the new session
8056
            // if (in_array($row['lp_id'], array_keys($flat_list))) {
8057
            $list[$row['id']] = $row;
8058
            // }
8059
        }
8060
8061
        if (!empty($list)) {
8062
            foreach ($list as $id => $data) {
8063
                if ($update_database) {
8064
                    $sql = "UPDATE $TBL_LP_VIEW
8065
                            SET session_id = $new_session_id
8066
                            WHERE c_id = $course_id AND id = $id ";
8067
                    if ($debug) {
8068
                        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...
8069
                    }
8070
                    $res = Database::query($sql);
8071
                    if ($debug) {
8072
                        var_dump($res);
8073
                    }
8074
                    if (!isset($result_message[$TBL_LP_VIEW])) {
8075
                        $result_message[$TBL_LP_VIEW] = 0;
8076
                    }
8077
                    $result_message[$TBL_LP_VIEW]++;
8078
                } else {
8079
                    // Getting all information of that lp_item_id
8080
                    $score = self::get_avg_student_score(
8081
                        $user_id,
8082
                        $origin_course_code,
8083
                        [$data['lp_id']],
8084
                        $origin_session_id
8085
                    );
8086
                    $progress = self::get_avg_student_progress(
8087
                        $user_id,
8088
                        $origin_course_code,
8089
                        [$data['lp_id']],
8090
                        $origin_session_id
8091
                    );
8092
                    $result_message['LP_VIEW'][$data['lp_id']] = [
8093
                        'score' => $score,
8094
                        'progress' => $progress,
8095
                    ];
8096
                }
8097
            }
8098
        }
8099
8100
        // Check destination.
8101
        if (!$update_database) {
8102
            $sql = "SELECT * FROM $TBL_LP_VIEW
8103
                    WHERE user_id = $user_id AND session_id = $new_session_id AND c_id = $course_id";
8104
            $res = Database::query($sql);
8105
8106
            // Getting the list of LPs in the new session
8107
            $lp_list = new LearnpathList($user_id, $course_info, $new_session_id);
8108
            $flat_list = $lp_list->get_flat_list();
8109
8110
            $list = [];
8111
            while ($row = Database::fetch_array($res, 'ASSOC')) {
8112
                // Checking if the LP exist in the new session
8113
                // if (in_array($row['lp_id'], array_keys($flat_list))) {
8114
                $list[$row['id']] = $row;
8115
                // }
8116
            }
8117
8118
            if (!empty($list)) {
8119
                foreach ($list as $id => $data) {
8120
                    // Getting all information of that lp_item_id
8121
                    $score = self::get_avg_student_score(
8122
                        $user_id,
8123
                        $origin_course_code,
8124
                        [$data['lp_id']],
8125
                        $new_session_id
8126
                    );
8127
                    $progress = self::get_avg_student_progress(
8128
                        $user_id,
8129
                        $origin_course_code,
8130
                        [$data['lp_id']],
8131
                        $new_session_id
8132
                    );
8133
                    $result_message_compare['LP_VIEW'][$data['lp_id']] = [
8134
                        'score' => $score,
8135
                        'progress' => $progress,
8136
                    ];
8137
                }
8138
            }
8139
        }
8140
8141
        // 6. Agenda
8142
        // calendar_event_attachment no problems no session_id
8143
        $sql = "SELECT ref FROM $TBL_ITEM_PROPERTY
8144
                WHERE tool = 'calendar_event' AND insert_user_id = $user_id AND c_id = $course_id ";
8145
        $res = Database::query($sql);
8146
        while ($row = Database::fetch_array($res, 'ASSOC')) {
8147
            $id = $row['ref'];
8148
            if ($update_database) {
8149
                $sql = "UPDATE $TBL_AGENDA SET session_id = $new_session_id WHERE c_id = $course_id AND id = $id ";
8150
                if ($debug) {
8151
                    var_dump($sql);
8152
                }
8153
                $res_update = Database::query($sql);
8154
                if ($debug) {
8155
                    var_dump($res_update);
8156
                }
8157
                if (!isset($result_message['agenda'])) {
8158
                    $result_message['agenda'] = 0;
8159
                }
8160
                $result_message['agenda']++;
8161
            }
8162
        }
8163
8164
        // 7. Forum ?? So much problems when trying to import data
8165
        // 8. Student publication - Works
8166
        $sql = "SELECT ref FROM $TBL_ITEM_PROPERTY
8167
                WHERE tool = 'work' AND insert_user_id = $user_id AND c_id = $course_id";
8168
        if ($debug) {
8169
            echo $sql;
8170
        }
8171
        $res = Database::query($sql);
8172
        while ($row = Database::fetch_array($res, 'ASSOC')) {
8173
            $id = $row['ref'];
8174
            $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION
8175
                    WHERE id = $id AND session_id = $origin_session_id AND c_id = $course_id";
8176
            if ($debug) {
8177
                var_dump($sql);
8178
            }
8179
            $sub_res = Database::query($sql);
8180
            if (Database::num_rows($sub_res) > 0) {
8181
                $data = Database::fetch_array($sub_res, 'ASSOC');
8182
                if ($debug) {
8183
                    var_dump($data);
8184
                }
8185
                $parent_id = $data['parent_id'];
8186
                if (isset($data['parent_id']) && !empty($data['parent_id'])) {
8187
                    $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION
8188
                            WHERE id = $parent_id AND c_id = $course_id";
8189
                    $select_res = Database::query($sql);
8190
                    $parent_data = Database::fetch_array($select_res, 'ASSOC');
8191
                    if ($debug) {
8192
                        var_dump($parent_data);
8193
                    }
8194
8195
                    $sys_course_path = api_get_path(SYS_COURSE_PATH);
8196
                    $course_dir = $sys_course_path.$course_info['path'];
8197
                    $base_work_dir = $course_dir.'/work';
8198
8199
                    // Creating the parent folder in the session if does not exists already
8200
                    // @todo ugly fix
8201
                    $search_this = "folder_moved_from_session_id_$origin_session_id";
8202
                    $search_this2 = $parent_data['url'];
8203
                    $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION
8204
                            WHERE description like '%$search_this%' AND
8205
                                  url LIKE '%$search_this2%' AND
8206
                                  session_id = $new_session_id AND
8207
                                  c_id = $course_id
8208
                            ORDER BY id desc  LIMIT 1";
8209
                    if ($debug) {
8210
                        echo $sql;
8211
                    }
8212
                    $sub_res = Database::query($sql);
8213
                    $num_rows = Database::num_rows($sub_res);
8214
                    $new_parent_id = 0;
8215
                    if ($num_rows > 0) {
8216
                        $new_result = Database::fetch_array($sub_res, 'ASSOC');
8217
                        $created_dir = $new_result['url'];
8218
                        $new_parent_id = $new_result['id'];
8219
                    } else {
8220
                        if ($update_database) {
8221
                            $dir_name = substr($parent_data['url'], 1);
8222
                            $created_dir = create_unexisting_work_directory($base_work_dir, $dir_name);
8223
                            $created_dir = '/'.$created_dir;
8224
                            $now = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
8225
                            // Creating directory
8226
                            $publication = new \Chamilo\CourseBundle\Entity\CStudentPublication();
8227
                            $publication
8228
                                ->setUrl($created_dir)
8229
                                ->setCId($course_id)
8230
                                ->setTitle($parent_data['title'])
8231
                                ->setDescription(
8232
                                    $parent_data['description']."folder_moved_from_session_id_$origin_session_id"
8233
                                )
8234
                                ->setActive(false)
8235
                                ->setAccepted(true)
8236
                                ->setPostGroupId(0)
8237
                                ->setHasProperties($parent_data['has_properties'])
8238
                                ->setWeight($parent_data['weight'])
8239
                                ->setContainsFile($parent_data['contains_file'])
8240
                                ->setFiletype('folder')
8241
                                ->setSentDate($now)
8242
                                ->setQualification($parent_data['qualification'])
8243
                                ->setParentId(0)
8244
                                ->setQualificatorId(0)
8245
                                ->setUserId($parent_data['user_id'])
8246
                                ->setAllowTextAssignment($parent_data['allow_text_assignment'])
8247
                                ->setSession($session);
8248
8249
                            $publication->setDocumentId($parent_data['document_id']);
8250
8251
                            Database::getManager()->persist($publication);
8252
                            Database::getManager()->flush();
8253
                            $id = $publication->getIid();
8254
                            // Folder created
8255
                            api_item_property_update(
8256
                                $course_info,
8257
                                'work',
8258
                                $id,
8259
                                'DirectoryCreated',
8260
                                api_get_user_id(),
8261
                                null,
8262
                                null,
8263
                                null,
8264
                                null,
8265
                                $new_session_id
8266
                            );
8267
                            $new_parent_id = $id;
8268
                            if (!isset($result_message[$TBL_STUDENT_PUBLICATION.' - new folder created called: '.$created_dir])) {
8269
                                $result_message[$TBL_STUDENT_PUBLICATION.' - new folder created called: '.$created_dir] = 0;
8270
                            }
8271
                            $result_message[$TBL_STUDENT_PUBLICATION.' - new folder created called: '.$created_dir]++;
8272
                        }
8273
                    }
8274
8275
                    // Creating student_publication_assignment if exists
8276
                    $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION_ASSIGNMENT
8277
                            WHERE publication_id = $parent_id AND c_id = $course_id";
8278
                    if ($debug) {
8279
                        var_dump($sql);
8280
                    }
8281
                    $rest_select = Database::query($sql);
8282
                    if (Database::num_rows($rest_select) > 0) {
8283
                        if ($update_database && $new_parent_id) {
8284
                            $assignment_data = Database::fetch_array($rest_select, 'ASSOC');
8285
                            $sql_add_publication = "INSERT INTO ".$TBL_STUDENT_PUBLICATION_ASSIGNMENT." SET
8286
                                c_id = '$course_id',
8287
                                expires_on = '".$assignment_data['expires_on']."',
8288
                                ends_on = '".$assignment_data['ends_on']."',
8289
                                add_to_calendar = '".$assignment_data['add_to_calendar']."',
8290
                                enable_qualification = '".$assignment_data['enable_qualification']."',
8291
                                publication_id = '".$new_parent_id."'";
8292
                            if ($debug) {
8293
                                echo $sql_add_publication;
8294
                            }
8295
                            Database::query($sql_add_publication);
8296
                            $id = (int) Database::insert_id();
8297
                            if ($id) {
8298
                                $sql_update = "UPDATE $TBL_STUDENT_PUBLICATION
8299
                                    SET  has_properties = '".$id."',
8300
                                        view_properties = '1'
8301
                                    WHERE id = ".$new_parent_id;
8302
                                if ($debug) {
8303
                                    echo $sql_update;
8304
                                }
8305
                                Database::query($sql_update);
8306
                                if (!isset($result_message[$TBL_STUDENT_PUBLICATION_ASSIGNMENT])) {
8307
                                    $result_message[$TBL_STUDENT_PUBLICATION_ASSIGNMENT] = 0;
8308
                                }
8309
                                $result_message[$TBL_STUDENT_PUBLICATION_ASSIGNMENT]++;
8310
                            }
8311
                        }
8312
                    }
8313
8314
                    $doc_url = $data['url'];
8315
                    $new_url = str_replace($parent_data['url'], $created_dir, $doc_url);
8316
8317
                    if ($update_database) {
8318
                        // Creating a new work
8319
                        $data['sent_date'] = new DateTime($data['sent_date'], new DateTimeZone('UTC'));
8320
                        $data['post_group_id'] = (int) $data['post_group_id'];
8321
                        $publication = new \Chamilo\CourseBundle\Entity\CStudentPublication();
8322
                        $publication
8323
                            ->setUrl($new_url)
8324
                            ->setCId($course_id)
8325
                            ->setTitle($data['title'])
8326
                            ->setDescription($data['description'].' file moved')
8327
                            ->setActive($data['active'])
8328
                            ->setAccepted($data['accepted'])
8329
                            ->setPostGroupId($data['post_group_id'])
8330
                            ->setSentDate($data['sent_date'])
8331
                            ->setParentId($new_parent_id)
8332
                            ->setWeight($data['weight'])
8333
                            ->setHasProperties(0)
8334
                            ->setWeight($data['weight'])
8335
                            ->setContainsFile($data['contains_file'])
8336
                            ->setSession($session)
8337
                            ->setUserId($data['user_id'])
8338
                            ->setFiletype('file')
8339
                            ->setDocumentId(0)
8340
                        ;
8341
8342
                        $em->persist($publication);
8343
                        $em->flush();
8344
8345
                        $id = $publication->getIid();
8346
                        api_item_property_update(
8347
                            $course_info,
8348
                            'work',
8349
                            $id,
8350
                            'DocumentAdded',
8351
                            $user_id,
8352
                            null,
8353
                            null,
8354
                            null,
8355
                            null,
8356
                            $new_session_id
8357
                        );
8358
                        if (!isset($result_message[$TBL_STUDENT_PUBLICATION])) {
8359
                            $result_message[$TBL_STUDENT_PUBLICATION] = 0;
8360
                        }
8361
                        $result_message[$TBL_STUDENT_PUBLICATION]++;
8362
                        $full_file_name = $course_dir.'/'.$doc_url;
8363
                        $new_file = $course_dir.'/'.$new_url;
8364
8365
                        if (file_exists($full_file_name)) {
8366
                            // deleting old assignment
8367
                            $result = copy($full_file_name, $new_file);
8368
                            if ($result) {
8369
                                unlink($full_file_name);
8370
                                if (isset($data['id'])) {
8371
                                    $sql = "DELETE FROM $TBL_STUDENT_PUBLICATION WHERE id= ".$data['id'];
8372
                                    if ($debug) {
8373
                                        var_dump($sql);
8374
                                    }
8375
                                    Database::query($sql);
8376
                                }
8377
                                api_item_property_update(
8378
                                    $course_info,
8379
                                    'work',
8380
                                    $data['id'],
8381
                                    'DocumentDeleted',
8382
                                    api_get_user_id()
8383
                                );
8384
                            }
8385
                        }
8386
                    }
8387
                }
8388
            }
8389
        }
8390
8391
        // 9. Survey   Pending
8392
        // 10. Dropbox - not neccesary to move categories (no presence of session_id)
8393
        $sql = "SELECT id FROM $TBL_DROPBOX_FILE
8394
                WHERE uploader_id = $user_id AND session_id = $origin_session_id AND c_id = $course_id";
8395
        if ($debug) {
8396
            var_dump($sql);
8397
        }
8398
        $res = Database::query($sql);
8399
        while ($row = Database::fetch_array($res, 'ASSOC')) {
8400
            $id = (int) $row['id'];
8401
            if ($update_database) {
8402
                $sql = "UPDATE $TBL_DROPBOX_FILE SET session_id = $new_session_id WHERE c_id = $course_id AND id = $id";
8403
                if ($debug) {
8404
                    var_dump($sql);
8405
                }
8406
                Database::query($sql);
8407
                if ($debug) {
8408
                    var_dump($res);
8409
                }
8410
8411
                $sql = "UPDATE $TBL_DROPBOX_POST SET session_id = $new_session_id WHERE file_id = $id";
8412
                if ($debug) {
8413
                    var_dump($sql);
8414
                }
8415
                Database::query($sql);
8416
                if ($debug) {
8417
                    var_dump($res);
8418
                }
8419
                if (!isset($result_message[$TBL_DROPBOX_FILE])) {
8420
                    $result_message[$TBL_DROPBOX_FILE] = 0;
8421
                }
8422
                $result_message[$TBL_DROPBOX_FILE]++;
8423
            }
8424
        }
8425
8426
        // 11. Notebook
8427
        /*$sql = "SELECT notebook_id FROM $TBL_NOTEBOOK
8428
                WHERE
8429
                    user_id = $user_id AND
8430
                    session_id = $origin_session_id AND
8431
                    course = '$origin_course_code' AND
8432
                    c_id = $course_id";
8433
        if ($debug) {
8434
            var_dump($sql);
8435
        }
8436
        $res = Database::query($sql);
8437
        while ($row = Database::fetch_array($res, 'ASSOC')) {
8438
            $id = $row['notebook_id'];
8439
            if ($update_database) {
8440
                $sql = "UPDATE $TBL_NOTEBOOK
8441
                        SET session_id = $new_session_id
8442
                        WHERE c_id = $course_id AND notebook_id = $id";
8443
                if ($debug) {
8444
                    var_dump($sql);
8445
                }
8446
                $res = Database::query($sql);
8447
                if ($debug) {
8448
                    var_dump($res);
8449
                }
8450
            }
8451
        }*/
8452
8453
        if ($update_database) {
8454
            echo Display::return_message(get_lang('StatsMoved'));
8455
            if (is_array($result_message)) {
8456
                foreach ($result_message as $table => $times) {
8457
                    echo 'Table '.$table.' - '.$times.' records updated <br />';
8458
                }
8459
            }
8460
        } else {
8461
            echo '<p class="lead">'.get_lang('UserInformationOfThisCourse').'</p>';
8462
            echo '<table class="table" width="100%">';
8463
            echo '<tr>';
8464
            echo '<th width="50%" valign="top">';
8465
            if ($origin_session_id == 0) {
8466
                echo '<p><strong>'.get_lang('OriginCourse').'</strong></p>';
8467
            } else {
8468
                echo '<p><strong>'.get_lang('OriginSession').' #'.$origin_session_id.'</strong></p>';
8469
            }
8470
            echo '</th>';
8471
            echo '<th width="50%" valign="top">';
8472
            if ($new_session_id == 0) {
8473
                echo '<p><strong>'.get_lang('DestinyCourse').'</strong></p>';
8474
            } else {
8475
                echo '<p><strong>'.get_lang('DestinySession').' #'.$new_session_id.'</strong></p>';
8476
            }
8477
            echo '</th>';
8478
            echo '</tr>';
8479
            echo '<tr>';
8480
            echo '<td>';
8481
            self::compareUserData($result_message);
8482
            echo '</td>';
8483
            echo '<td>';
8484
            self::compareUserData($result_message_compare);
8485
            echo '</td>';
8486
            echo '</tr>';
8487
            echo '</table>';
8488
        }
8489
    }
8490
8491
    public static function compareUserData($result_message)
8492
    {
8493
        foreach ($result_message as $table => $data) {
8494
            $title = $table;
8495
            if ($table === 'TRACK_E_EXERCISES') {
8496
                $title = get_lang('Exercises');
8497
            } elseif ($table === 'TRACK_E_EXERCISES_IN_LP') {
8498
                $title = get_lang('ExercisesInLp');
8499
            } elseif ($table === 'LP_VIEW') {
8500
                $title = get_lang('LearningPaths');
8501
            }
8502
            echo '<h3 class="page-header">'.get_lang($title).' </h3>';
8503
8504
            if (is_array($data)) {
8505
                echo '<ul>';
8506
                foreach ($data as $id => $item) {
8507
                    echo '<li>';
8508
                    if ($table === 'TRACK_E_EXERCISES' || $table === 'TRACK_E_EXERCISES_IN_LP') {
8509
                        echo '<p class="lead">';
8510
                        echo get_lang('Exercise').' #'.$item['exe_exo_id'];
8511
                        if (!empty($item['orig_lp_id'])) {
8512
                            echo PHP_EOL.'<small>(';
8513
                            echo get_lang('LearningPath').' #'.$item['orig_lp_id'];
8514
                            echo ')</small>';
8515
                        }
8516
                        echo '</p>';
8517
                        echo "<p><strong>".get_lang('Attempt')." #$id</strong></p>";
8518
                        // Process data.
8519
                        $array = [
8520
                            'exe_date' => get_lang('Date'),
8521
                            'exe_result' => get_lang('Score'),
8522
                            'exe_weighting' => get_lang('Weighting'),
8523
                        ];
8524
                        foreach ($item as $key => $value) {
8525
                            if (in_array($key, array_keys($array))) {
8526
                                $key = $array[$key];
8527
                                echo "<p>$key =  $value </p>";
8528
                            }
8529
                        }
8530
                    } else {
8531
                        echo '<p class="lead">'.get_lang('Id')." #$id</p>";
8532
                        // process data
8533
                        foreach ($item as $key => $value) {
8534
                            echo "<p>$key = $value</p>";
8535
                        }
8536
                    }
8537
                    echo '</li>';
8538
                }
8539
                echo '</ul>';
8540
            } else {
8541
                echo '<p>'.get_lang('NoResults').'</p>';
8542
            }
8543
        }
8544
    }
8545
8546
    public static function updateUserLastLogin($userId)
8547
    {
8548
        $tblTrackLogin = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
8549
        $sql = "SELECT login_id, login_date
8550
            FROM $tblTrackLogin
8551
            WHERE
8552
                login_user_id='".$userId."'
8553
            ORDER BY login_date DESC
8554
            LIMIT 0,1";
8555
8556
        $qLastConnection = Database::query($sql);
8557
        if (Database::num_rows($qLastConnection) > 0) {
8558
            $now = api_get_utc_datetime();
8559
            $iIdLastConnection = Database::result($qLastConnection, 0, 'login_id');
8560
8561
            // is the latest logout_date still relevant?
8562
            $sql = "SELECT logout_date FROM $tblTrackLogin
8563
                WHERE login_id = $iIdLastConnection";
8564
            $qLogoutDate = Database::query($sql);
8565
            $resLogoutDate = convert_sql_date(Database::result($qLogoutDate, 0, 'logout_date'));
8566
            $lifeTime = api_get_configuration_value('session_lifetime');
8567
8568
            if ($resLogoutDate < time() - $lifeTime) {
8569
                // it isn't, we should create a fresh entry
8570
                Event::eventLogin($userId);
8571
            // now that it's created, we can get its ID and carry on
8572
            } else {
8573
                $sql = "UPDATE $tblTrackLogin SET logout_date = '$now'
8574
                    WHERE login_id = '$iIdLastConnection'";
8575
                Database::query($sql);
8576
            }
8577
8578
            $tableUser = Database::get_main_table(TABLE_MAIN_USER);
8579
            $sql = "UPDATE $tableUser SET last_login = '$now'
8580
                WHERE user_id = ".$userId;
8581
            Database::query($sql);
8582
        }
8583
    }
8584
8585
    /**
8586
     * Get results of user in exercises by dates.
8587
     *
8588
     * @return array
8589
     */
8590
    public static function getUserTrackExerciseByDates(
8591
        int $userId,
8592
        int $courseId,
8593
        string $startDate,
8594
        string $endDate
8595
    ) {
8596
        $startDate = Database::escape_string($startDate);
8597
        $endDate = Database::escape_string($endDate);
8598
8599
        $tblTrackExercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
8600
        $tblQuiz = Database::get_course_table(TABLE_QUIZ_TEST);
8601
        $sql = "SELECT
8602
                    te.exe_exo_id,
8603
                    q.title,
8604
                    MAX((te.exe_result/te.exe_weighting) * 100) as score
8605
                FROM
8606
                    $tblTrackExercises te
8607
                INNER JOIN
8608
                    $tblQuiz q ON (q.iid = te.exe_exo_id AND q.c_id = te.c_id)
8609
                WHERE
8610
                    te.exe_user_id = $userId AND
8611
                    te.c_id = $courseId AND
8612
                    te.status = '' AND
8613
                    te.start_date BETWEEN '$startDate 00:00:00' AND '$endDate 23:59:59'
8614
                GROUP BY
8615
                    te.exe_exo_id,
8616
                    q.title
8617
                ";
8618
        $rs = Database::query($sql);
8619
        $result = [];
8620
        if (Database::num_rows($rs) > 0) {
8621
            while ($row = Database::fetch_array($rs)) {
8622
                $result[] = $row;
8623
            }
8624
        }
8625
8626
        return $result;
8627
    }
8628
8629
    /**
8630
     * Generates a report based on the specified type and selected users within a date range.
8631
     *
8632
     * @param string $reportType       The type of report to generate ('time_report' or 'billing_report').
8633
     * @param array  $selectedUserList An array of user IDs to include in the report.
8634
     * @param string $startDate        The start date for the report in 'Y-m-d H:i:s' format.
8635
     * @param string $endDate          The end date for the report in 'Y-m-d H:i:s' format.
8636
     *
8637
     * @throws Exception Throws an exception if an invalid report type is provided.
8638
     *
8639
     * @return array An array containing the report data. The first element is an array of headers,
8640
     *               followed by the rows of data.
8641
     */
8642
    public static function generateReport(string $reportType, array $selectedUserList, string $startDate, string $endDate): array
8643
    {
8644
        if (empty($selectedUserList)) {
8645
            return ['headers' => [], 'rows' => []];
8646
        }
8647
8648
        $tblTrackCourseAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
8649
        $tblLpView = Database::get_course_table(TABLE_LP_VIEW);
8650
        $tblLpItemView = Database::get_course_table(TABLE_LP_ITEM_VIEW);
8651
        $tblLpItem = Database::get_course_table(TABLE_LP_ITEM);
8652
        $tblLp = Database::get_course_table(TABLE_LP_MAIN);
8653
8654
        switch ($reportType) {
8655
            case 'time_report':
8656
                $headers = [
8657
                    get_lang('LastName'),
8658
                    get_lang('FirstName'),
8659
                    get_lang('SessionName'),
8660
                    get_lang('CourseName'),
8661
                    get_lang('StartingAccessDate'),
8662
                    get_lang('EndingAccessDate'),
8663
                    get_lang('TimeSpent'),
8664
                ];
8665
                $sql = "SELECT user_id, session_id, c_id, login_course_date, logout_course_date, (UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) AS time
8666
                    FROM $tblTrackCourseAccess
8667
                    WHERE login_course_date >= '".api_get_utc_datetime($startDate.' 00:00:00')."'
8668
                      AND login_course_date <= '".api_get_utc_datetime($endDate.' 23:59:59')."'
8669
                      AND logout_course_date >= '".api_get_utc_datetime($startDate.' 00:00:00')."'
8670
                      AND logout_course_date <= '".api_get_utc_datetime($endDate.' 23:59:59')."'
8671
                      AND user_id IN (".implode(',', $selectedUserList).")
8672
                    ORDER BY user_id, login_course_date";
8673
                break;
8674
8675
            case 'billing_report':
8676
                $extraFieldVariable = api_get_configuration_value('billing_report_lp_extra_field');
8677
                $extraField = (new ExtraField('lp'))->get_handler_field_info_by_field_variable($extraFieldVariable);
8678
                $headers = [
8679
                    get_lang('LastName'),
8680
                    get_lang('FirstName'),
8681
                    get_lang('SessionName'),
8682
                    get_lang('CourseName'),
8683
                    get_lang('LearningpathName'),
8684
                    get_lang('ValidationDate'),
8685
                    $extraField['display_text'],
8686
                ];
8687
                $sql = "SELECT lv.user_id, lv.session_id, lv.c_id, lv.lp_id, liv.start_time, l.name AS lp_name
8688
                    FROM $tblLpView lv
8689
                    INNER JOIN $tblLpItemView liv ON lv.iid = liv.lp_view_id
8690
                    INNER JOIN $tblLpItem li ON li.iid = liv.lp_item_id
8691
                    INNER JOIN $tblLp l ON l.id = li.lp_id
8692
                    WHERE lv.user_id IN (".implode(',', $selectedUserList).")
8693
                      AND liv.start_time >= UNIX_TIMESTAMP('".api_get_utc_datetime($startDate.' 00:00:00')."')
8694
                      AND liv.start_time <= UNIX_TIMESTAMP('".api_get_utc_datetime($endDate.' 23:59:59')."')
8695
                      AND lv.progress = 100
8696
                      AND li.item_type = '".TOOL_LP_FINAL_ITEM."'
8697
                    ORDER BY lv.user_id, liv.start_time";
8698
                break;
8699
8700
            default:
8701
                throw new Exception('Invalid report type');
8702
        }
8703
8704
        $result = Database::query($sql);
8705
        $rows = [];
8706
8707
        while ($row = Database::fetch_array($result, 'ASSOC')) {
8708
            $user = api_get_user_info($row['user_id']);
8709
            $session = api_get_session_info($row['session_id']);
8710
            $course = api_get_course_info_by_id($row['c_id']);
8711
8712
            if ($reportType == 'time_report') {
8713
                $rows[] = [
8714
                    $user['lastname'],
8715
                    $user['firstname'],
8716
                    $session['name'],
8717
                    $course['title'],
8718
                    api_get_local_time($row['login_course_date']),
8719
                    api_get_local_time($row['logout_course_date']),
8720
                    gmdate('H:i:s', $row['time']),
8721
                ];
8722
            } elseif ($reportType == 'billing_report') {
8723
                $extraFieldValue = (new ExtraFieldValue('lp'))->get_values_by_handler_and_field_variable($row['lp_id'], $extraFieldVariable);
8724
                $rows[] = [
8725
                    $user['lastname'],
8726
                    $user['firstname'],
8727
                    $session['name'],
8728
                    $course['title'],
8729
                    $row['lp_name'],
8730
                    api_get_local_time(date('Y-m-d H:i:s', $row['start_time'])),
8731
                    $extraFieldValue['value'] ?? '',
8732
                ];
8733
            }
8734
        }
8735
8736
        return ['headers' => $headers, 'rows' => $rows];
8737
    }
8738
8739
    private static function generateQuizzesTable(array $courseInfo, int $sessionId = 0): string
8740
    {
8741
        if (empty($sessionId)) {
8742
            $userList = CourseManager::get_user_list_from_course_code(
8743
                $courseInfo['code'],
8744
                $sessionId,
8745
                null,
8746
                null,
8747
                STUDENT
8748
            );
8749
        } else {
8750
            $userList = CourseManager::get_user_list_from_course_code($courseInfo['code'], $sessionId, null, null, 0);
8751
        }
8752
8753
        $active = 3;
8754
        if (true === api_get_configuration_value('tracking_my_progress_show_deleted_exercises')) {
8755
            $active = 2;
8756
        }
8757
8758
        $exerciseList = ExerciseLib::get_all_exercises($courseInfo, $sessionId, false, null, false, $active);
8759
8760
        if (empty($exerciseList)) {
8761
            return Display::return_message(get_lang('NoEx'));
8762
        }
8763
8764
        $toGraphExerciseResult = [];
8765
8766
        $quizzesTable = new SortableTableFromArray([], 0, 0, 'quizzes');
8767
        $quizzesTable->setHeaders(
8768
            [
8769
                get_lang('Title'),
8770
                get_lang('InLp'),
8771
                get_lang('Attempts'),
8772
                get_lang('BestAttempt'),
8773
                get_lang('Ranking'),
8774
                get_lang('BestResultInCourse'),
8775
                get_lang('Statistics').Display::return_icon('info3.gif', get_lang('OnlyBestResultsPerStudent')),
8776
            ]
8777
        );
8778
8779
        $webCodePath = api_get_path(WEB_CODE_PATH);
8780
8781
        foreach ($exerciseList as $exercices) {
8782
            $objExercise = new Exercise($courseInfo['real_id']);
8783
            $objExercise->read($exercices['iid']);
8784
            $visibleReturn = $objExercise->is_visible();
8785
8786
            // Getting count of attempts by user
8787
            $attempts = Event::count_exercise_attempts_by_user(
8788
                api_get_user_id(),
8789
                $exercices['iid'],
8790
                $courseInfo['real_id'],
8791
                $sessionId,
8792
                false
8793
            );
8794
8795
            $url = $webCodePath.'exercise/overview.php?'
8796
                .http_build_query(
8797
                    ['cidReq' => $courseInfo['code'], 'id_session' => $sessionId, 'exerciseId' => $exercices['iid']]
8798
                );
8799
8800
            if ($visibleReturn['value'] == true) {
8801
                $exercices['title'] = Display::url(
8802
                    $exercices['title'],
8803
                    $url,
8804
                    ['target' => SESSION_LINK_TARGET]
8805
                );
8806
            } elseif ($exercices['active'] == -1) {
8807
                $exercices['title'] = sprintf(get_lang('XParenthesisDeleted'), $exercices['title']);
8808
            }
8809
8810
            $lpList = Exercise::getLpListFromExercise($exercices['iid'], $courseInfo['real_id']);
8811
            $inLp = !empty($lpList) ? get_lang('Yes') : get_lang('No');
8812
8813
            $quizData = [
8814
                $exercices['title'],
8815
                $inLp,
8816
                $attempts,
8817
                '-',
8818
                '-',
8819
                '-',
8820
                '-',
8821
            ];
8822
8823
            // Exercise configuration show results or show only score
8824
            if (!in_array($exercices['results_disabled'], [0, 2])
8825
                || empty($attempts)
8826
            ) {
8827
                $quizzesTable->addRow($quizData);
8828
8829
                continue;
8830
            }
8831
8832
            // For graphics
8833
            $bestExerciseAttempts = Event::get_best_exercise_results_by_user(
8834
                $exercices['iid'],
8835
                $courseInfo['real_id'],
8836
                $sessionId,
8837
                0,
8838
                false
8839
            );
8840
8841
            $toGraphExerciseResult[$exercices['iid']] = [
8842
                'title' => $exercices['title'],
8843
                'data' => $bestExerciseAttempts,
8844
            ];
8845
8846
            // Getting best results
8847
            $bestScoreData = ExerciseLib::get_best_attempt_in_course(
8848
                $exercices['iid'],
8849
                $courseInfo['real_id'],
8850
                $sessionId,
8851
                false
8852
            );
8853
8854
            if (!empty($bestScoreData)) {
8855
                $quizData[6] = ExerciseLib::show_score(
8856
                    $bestScoreData['exe_result'],
8857
                    $bestScoreData['exe_weighting']
8858
                );
8859
            }
8860
8861
            $exerciseAttempt = ExerciseLib::get_best_attempt_by_user(
8862
                api_get_user_id(),
8863
                $exercices['iid'],
8864
                $courseInfo['real_id'],
8865
                $sessionId,
8866
                false
8867
            );
8868
8869
            if (!empty($exerciseAttempt)) {
8870
                // Always getting the BEST attempt
8871
                $score = $exerciseAttempt['exe_result'];
8872
                $weighting = $exerciseAttempt['exe_weighting'];
8873
                $exeId = $exerciseAttempt['exe_id'];
8874
8875
                $latestAttemptUrl = $webCodePath.'exercise/result.php?'
8876
                    .http_build_query(
8877
                        [
8878
                            'id' => $exeId,
8879
                            'cidReq' => $courseInfo['code'],
8880
                            'show_headers' => 1,
8881
                            'id_session' => $sessionId,
8882
                        ]
8883
                    );
8884
8885
                $quizData[4] = Display::url(
8886
                    ExerciseLib::show_score($score, $weighting),
8887
                    $latestAttemptUrl
8888
                );
8889
8890
                $myScore = !empty($weighting) && intval($weighting) != 0 ? $score / $weighting : 0;
8891
8892
                // @todo this function slows the page
8893
                if (is_int($userList)) {
8894
                    $userList = [$userList];
8895
                }
8896
8897
                $quizData[5] = ExerciseLib::get_exercise_result_ranking(
8898
                    $myScore,
8899
                    $exeId,
8900
                    $exercices['iid'],
8901
                    $courseInfo['code'],
8902
                    $sessionId,
8903
                    $userList,
8904
                    true,
8905
                    false
8906
                );
8907
                $graph = self::generate_exercise_result_thumbnail_graph($toGraphExerciseResult[$exercices['iid']]);
8908
                $normalGraph = self::generate_exercise_result_graph($toGraphExerciseResult[$exercices['iid']]);
8909
8910
                $quizData[7] = Display::url(
8911
                    Display::img($graph, '', [], false),
8912
                    $normalGraph,
8913
                    ['id' => $exercices['iid'], 'class' => 'expand-image']
8914
                );
8915
            }
8916
8917
            $quizzesTable->addRow($quizData);
8918
        }
8919
8920
        return Display::page_subheader2(get_lang('Exercises'))
8921
            .Display::div(
8922
                $quizzesTable->toHtml(),
8923
                ['class' => 'table-responsive']
8924
            );
8925
    }
8926
8927
    private static function generateLearningPathsTable(
8928
        User $user,
8929
        array $courseInfo,
8930
        int $sessionId = 0,
8931
        bool $isAllowedToEdit = true
8932
    ): string {
8933
        $html = [];
8934
8935
        $columnHeaders = [
8936
            'lp' => get_lang('LearningPath'),
8937
            'time' => get_lang('LatencyTimeSpent'),
8938
            'progress' => get_lang('Progress'),
8939
            'score' => get_lang('Score'),
8940
            'best_score' => get_lang('BestScore'),
8941
            'last_connection' => get_lang('LastConnexion'),
8942
        ];
8943
8944
        $trackingColumns = api_get_configuration_value('tracking_columns');
8945
8946
        if (isset($trackingColumns['my_progress_lp'])) {
8947
            $columnHeaders = array_filter(
8948
                $columnHeaders,
8949
                function ($columHeader, $key) use ($trackingColumns) {
8950
                    if (!isset($trackingColumns['my_progress_lp'][$key])
8951
                        || $trackingColumns['my_progress_lp'][$key] == false
8952
                    ) {
8953
                        return false;
8954
                    }
8955
8956
                    return true;
8957
                },
8958
                ARRAY_FILTER_USE_BOTH
8959
            );
8960
        }
8961
8962
        if (true === api_get_configuration_value('student_follow_page_add_LP_subscription_info')) {
8963
            $columnHeaders['student_follow_page_add_LP_subscription_info'] = get_lang('Unlock');
8964
        }
8965
8966
        if (true === api_get_configuration_value('student_follow_page_add_LP_acquisition_info')) {
8967
            $columnHeaders['student_follow_page_add_LP_acquisition_info'] = get_lang('Acquisition');
8968
        }
8969
8970
        $addLpInvisibleCheckbox = api_get_configuration_value('student_follow_page_add_LP_invisible_checkbox');
8971
        $includeNotsubscribedLp = api_get_configuration_value('student_follow_page_include_not_subscribed_lp_students');
8972
8973
        $columnHeadersKeys = array_keys($columnHeaders);
8974
8975
        $categories = learnpath::getCategories($courseInfo['real_id'], true);
8976
        $countCategories = count($categories);
8977
8978
        $webCodePath = api_get_path(WEB_CODE_PATH);
8979
8980
        /** @var CLpCategory $category */
8981
        foreach ($categories as $category) {
8982
            // LP table results
8983
            $objLearnpathList = new LearnpathList(
8984
                $user->getId(),
8985
                $courseInfo,
8986
                $sessionId,
8987
                null,
8988
                true,
8989
                $category->getId(),
8990
                false,
8991
                false,
8992
                $includeNotsubscribedLp === false
8993
            );
8994
            $lpList = $objLearnpathList->get_flat_list();
8995
8996
            $learningpathsTable = new SortableTableFromArray([], 0, 0, 'learningpaths');
8997
            $learningpathsTable->setHeaders($columnHeaders);
8998
8999
            foreach ($lpList as $lpId => $learnpath) {
9000
                $learningpathData = [];
9001
9002
                if (!$learnpath['lp_visibility']) {
9003
                    continue;
9004
                }
9005
9006
                if ($addLpInvisibleCheckbox) {
9007
                    if (!StudentFollowPage::isViewVisible($lpId, $user->getId(), $courseInfo['real_id'], $sessionId)) {
9008
                        continue;
9009
                    }
9010
                }
9011
9012
                $url = $webCodePath.'lp/lp_controller.php?'
9013
                    .http_build_query(
9014
                        ['cidReq' => $courseInfo['code'], 'id_session' => $sessionId, 'lp_id' => $lpId, 'action' => 'view']
9015
                    );
9016
9017
                if (in_array('lp', $columnHeadersKeys)) {
9018
                    if ($learnpath['lp_visibility'] == 0) {
9019
                        $learningpathData[] = $learnpath['lp_name'];
9020
                    } else {
9021
                        $learningpathData[] = Display::url(
9022
                            $learnpath['lp_name'],
9023
                            $url,
9024
                            ['target' => SESSION_LINK_TARGET]
9025
                        );
9026
                    }
9027
                }
9028
9029
                if (in_array('time', $columnHeadersKeys)) {
9030
                    $time_spent_in_lp = self::get_time_spent_in_lp(
9031
                        $user->getId(),
9032
                        $courseInfo['code'],
9033
                        [$lpId],
9034
                        $sessionId
9035
                    );
9036
9037
                    $learningpathData[] = api_time_to_hms($time_spent_in_lp);
9038
                }
9039
9040
                if (in_array('progress', $columnHeadersKeys)) {
9041
                    $progress = self::get_avg_student_progress(
9042
                        $user->getId(),
9043
                        $courseInfo['code'],
9044
                        [$lpId],
9045
                        $sessionId
9046
                    );
9047
9048
                    if (is_numeric($progress)) {
9049
                        $progress = sprintf(get_lang('XPercent'), $progress);
9050
                    }
9051
9052
                    $learningpathData[] = $progress;
9053
                }
9054
9055
                if (in_array('score', $columnHeadersKeys)) {
9056
                    $percentage_score = self::get_avg_student_score(
9057
                        $user->getId(),
9058
                        $courseInfo['code'],
9059
                        [$lpId],
9060
                        $sessionId
9061
                    );
9062
9063
                    if (is_numeric($percentage_score)) {
9064
                        $percentage_score = sprintf(get_lang('XPercent'), $percentage_score);
9065
                    } else {
9066
                        $percentage_score = sprintf(get_lang('XPercent'), 0);
9067
                    }
9068
9069
                    $learningpathData[] = $percentage_score;
9070
                }
9071
9072
                if (in_array('best_score', $columnHeadersKeys)) {
9073
                    $bestScore = self::get_avg_student_score(
9074
                        $user->getId(),
9075
                        $courseInfo['code'],
9076
                        [$lpId],
9077
                        $sessionId,
9078
                        false,
9079
                        false,
9080
                        true
9081
                    );
9082
9083
                    if (is_numeric($bestScore)) {
9084
                        $bestScore = sprintf(get_lang('XPercent'), $bestScore);
9085
                    } else {
9086
                        $bestScore = '-';
9087
                    }
9088
9089
                    $learningpathData[] = $bestScore;
9090
                }
9091
9092
                if (in_array('last_connection', $columnHeadersKeys)) {
9093
                    $lastConnectionInLp = self::get_last_connection_time_in_lp(
9094
                        $user->getId(),
9095
                        $courseInfo['code'],
9096
                        $lpId,
9097
                        $sessionId
9098
                    );
9099
9100
                    $lastConnection = '-';
9101
9102
                    if (!empty($lastConnectionInLp)) {
9103
                        $lastConnection = api_convert_and_format_date($lastConnectionInLp, DATE_TIME_FORMAT_LONG);
9104
                    }
9105
9106
                    $learningpathData[] = $lastConnection;
9107
                }
9108
9109
                if (in_array('student_follow_page_add_LP_subscription_info', $columnHeadersKeys)) {
9110
                    $learningpathData[] = StudentFollowPage::getLpSubscription(
9111
                        $learnpath,
9112
                        $user->getId(),
9113
                        $courseInfo['real_id'],
9114
                        $sessionId,
9115
                        $isAllowedToEdit
9116
                    );
9117
                }
9118
9119
                if (in_array('student_follow_page_add_LP_acquisition_info', $columnHeadersKeys)) {
9120
                    $learningpathData[] = StudentFollowPage::getLpAcquisition(
9121
                        $learnpath,
9122
                        $user->getId(),
9123
                        $courseInfo['real_id'],
9124
                        $sessionId
9125
                    );
9126
                }
9127
9128
                $learningpathsTable->addRow($learningpathData);
9129
            }
9130
9131
            if ($learningpathsTable->getRowCount() < 2) {
9132
                continue;
9133
            }
9134
9135
            if ($countCategories > 1) {
9136
                $html[] = Display::tag('h5', $category->getName());
9137
            }
9138
9139
            $html[] = Display::div(
9140
                $learningpathsTable->toHtml(),
9141
                ['class' => 'table-responsive']
9142
            );
9143
        }
9144
9145
        return implode(PHP_EOL, $html);
9146
    }
9147
9148
    private static function countSubscribedCoursesPerUser()
9149
    {
9150
    }
9151
}
9152