Passed
Push — 1.11.x ( 5b3cc8...a5e65d )
by Yannick
08:50
created

Tracking::getLastConnectionTimeInSessionCourseLp()   A

Complexity

Conditions 4
Paths 3

Size

Total Lines 32
Code Lines 17

Duplication

Lines 0
Ratio 0 %

Importance

Changes 1
Bugs 0 Features 0
Metric Value
eloc 17
c 1
b 0
f 0
dl 0
loc 32
rs 9.7
cc 4
nc 3
nop 3
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
                    $getCount
2398
                );
2399
            } else {
2400
                $studentList = UserManager::getUsersFollowedByUser(
2401
                    $userId,
2402
                    STUDENT,
2403
                    false,
2404
                    false,
2405
                    $getCount,
2406
                    null,
2407
                    null,
2408
                    null,
2409
                    null,
2410
                    null,
2411
                    null,
2412
                    COURSEMANAGER,
2413
                    null,
2414
                    $checkSessionVisibility
2415
                );
2416
            }
2417
2418
            if ($getCount) {
2419
                $studentCount = (int) $studentList;
2420
            } else {
2421
                $students = [];
2422
                if (is_array($studentList)) {
2423
                    foreach ($studentList as $studentData) {
2424
                        $students[] = $studentData['user_id'];
2425
                    }
2426
                }
2427
            }
2428
2429
            $studentBossesList = UserManager::getUsersFollowedByUser(
2430
                $userId,
2431
                STUDENT_BOSS,
2432
                false,
2433
                false,
2434
                $getCount,
2435
                null,
2436
                null,
2437
                null,
2438
                null,
2439
                null,
2440
                null,
2441
                COURSEMANAGER,
2442
                null,
2443
                $checkSessionVisibility
2444
            );
2445
2446
            if ($getCount) {
2447
                $studentBossCount = $studentBossesList;
2448
            } else {
2449
                $studentBosses = [];
2450
                if (is_array($studentBossesList)) {
2451
                    foreach ($studentBossesList as $studentBossData) {
2452
                        $studentBosses[] = $studentBossData['user_id'];
2453
                    }
2454
                }
2455
            }
2456
2457
            $teacherList = UserManager::getUsersFollowedByUser(
2458
                $userId,
2459
                COURSEMANAGER,
2460
                false,
2461
                false,
2462
                $getCount,
2463
                null,
2464
                null,
2465
                null,
2466
                null,
2467
                null,
2468
                null,
2469
                COURSEMANAGER,
2470
                null,
2471
                $checkSessionVisibility
2472
            );
2473
2474
            if ($getCount) {
2475
                $teachersCount = $teacherList;
2476
            } else {
2477
                $teachers = [];
2478
                foreach ($teacherList as $teacherData) {
2479
                    $teachers[] = $teacherData['user_id'];
2480
                }
2481
            }
2482
2483
            $humanResources = UserManager::getUsersFollowedByUser(
2484
                $userId,
2485
                DRH,
2486
                false,
2487
                false,
2488
                $getCount,
2489
                null,
2490
                null,
2491
                null,
2492
                null,
2493
                null,
2494
                null,
2495
                COURSEMANAGER,
2496
                null,
2497
                $checkSessionVisibility
2498
            );
2499
2500
            if ($getCount) {
2501
                $drhCount = $humanResources;
2502
            } else {
2503
                $humanResourcesList = [];
2504
                foreach ($humanResources as $item) {
2505
                    $humanResourcesList[] = $item['user_id'];
2506
                }
2507
            }
2508
2509
            $platformCourses = CourseManager::getCoursesFollowedByUser(
2510
                $userId,
2511
                COURSEMANAGER,
2512
                null,
2513
                null,
2514
                null,
2515
                null,
2516
                $getCount,
2517
                null,
2518
                null,
2519
                true
2520
            );
2521
2522
            if ($getCount) {
2523
                $assignedCourseCount = $platformCourses;
2524
            } else {
2525
                foreach ($platformCourses as $course) {
2526
                    $assignedCourses[$course['code']] = $course['code'];
2527
                }
2528
            }
2529
2530
            $platformCourses = CourseManager::getCoursesFollowedByUser(
2531
                $userId,
2532
                COURSEMANAGER,
2533
                null,
2534
                null,
2535
                null,
2536
                null,
2537
                $getCount
2538
            );
2539
2540
            if ($getCount) {
2541
                $courseCount = $platformCourses;
2542
            } else {
2543
                foreach ($platformCourses as $course) {
2544
                    $courses[$course['code']] = $course['code'];
2545
                }
2546
            }
2547
2548
            $sessions = SessionManager::getSessionsFollowedByUser(
2549
                $userId,
2550
                COURSEMANAGER,
2551
                null,
2552
                null,
2553
                false
2554
            );
2555
        }
2556
2557
        if ($getCount) {
2558
            return [
2559
                'drh' => $drhCount,
2560
                'teachers' => $teachersCount,
2561
                'student_count' => $studentCount,
2562
                'student_list' => $students,
2563
                'student_bosses' => $studentBossCount,
2564
                'courses' => $courseCount,
2565
                'session_count' => count($sessions),
2566
                'session_list' => $sessions,
2567
                'assigned_courses' => $assignedCourseCount,
2568
            ];
2569
        }
2570
2571
        return [
2572
            'drh' => $humanResourcesList,
2573
            'teachers' => $teachers,
2574
            'student_list' => $students,
2575
            'student_bosses' => $studentBosses,
2576
            'courses' => $courses,
2577
            'sessions' => $sessions,
2578
            'assigned_courses' => $assignedCourses,
2579
        ];
2580
    }
2581
2582
    /**
2583
     * Calculates the time spent on the platform by a user.
2584
     *
2585
     * @param int|array $userId
2586
     * @param string    $timeFilter       type of time filter: 'last_week' or 'custom'
2587
     * @param string    $start_date       start date date('Y-m-d H:i:s')
2588
     * @param string    $end_date         end date date('Y-m-d H:i:s')
2589
     * @param bool      $returnAllRecords
2590
     *
2591
     * @return int
2592
     */
2593
    public static function get_time_spent_on_the_platform(
2594
        $userId,
2595
        $timeFilter = 'last_7_days',
2596
        $start_date = null,
2597
        $end_date = null,
2598
        $returnAllRecords = false
2599
    ) {
2600
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2601
        $condition_time = '';
2602
2603
        if (is_array($userId)) {
2604
            $userList = array_map('intval', $userId);
2605
            $userCondition = " login_user_id IN ('".implode("','", $userList)."')";
2606
        } else {
2607
            $userId = (int) $userId;
2608
            $userCondition = " login_user_id = $userId ";
2609
        }
2610
2611
        $url_condition = null;
2612
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2613
        $url_table = null;
2614
        if (api_is_multiple_url_enabled()) {
2615
            $access_url_id = api_get_current_access_url_id();
2616
            $url_table = ", $tbl_url_rel_user as url_users";
2617
            $url_condition = " AND u.login_user_id = url_users.user_id AND access_url_id='$access_url_id'";
2618
        }
2619
2620
        if (empty($timeFilter)) {
2621
            $timeFilter = 'last_week';
2622
        }
2623
2624
        $today = new DateTime('now', new DateTimeZone('UTC'));
2625
2626
        switch ($timeFilter) {
2627
            case 'last_7_days':
2628
                $newDate = new DateTime('-7 day', new DateTimeZone('UTC'));
2629
                $condition_time = " AND (login_date >= '{$newDate->format('Y-m-d H:i:s')}'";
2630
                $condition_time .= " AND logout_date <= '{$today->format('Y-m-d H:i:s')}') ";
2631
                break;
2632
            case 'last_30_days':
2633
                $newDate = new DateTime('-30 days', new DateTimeZone('UTC'));
2634
                $condition_time = " AND (login_date >= '{$newDate->format('Y-m-d H:i:s')}'";
2635
                $condition_time .= "AND logout_date <= '{$today->format('Y-m-d H:i:s')}') ";
2636
                break;
2637
            case 'wide':
2638
                if (!empty($start_date) && !empty($end_date)) {
2639
                    $start_date = Database::escape_string($start_date);
2640
                    $end_date = Database::escape_string($end_date);
2641
                    $condition_time = ' AND (
2642
                        (login_date >= "'.$start_date.'" AND login_date <= "'.$end_date.'") OR
2643
                        (logout_date >= "'.$start_date.'" AND logout_date <= "'.$end_date.'") OR
2644
                        (login_date <= "'.$start_date.'" AND logout_date >= "'.$end_date.'")
2645
                    ) ';
2646
                }
2647
                break;
2648
            case 'custom':
2649
                if (!empty($start_date) && !empty($end_date)) {
2650
                    $start_date = Database::escape_string($start_date);
2651
                    $end_date = Database::escape_string($end_date);
2652
                    $condition_time = ' AND (login_date >= "'.$start_date.'" AND logout_date <= "'.$end_date.'" ) ';
2653
                }
2654
                break;
2655
        }
2656
2657
        if ($returnAllRecords) {
2658
            $sql = "SELECT login_date, logout_date, TIMESTAMPDIFF(SECOND, login_date, logout_date) diff
2659
                    FROM $tbl_track_login u $url_table
2660
                    WHERE $userCondition $condition_time $url_condition
2661
                    ORDER BY login_date";
2662
            $rs = Database::query($sql);
2663
2664
            return Database::store_result($rs, 'ASSOC');
2665
        }
2666
2667
        $sql = "SELECT SUM(TIMESTAMPDIFF(SECOND, login_date, logout_date)) diff
2668
    	        FROM $tbl_track_login u $url_table
2669
                WHERE $userCondition $condition_time $url_condition";
2670
        $rs = Database::query($sql);
2671
        $row = Database::fetch_array($rs, 'ASSOC');
2672
        $diff = $row['diff'];
2673
2674
        if ($diff >= 0) {
2675
            return $diff;
2676
        }
2677
2678
        return -1;
2679
    }
2680
2681
    /**
2682
     * @param string $startDate
2683
     * @param string $endDate
2684
     *
2685
     * @return int
2686
     */
2687
    public static function getTotalTimeSpentOnThePlatform(
2688
        $startDate = '',
2689
        $endDate = ''
2690
    ) {
2691
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2692
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2693
2694
        $url_table = null;
2695
        $url_condition = null;
2696
        if (api_is_multiple_url_enabled()) {
2697
            $access_url_id = api_get_current_access_url_id();
2698
            $url_table = ", ".$tbl_url_rel_user." as url_users";
2699
            $url_condition = " AND u.login_user_id = url_users.user_id AND access_url_id='$access_url_id'";
2700
        }
2701
2702
        if (!empty($startDate) && !empty($endDate)) {
2703
            $startDate = Database::escape_string($startDate);
2704
            $endDate = Database::escape_string($endDate);
2705
            $condition_time = ' (login_date >= "'.$startDate.'" AND logout_date <= "'.$endDate.'" ) ';
2706
        }
2707
        $sql = "SELECT SUM(TIMESTAMPDIFF(SECOND, login_date, logout_date)) diff
2708
    	        FROM $tbl_track_login u $url_table
2709
                WHERE $condition_time $url_condition";
2710
        $rs = Database::query($sql);
2711
        $row = Database::fetch_array($rs, 'ASSOC');
2712
        $diff = $row['diff'];
2713
2714
        if ($diff >= 0) {
2715
            return $diff;
2716
        }
2717
2718
        return -1;
2719
    }
2720
2721
    /**
2722
     * Return the total time spent in courses (no the total in platform).
2723
     *
2724
     * @return int
2725
     */
2726
    public static function getTotalTimeSpentInCourses(
2727
        string $dateFrom = '',
2728
        string $dateUntil = ''
2729
    ) {
2730
        $tableTrackLogin = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2731
        $tableUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2732
2733
        $tableUrl = null;
2734
        $urlCondition = null;
2735
        $conditionTime = null;
2736
        if (api_is_multiple_url_enabled()) {
2737
            $accessUrlId = api_get_current_access_url_id();
2738
            $tableUrl = ", ".$tableUrlRelUser." as url_users";
2739
            $urlCondition = " AND teca.user_id = url_users.user_id AND url_users.access_url_id = $accessUrlId";
2740
        }
2741
2742
        if (!empty($dateFrom) && !empty($dateUntil)) {
2743
            $dateFrom = Database::escape_string($dateFrom);
2744
            $dateUntil = Database::escape_string($dateUntil);
2745
            $conditionTime = " (teca.login_course_date >= '$dateFrom' AND teca.logout_course_date <= '$dateUntil' ) ";
2746
        }
2747
        $sql = "SELECT SUM(TIMESTAMPDIFF(HOUR, teca.login_course_date, teca.logout_course_date)) diff
2748
    	        FROM $tableTrackLogin teca $tableUrl
2749
                WHERE $conditionTime $urlCondition";
2750
2751
        $rs = Database::query($sql);
2752
        if (Database::num_rows($rs) < 1) {
2753
            return -1;
2754
        }
2755
        $row = Database::fetch_array($rs, 'ASSOC');
2756
        $diff = $row['diff'];
2757
2758
        if (isset($diff) && $diff >= 0) {
2759
            return $diff;
2760
        }
2761
2762
        return -1;
2763
    }
2764
2765
    /**
2766
     * Checks if the "lp_minimum_time" feature is available for the course.
2767
     *
2768
     * @param int $sessionId
2769
     * @param int $courseId
2770
     *
2771
     * @return bool
2772
     */
2773
    public static function minimumTimeAvailable($sessionId, $courseId)
2774
    {
2775
        if (!api_get_configuration_value('lp_minimum_time')) {
2776
            return false;
2777
        }
2778
2779
        if (!empty($sessionId)) {
2780
            $extraFieldValue = new ExtraFieldValue('session');
2781
            $value = $extraFieldValue->get_values_by_handler_and_field_variable($sessionId, 'new_tracking_system');
2782
2783
            if ($value && isset($value['value']) && 1 == $value['value']) {
2784
                return true;
2785
            }
2786
        } else {
2787
            if ($courseId) {
2788
                $extraFieldValue = new ExtraFieldValue('course');
2789
                $value = $extraFieldValue->get_values_by_handler_and_field_variable($courseId, 'new_tracking_system');
2790
                if ($value && isset($value['value']) && 1 == $value['value']) {
2791
                    return true;
2792
                }
2793
            }
2794
        }
2795
2796
        return false;
2797
    }
2798
2799
    /**
2800
     * Calculates the time spent on the course.
2801
     *
2802
     * @param int|array<int, int> $user_id
2803
     * @param int                 $courseId
2804
     * @param int                 $session_id
2805
     * @param string              $startDate  date string
2806
     * @param string              $endDate    date string
2807
     *
2808
     * @return int Time in seconds
2809
     */
2810
    public static function get_time_spent_on_the_course(
2811
        $user_id,
2812
        $courseId,
2813
        $session_id = 0,
2814
        $startDate = null,
2815
        $endDate = null
2816
    ) {
2817
        $courseId = (int) $courseId;
2818
2819
        if (empty($courseId) || empty($user_id)) {
2820
            return 0;
2821
        }
2822
2823
        if (self::minimumTimeAvailable($session_id, $courseId)) {
2824
            $courseTime = self::getCalculateTime($user_id, $courseId, $session_id);
2825
2826
            return isset($courseTime['total_time']) ? $courseTime['total_time'] : 0;
2827
        }
2828
2829
        $conditionUser = '';
2830
        $session_id = (int) $session_id;
2831
        if (is_array($user_id)) {
2832
            $user_id = array_map('intval', $user_id);
2833
            $conditionUser = " AND user_id IN (".implode(',', $user_id).") ";
2834
        } else {
2835
            if (!empty($user_id)) {
2836
                $user_id = (int) $user_id;
2837
                $conditionUser = " AND user_id = $user_id ";
2838
            }
2839
        }
2840
2841
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2842
        $sql = "SELECT
2843
                SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as nb_seconds
2844
                FROM $table
2845
                WHERE
2846
                    UNIX_TIMESTAMP(logout_course_date) > UNIX_TIMESTAMP(login_course_date) AND
2847
                    c_id = '$courseId' ";
2848
2849
        if (-1 != $session_id) {
2850
            $sql .= "AND session_id = '$session_id' ";
2851
        }
2852
2853
        if (!empty($startDate)) {
2854
            $startDate = api_get_utc_datetime($startDate, false, true);
2855
            $sql .= " AND login_course_date >= '".$startDate->format('Y-m-d 00:00:00')."' ";
2856
        }
2857
        if (!empty($endDate)) {
2858
            $endDate = api_get_utc_datetime($endDate, false, true);
2859
            $sql .= " AND login_course_date <= '".$endDate->format('Y-m-d 23:59:59')."' ";
2860
        }
2861
2862
        $sql .= $conditionUser;
2863
2864
        $rs = Database::query($sql);
2865
        $row = Database::fetch_array($rs);
2866
2867
        return $row['nb_seconds'];
2868
    }
2869
2870
    /**
2871
     * Get first connection date for a student.
2872
     *
2873
     * @param int $student_id
2874
     *
2875
     * @return string|bool Date format long without day or false if there are no connections
2876
     */
2877
    public static function get_first_connection_date($student_id, $dateFormat = DATE_FORMAT_SHORT)
2878
    {
2879
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2880
        $sql = 'SELECT login_date
2881
                FROM '.$table.'
2882
                WHERE login_user_id = '.intval($student_id).'
2883
                ORDER BY login_date ASC
2884
                LIMIT 0,1';
2885
2886
        $rs = Database::query($sql);
2887
        if (Database::num_rows($rs) > 0) {
2888
            if ($first_login_date = Database::result($rs, 0, 0)) {
2889
                return api_convert_and_format_date(
2890
                    $first_login_date,
2891
                    $dateFormat
2892
                );
2893
            }
2894
        }
2895
2896
        return false;
2897
    }
2898
2899
    /**
2900
     * Get las connection date for a student.
2901
     *
2902
     * @param int  $student_id
2903
     * @param bool $warning_message  Show a warning message (optional)
2904
     * @param bool $return_timestamp True for returning results in timestamp (optional)
2905
     *
2906
     * @return string|int|bool Date format long without day, false if there are no connections or
2907
     *                         timestamp if parameter $return_timestamp is true
2908
     */
2909
    public static function get_last_connection_date(
2910
        $student_id,
2911
        $warning_message = false,
2912
        $return_timestamp = false,
2913
        $dateFormat = DATE_FORMAT_SHORT
2914
    ) {
2915
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2916
        $sql = 'SELECT login_date
2917
                FROM '.$table.'
2918
                WHERE login_user_id = '.intval($student_id).'
2919
                ORDER BY login_date
2920
                DESC LIMIT 0,1';
2921
2922
        $rs = Database::query($sql);
2923
        if (Database::num_rows($rs) > 0) {
2924
            if ($last_login_date = Database::result($rs, 0, 0)) {
2925
                $last_login_date = api_get_local_time($last_login_date);
2926
                if ($return_timestamp) {
2927
                    return api_strtotime($last_login_date, 'UTC');
2928
                } else {
2929
                    if (!$warning_message) {
2930
                        return api_format_date($last_login_date, $dateFormat);
2931
                    } else {
2932
                        $timestamp = api_strtotime($last_login_date, 'UTC');
2933
                        $currentTimestamp = time();
2934
2935
                        // If the last connection is > than 7 days, the text is red
2936
                        // 604800 = 7 days in seconds
2937
                        if ($currentTimestamp - $timestamp > 604800) {
2938
                            return '<span style="color: #F00;">'.api_format_date($last_login_date, $dateFormat).'</span>';
2939
                        } else {
2940
                            return api_format_date($last_login_date, $dateFormat);
2941
                        }
2942
                    }
2943
                }
2944
            }
2945
        }
2946
2947
        return false;
2948
    }
2949
2950
    /**
2951
     * Get first user's connection date on the course.
2952
     *
2953
     * @param int User id
2954
     * @param int $courseId
2955
     * @param int Session id (optional, default=0)
2956
     * @param bool $convert_date
2957
     *
2958
     * @return string|bool Date with format long without day or false if there is no date
2959
     */
2960
    public static function get_first_connection_date_on_the_course(
2961
        $student_id,
2962
        $courseId,
2963
        $session_id = 0,
2964
        $convert_date = true
2965
    ) {
2966
        $student_id = (int) $student_id;
2967
        $courseId = (int) $courseId;
2968
        $session_id = (int) $session_id;
2969
2970
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2971
        $sql = 'SELECT login_course_date
2972
                FROM '.$table.'
2973
                WHERE
2974
                    user_id = '.$student_id.' AND
2975
                    c_id = '.$courseId.' AND
2976
                    session_id = '.$session_id.'
2977
                ORDER BY login_course_date ASC
2978
                LIMIT 0,1';
2979
        $rs = Database::query($sql);
2980
        if (Database::num_rows($rs) > 0) {
2981
            if ($first_login_date = Database::result($rs, 0, 0)) {
2982
                if (empty($first_login_date)) {
2983
                    return false;
2984
                }
2985
2986
                if ($convert_date) {
2987
                    return api_convert_and_format_date(
2988
                        $first_login_date,
2989
                        DATE_FORMAT_SHORT
2990
                    );
2991
                }
2992
2993
                return $first_login_date;
2994
            }
2995
        }
2996
2997
        return false;
2998
    }
2999
3000
    /**
3001
     * Get last user's connection date on the course.
3002
     *
3003
     * @param int         User id
3004
     * @param array $courseInfo real_id and code are used
3005
     * @param int            Session id (optional, default=0)
3006
     * @param bool $convert_date
3007
     *
3008
     * @return string|bool Date with format long without day or false if there is no date
3009
     */
3010
    public static function get_last_connection_date_on_the_course(
3011
        $student_id,
3012
        $courseInfo,
3013
        $session_id = 0,
3014
        $convert_date = true
3015
    ) {
3016
        // protect data
3017
        $student_id = (int) $student_id;
3018
        $session_id = (int) $session_id;
3019
3020
        if (empty($courseInfo) || empty($student_id)) {
3021
            return false;
3022
        }
3023
3024
        $courseId = $courseInfo['real_id'];
3025
3026
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
3027
3028
        if (self::minimumTimeAvailable($session_id, $courseId)) {
3029
            // Show the last date on which the user acceed the session when it was active
3030
            $where_condition = '';
3031
            $userInfo = api_get_user_info($student_id);
3032
            if (STUDENT == $userInfo['status'] && !empty($session_id)) {
3033
                // fin de acceso a la sesión
3034
                $sessionInfo = SessionManager::fetch($session_id);
3035
                $last_access = $sessionInfo['access_end_date'];
3036
                if (!empty($last_access)) {
3037
                    $where_condition = ' AND logout_course_date < "'.$last_access.'" ';
3038
                }
3039
            }
3040
            $sql = "SELECT logout_course_date
3041
                    FROM $table
3042
                    WHERE   user_id = $student_id AND
3043
                            c_id = $courseId AND
3044
                            session_id = $session_id $where_condition
3045
                    ORDER BY logout_course_date DESC
3046
                    LIMIT 0,1";
3047
3048
            $rs = Database::query($sql);
3049
            if (Database::num_rows($rs) > 0) {
3050
                if ($last_login_date = Database::result($rs, 0, 0)) {
3051
                    if (empty($last_login_date)) {
3052
                        return false;
3053
                    }
3054
                    if ($convert_date) {
3055
                        return api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
3056
                    }
3057
3058
                    return $last_login_date;
3059
                }
3060
            }
3061
        } else {
3062
            $sql = "SELECT logout_course_date
3063
                    FROM $table
3064
                    WHERE   user_id = $student_id AND
3065
                            c_id = $courseId AND
3066
                            session_id = $session_id
3067
                    ORDER BY logout_course_date DESC
3068
                    LIMIT 0,1";
3069
3070
            $rs = Database::query($sql);
3071
            if (Database::num_rows($rs) > 0) {
3072
                if ($last_login_date = Database::result($rs, 0, 0)) {
3073
                    if (empty($last_login_date)) {
3074
                        return false;
3075
                    }
3076
                    // see #5736
3077
                    $last_login_date_timestamp = api_strtotime($last_login_date);
3078
                    $now = time();
3079
                    // If the last connection is > than 7 days, the text is red
3080
                    // 604800 = 7 days in seconds
3081
                    if ($now - $last_login_date_timestamp > 604800) {
3082
                        if ($convert_date) {
3083
                            $last_login_date = api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
3084
                            $icon = null;
3085
                            if (api_is_allowed_to_edit()) {
3086
                                $url = api_get_path(WEB_CODE_PATH).
3087
                                    'announcements/announcements.php?action=add&remind_inactive='.$student_id.'&cidReq='.$courseInfo['code'];
3088
                                $icon = '<a href="'.$url.'" title="'.get_lang('RemindInactiveUser').'">
3089
                                  '.Display::return_icon('messagebox_warning.gif').'
3090
                                 </a>';
3091
                            }
3092
3093
                            return $icon.Display::label($last_login_date, 'warning');
3094
                        }
3095
3096
                        return $last_login_date;
3097
                    } else {
3098
                        if ($convert_date) {
3099
                            return api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
3100
                        }
3101
3102
                        return $last_login_date;
3103
                    }
3104
                }
3105
            }
3106
        }
3107
3108
        return false;
3109
    }
3110
3111
    public static function getLastConnectionInAnyCourse($studentId)
3112
    {
3113
        $studentId = (int) $studentId;
3114
3115
        if (empty($studentId)) {
3116
            return false;
3117
        }
3118
3119
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
3120
        $sql = "SELECT logout_course_date
3121
                FROM $table
3122
                WHERE user_id = $studentId
3123
                ORDER BY logout_course_date DESC
3124
                LIMIT 1";
3125
        $result = Database::query($sql);
3126
        if (Database::num_rows($result)) {
3127
            $row = Database::fetch_array($result);
3128
3129
            return $row['logout_course_date'];
3130
        }
3131
3132
        return false;
3133
    }
3134
3135
    /**
3136
     * Get last course access by course/session.
3137
     */
3138
    public static function getLastConnectionDateByCourse($courseId, $sessionId = 0)
3139
    {
3140
        $courseId = (int) $courseId;
3141
        $sessionId = (int) $sessionId;
3142
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
3143
3144
        $sql = "SELECT logout_course_date
3145
                FROM $table
3146
                WHERE
3147
                        c_id = $courseId AND
3148
                        session_id = $sessionId
3149
                ORDER BY logout_course_date DESC
3150
                LIMIT 0,1";
3151
3152
        $result = Database::query($sql);
3153
        if (Database::num_rows($result)) {
3154
            $row = Database::fetch_array($result);
3155
            if ($row) {
3156
                return $row['logout_course_date'];
3157
            }
3158
        }
3159
3160
        return '';
3161
    }
3162
3163
    /**
3164
     * Get count of the connections to the course during a specified period.
3165
     *
3166
     * @param int $courseId
3167
     * @param   int     Session id (optional)
3168
     * @param   int     Datetime from which to collect data (defaults to 0)
3169
     * @param   int     Datetime to which to collect data (defaults to now)
3170
     *
3171
     * @return int count connections
3172
     */
3173
    public static function get_course_connections_count(
3174
        $courseId,
3175
        $session_id = 0,
3176
        $start = 0,
3177
        $stop = null
3178
    ) {
3179
        if ($start < 0) {
3180
            $start = 0;
3181
        }
3182
        if (!isset($stop) || $stop < 0) {
3183
            $stop = api_get_utc_datetime();
3184
        }
3185
3186
        // Given we're storing in cache, round the start and end times
3187
        // to the lower minute
3188
        $roundedStart = substr($start, 0, -2).'00';
3189
        $roundedStop = substr($stop, 0, -2).'00';
3190
        $roundedStart = Database::escape_string($roundedStart);
3191
        $roundedStop = Database::escape_string($roundedStop);
3192
        $month_filter = " AND login_course_date > '$roundedStart' AND login_course_date < '$roundedStop' ";
3193
        $courseId = (int) $courseId;
3194
        $session_id = (int) $session_id;
3195
        $count = 0;
3196
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
3197
        $sql = "SELECT count(*) as count_connections
3198
                FROM $table
3199
                WHERE
3200
                    c_id = $courseId AND
3201
                    session_id = $session_id
3202
                    $month_filter";
3203
3204
        // This query can be very slow (several seconds on an indexed table
3205
        // with 14M rows). As such, we'll try to use APCu if it is
3206
        // available to store the resulting value for a few seconds
3207
        $cacheAvailable = api_get_configuration_value('apc');
3208
        if ($cacheAvailable === true) {
3209
            $apc = apcu_cache_info(true);
3210
            $apc_end = $apc['start_time'] + $apc['ttl'];
3211
            $apc_var = api_get_configuration_value('apc_prefix').'course_access_'.$courseId.'_'.$session_id.'_'.strtotime($roundedStart).'_'.strtotime($roundedStop);
3212
            if (apcu_exists($apc_var) && (time() < $apc_end)
3213
                && apcu_fetch($apc_var) > 0
3214
            ) {
3215
                $count = apcu_fetch($apc_var);
3216
            } else {
3217
                $rs = Database::query($sql);
3218
                if (Database::num_rows($rs) > 0) {
3219
                    $row = Database::fetch_object($rs);
3220
                    $count = $row->count_connections;
3221
                }
3222
                apcu_clear_cache();
3223
                apcu_store($apc_var, $count, 60);
3224
            }
3225
        } else {
3226
            $rs = Database::query($sql);
3227
            if (Database::num_rows($rs) > 0) {
3228
                $row = Database::fetch_object($rs);
3229
                $count = $row->count_connections;
3230
            }
3231
        }
3232
3233
        return $count;
3234
    }
3235
3236
    /**
3237
     * Get count courses per student.
3238
     *
3239
     * @param int  $user_id          Student id
3240
     * @param bool $include_sessions Include sessions (optional)
3241
     *
3242
     * @return int count courses
3243
     */
3244
    public static function count_course_per_student($user_id, $include_sessions = true)
3245
    {
3246
        $user_id = (int) $user_id;
3247
        $tbl_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3248
        $tbl_session_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3249
3250
        $sql = 'SELECT DISTINCT c_id
3251
                FROM '.$tbl_course_rel_user.'
3252
                WHERE user_id = '.$user_id.' AND relation_type<>'.COURSE_RELATION_TYPE_RRHH;
3253
        $rs = Database::query($sql);
3254
        $nb_courses = Database::num_rows($rs);
3255
3256
        if ($include_sessions) {
3257
            $sql = 'SELECT DISTINCT c_id
3258
                    FROM '.$tbl_session_course_rel_user.'
3259
                    WHERE user_id = '.$user_id;
3260
            $rs = Database::query($sql);
3261
            $nb_courses += Database::num_rows($rs);
3262
        }
3263
3264
        return $nb_courses;
3265
    }
3266
3267
    public static function countSessionsPerStudent(int $userId): int
3268
    {
3269
        $tblSessionUser = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3270
3271
        $sql = 'SELECT DISTINCT id
3272
            FROM '.$tblSessionUser.'
3273
            WHERE relation_type = '.SessionEntity::STUDENT.' AND user_id = '.$userId;
3274
3275
        $rs = Database::query($sql);
3276
3277
        return Database::num_rows($rs);
3278
    }
3279
3280
    /**
3281
     * Gets the score average from all tests in a course by student.
3282
     *
3283
     * @param $student_id
3284
     * @param $course_code
3285
     * @param int  $exercise_id
3286
     * @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...
3287
     * @param int  $active_filter 2 for consider all tests
3288
     *                            1 for active <> -1
3289
     *                            0 for active <> 0
3290
     * @param int  $into_lp       1 for all exercises
3291
     *                            0 for without LP
3292
     * @param mixed id
3293
     * @param string code
3294
     * @param int id (optional), filtered by exercise
3295
     * @param int id (optional), if param $session_id is null
3296
     *                                                it'll return results including sessions, 0 = session is not
3297
     *                                                filtered
3298
     *
3299
     * @return string value (number %) Which represents a round integer about the score average
3300
     */
3301
    public static function get_avg_student_exercise_score(
3302
        $student_id,
3303
        $course_code,
3304
        $exercise_id = 0,
3305
        $session_id = null,
3306
        $active_filter = 1,
3307
        $into_lp = 0
3308
    ) {
3309
        $course_code = Database::escape_string($course_code);
3310
        $course_info = api_get_course_info($course_code);
3311
        if (!empty($course_info)) {
3312
            // table definition
3313
            $tbl_course_quiz = Database::get_course_table(TABLE_QUIZ_TEST);
3314
            $tbl_stats_exercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3315
3316
            // Compose a filter based on optional exercise given
3317
            $condition_quiz = "";
3318
            if (!empty($exercise_id)) {
3319
                $exercise_id = intval($exercise_id);
3320
                $condition_quiz = " AND iid = $exercise_id ";
3321
            }
3322
3323
            // Compose a filter based on optional session id given
3324
            $condition_session = '';
3325
            if (isset($session_id)) {
3326
                $session_id = intval($session_id);
3327
                $condition_session = " AND session_id = $session_id ";
3328
            }
3329
            if ($active_filter == 1) {
3330
                $condition_active = 'AND active <> -1';
3331
            } elseif ($active_filter == 0) {
3332
                $condition_active = 'AND active <> 0';
3333
            } else {
3334
                $condition_active = '';
3335
            }
3336
            $condition_into_lp = '';
3337
            $select_lp_id = '';
3338
            if ($into_lp == 0) {
3339
                $condition_into_lp = 'AND orig_lp_id = 0 AND orig_lp_item_id = 0';
3340
            } else {
3341
                $select_lp_id = ', orig_lp_id as lp_id ';
3342
            }
3343
3344
            $sql = "SELECT count(iid)
3345
    		        FROM $tbl_course_quiz
3346
    				WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz ";
3347
            $count_quiz = 0;
3348
            $countQuizResult = Database::query($sql);
3349
            if (!empty($countQuizResult)) {
3350
                $count_quiz = Database::fetch_row($countQuizResult);
3351
            }
3352
3353
            if (!empty($count_quiz[0]) && !empty($student_id)) {
3354
                if (is_array($student_id)) {
3355
                    $student_id = array_map('intval', $student_id);
3356
                    $condition_user = " AND exe_user_id IN (".implode(',', $student_id).") ";
3357
                } else {
3358
                    $student_id = intval($student_id);
3359
                    $condition_user = " AND exe_user_id = '$student_id' ";
3360
                }
3361
3362
                if (empty($exercise_id)) {
3363
                    $sql = "SELECT iid FROM $tbl_course_quiz
3364
                            WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz";
3365
                    $result = Database::query($sql);
3366
                    $exercise_list = [];
3367
                    $exercise_id = null;
3368
                    if (!empty($result) && Database::num_rows($result)) {
3369
                        while ($row = Database::fetch_array($result)) {
3370
                            $exercise_list[] = $row['iid'];
3371
                        }
3372
                    }
3373
                    if (!empty($exercise_list)) {
3374
                        $exercise_id = implode("','", $exercise_list);
3375
                    }
3376
                }
3377
3378
                $count_quiz = Database::fetch_row(Database::query($sql));
3379
                $sql = "SELECT
3380
                        SUM(exe_result/exe_weighting*100) as avg_score,
3381
                        COUNT(*) as num_attempts
3382
                        $select_lp_id
3383
                        FROM $tbl_stats_exercise
3384
                        WHERE
3385
                            exe_exo_id IN ('".$exercise_id."')
3386
                            $condition_user AND
3387
                            status = '' AND
3388
                            c_id = {$course_info['real_id']}
3389
                            $condition_session
3390
                            $condition_into_lp
3391
                        ORDER BY exe_date DESC";
3392
3393
                $res = Database::query($sql);
3394
                $row = Database::fetch_array($res);
3395
                $quiz_avg_score = null;
3396
3397
                if (!empty($row['avg_score'])) {
3398
                    $quiz_avg_score = round($row['avg_score'], 2);
3399
                }
3400
3401
                if (!empty($row['num_attempts'])) {
3402
                    $quiz_avg_score = round($quiz_avg_score / $row['num_attempts'], 2);
3403
                }
3404
                if (is_array($student_id)) {
3405
                    $quiz_avg_score = round($quiz_avg_score / count($student_id), 2);
3406
                }
3407
                if ($into_lp == 0) {
3408
                    return $quiz_avg_score;
3409
                } else {
3410
                    if (!empty($row['lp_id'])) {
3411
                        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
3412
                        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3413
                        $sql = "SELECT lp.name
3414
                                FROM $tbl_lp as lp, $tbl_course as c
3415
                                WHERE
3416
                                    c.code = '$course_code' AND
3417
                                    lp.id = ".$row['lp_id']." AND
3418
                                    lp.c_id = c.id
3419
                                LIMIT 1;
3420
                        ";
3421
                        $result = Database::query($sql);
3422
                        $row_lp = Database::fetch_row($result);
3423
                        $lp_name = null;
3424
                        if ($row_lp && isset($row_lp[0])) {
3425
                            $lp_name = $row_lp[0];
3426
                        }
3427
3428
                        return [$quiz_avg_score, $lp_name];
3429
                    } else {
3430
                        return [$quiz_avg_score, null];
3431
                    }
3432
                }
3433
            }
3434
        }
3435
3436
        return null;
3437
    }
3438
3439
    /**
3440
     * Get count student's exercise COMPLETED attempts.
3441
     *
3442
     * @param int $student_id
3443
     * @param int $courseId
3444
     * @param int $exercise_id
3445
     * @param int $lp_id
3446
     * @param int $lp_item_id
3447
     * @param int $session_id
3448
     * @param int $find_all_lp 0 = just LP specified
3449
     *                         1 = LP specified or whitout LP,
3450
     *                         2 = all rows
3451
     *
3452
     * @internal param \Student $int id
3453
     * @internal param \Course $string code
3454
     * @internal param \Exercise $int id
3455
     * @internal param \Learning $int path id (optional),
3456
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required
3457
     * @internal param \Learning $int path item id (optional),
3458
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required
3459
     *
3460
     * @return int count of attempts
3461
     */
3462
    public static function count_student_exercise_attempts(
3463
        $student_id,
3464
        $courseId,
3465
        $exercise_id,
3466
        $lp_id = 0,
3467
        $lp_item_id = 0,
3468
        $session_id = 0,
3469
        $find_all_lp = 0
3470
    ) {
3471
        $courseId = intval($courseId);
3472
        $student_id = intval($student_id);
3473
        $exercise_id = intval($exercise_id);
3474
        $session_id = intval($session_id);
3475
3476
        $lp_id = intval($lp_id);
3477
        $lp_item_id = intval($lp_item_id);
3478
        $tbl_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3479
3480
        $sql = "SELECT COUNT(ex.exe_id) as essais
3481
                FROM $tbl_stats_exercises AS ex
3482
                WHERE
3483
                    ex.c_id = $courseId AND
3484
                    ex.exe_exo_id = $exercise_id AND
3485
                    status = '' AND
3486
                    exe_user_id= $student_id AND
3487
                    session_id = $session_id ";
3488
3489
        if ($find_all_lp == 1) {
3490
            $sql .= "AND (orig_lp_id = $lp_id OR orig_lp_id = 0)
3491
                AND (orig_lp_item_id = $lp_item_id OR orig_lp_item_id = 0)";
3492
        } elseif ($find_all_lp == 0) {
3493
            $sql .= "AND orig_lp_id = $lp_id
3494
                AND orig_lp_item_id = $lp_item_id";
3495
        }
3496
3497
        $rs = Database::query($sql);
3498
        $row = Database::fetch_row($rs);
3499
        $count_attempts = $row[0];
3500
3501
        return $count_attempts;
3502
    }
3503
3504
    /**
3505
     * It gets the last finalization date of learnpaths in a course.
3506
     *
3507
     * @return string finalization date formatted or false if it is empty.
3508
     */
3509
    public static function getCourseLpFinalizationDate(
3510
        int $userId,
3511
        int $courseId,
3512
        int $sessionId,
3513
        bool $convertDate = true
3514
    ) {
3515
        $tblLpView = Database::get_course_table(TABLE_LP_VIEW);
3516
        $tblLpItem = Database::get_course_table(TABLE_LP_ITEM);
3517
        $tblLpItemView = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3518
3519
        $sql = "SELECT FROM_UNIXTIME(liv.start_time) as start_date
3520
                FROM $tblLpItemView liv
3521
                INNER JOIN
3522
                    $tblLpView lv ON lv.iid = liv.lp_view_id
3523
                INNER JOIN
3524
                    $tblLpItem li ON li.iid = liv.lp_item_id
3525
                WHERE
3526
                    lv.user_id = $userId AND
3527
                    lv.c_id = $courseId AND
3528
                    lv.session_id = $sessionId AND
3529
                    li.item_type = '".TOOL_LP_FINAL_ITEM."' AND
3530
                    liv.status = 'completed'
3531
                ORDER BY start_date DESC
3532
                LIMIT 1";
3533
3534
        $rs = Database::query($sql);
3535
        $lpFinalDate = Database::result($rs, 0, 0);
3536
3537
        if (empty($lpFinalDate)) {
3538
            return false;
3539
        }
3540
3541
        if ($convertDate) {
3542
            return api_convert_and_format_date($lpFinalDate, DATE_FORMAT_SHORT);
3543
        }
3544
3545
        return $lpFinalDate;
3546
    }
3547
3548
    /**
3549
     * It gets the last finalization date of exercises in a course.
3550
     *
3551
     * @return string finalization date formatted or false if it is empty.
3552
     */
3553
    public static function getCourseQuizLastFinalizationDate(
3554
        int $userId,
3555
        int $courseId,
3556
        int $sessionId,
3557
        bool $convertDate = true
3558
    ) {
3559
        $tblTrackExercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3560
3561
        $sql = "SELECT ex.exe_date
3562
                FROM $tblTrackExercise AS ex
3563
                WHERE
3564
                    ex.c_id = $courseId AND
3565
                    ex.session_id  = $sessionId AND
3566
                    ex.exe_user_id = $userId AND
3567
                    ex.status = ''
3568
                ORDER BY ex.exe_date DESC
3569
                LIMIT 1";
3570
        $rs = Database::query($sql);
3571
        $exeDate = Database::result($rs, 0, 0);
3572
3573
        if (empty($exeDate)) {
3574
            return false;
3575
        }
3576
3577
        if ($convertDate) {
3578
            return api_convert_and_format_date($exeDate, DATE_FORMAT_SHORT);
3579
        }
3580
3581
        return $exeDate;
3582
    }
3583
3584
    /**
3585
     * Get count student's exercise progress.
3586
     *
3587
     * @param array $exercise_list
3588
     * @param int   $user_id
3589
     * @param int   $courseId
3590
     * @param int   $session_id
3591
     *
3592
     * @return string
3593
     */
3594
    public static function get_exercise_student_progress(
3595
        $exercise_list,
3596
        $user_id,
3597
        $courseId,
3598
        $session_id
3599
    ) {
3600
        $courseId = (int) $courseId;
3601
        $user_id = (int) $user_id;
3602
        $session_id = (int) $session_id;
3603
3604
        if (empty($exercise_list)) {
3605
            return '0%';
3606
        }
3607
        $tbl_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3608
        $exercise_list = array_keys($exercise_list);
3609
        $exercise_list = array_map('intval', $exercise_list);
3610
3611
        $exercise_list_imploded = implode("' ,'", $exercise_list);
3612
3613
        $sql = "SELECT COUNT(DISTINCT ex.exe_exo_id)
3614
                FROM $tbl_stats_exercises AS ex
3615
                WHERE
3616
                    ex.c_id = $courseId AND
3617
                    ex.session_id  = $session_id AND
3618
                    ex.exe_user_id = $user_id AND
3619
                    ex.status = '' AND
3620
                    ex.exe_exo_id IN ('$exercise_list_imploded') ";
3621
3622
        $rs = Database::query($sql);
3623
        $count = 0;
3624
        if ($rs) {
3625
            $row = Database::fetch_row($rs);
3626
            $count = $row[0];
3627
        }
3628
        $count = ($count != 0) ? 100 * round(intval($count) / count($exercise_list), 2).'%' : '0%';
3629
3630
        return $count;
3631
    }
3632
3633
    /**
3634
     * @param array $exercise_list
3635
     * @param int   $user_id
3636
     * @param int   $courseId
3637
     * @param int   $session_id
3638
     *
3639
     * @return string
3640
     */
3641
    public static function get_exercise_student_average_best_attempt(
3642
        $exercise_list,
3643
        $user_id,
3644
        $courseId,
3645
        $session_id
3646
    ) {
3647
        $result = 0;
3648
        if (!empty($exercise_list)) {
3649
            foreach ($exercise_list as $exercise_data) {
3650
                $exercise_id = $exercise_data['iid'];
3651
                $best_attempt = Event::get_best_attempt_exercise_results_per_user(
3652
                    $user_id,
3653
                    $exercise_id,
3654
                    $courseId,
3655
                    $session_id
3656
                );
3657
3658
                if (!empty($best_attempt) && !empty($best_attempt['exe_weighting'])) {
3659
                    $result += $best_attempt['exe_result'] / $best_attempt['exe_weighting'];
3660
                }
3661
            }
3662
            $result = $result / count($exercise_list);
3663
            $result = round($result, 2) * 100;
3664
        }
3665
3666
        return $result.'%';
3667
    }
3668
3669
    /**
3670
     * Returns the average student progress in the learning paths of the given
3671
     * course, it will take into account the progress that were not started.
3672
     *
3673
     * @param int|array $studentId
3674
     * @param string    $courseCode
3675
     * @param array     $lpIdList        Limit average to listed lp ids
3676
     * @param int       $sessionId       Session id (optional),
3677
     *                                   if parameter $session_id is null(default) it'll return results including
3678
     *                                   sessions, 0 = session is not filtered
3679
     * @param bool      $returnArray     Will return an array of the type:
3680
     *                                   [sum_of_progresses, number] if it is set to true
3681
     * @param bool      $onlySeriousGame Optional. Limit average to lp on seriousgame mode
3682
     * @param bool      $maxInsteadAvg   Optional. It will return the max progress instead the average
3683
     *
3684
     * @return float Average or max progress of the user in this course from 0 to 100
3685
     */
3686
    public static function get_avg_student_progress(
3687
        $studentId,
3688
        $courseCode = null,
3689
        $lpIdList = [],
3690
        $sessionId = null,
3691
        $returnArray = false,
3692
        $onlySeriousGame = false,
3693
        $maxInsteadAvg = false,
3694
        $startDate = null,
3695
        $endDate = null
3696
    ) {
3697
        // If there is at least one learning path and one student.
3698
        if (empty($studentId)) {
3699
            return false;
3700
        }
3701
3702
        $sessionId = (int) $sessionId;
3703
        $courseInfo = api_get_course_info($courseCode);
3704
3705
        if (empty($courseInfo)) {
3706
            return false;
3707
        }
3708
3709
        $lPTable = Database::get_course_table(TABLE_LP_MAIN);
3710
        $lpViewTable = Database::get_course_table(TABLE_LP_VIEW);
3711
        $lpConditions = [];
3712
        $lpConditions['c_id = ? '] = $courseInfo['real_id'];
3713
3714
        if ($sessionId > 0) {
3715
            $lpConditions['AND (session_id = ? OR session_id = 0 OR session_id IS NULL)'] = $sessionId;
3716
        } else {
3717
            $lpConditions['AND session_id = ?'] = $sessionId;
3718
        }
3719
3720
        if (is_array($lpIdList) && count($lpIdList) > 0) {
3721
            $placeHolders = [];
3722
            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...
3723
                $placeHolders[] = '?';
3724
            }
3725
            $lpConditions['AND id IN('.implode(', ', $placeHolders).') '] = $lpIdList;
3726
        }
3727
3728
        if ($onlySeriousGame) {
3729
            $lpConditions['AND seriousgame_mode = ? '] = true;
3730
        }
3731
3732
        $resultLP = Database::select(
3733
            'id',
3734
            $lPTable,
3735
            ['where' => $lpConditions]
3736
        );
3737
        $filteredLP = array_keys($resultLP);
3738
3739
        if (empty($filteredLP)) {
3740
            return false;
3741
        }
3742
3743
        $conditions = [
3744
            " lp_view.c_id = {$courseInfo['real_id']} ",
3745
            " lp_view.lp_id IN (".implode(', ', $filteredLP).") ",
3746
        ];
3747
3748
        $groupBy = 'GROUP BY lp_view.lp_id';
3749
3750
        if (is_array($studentId)) {
3751
            $studentId = array_map('intval', $studentId);
3752
            $conditions[] = " lp_view.user_id IN (".implode(',', $studentId).")  ";
3753
        } else {
3754
            $studentId = (int) $studentId;
3755
            $conditions[] = " lp_view.user_id = '$studentId' ";
3756
3757
            if (empty($lpIdList)) {
3758
                $lpList = new LearnpathList(
3759
                    $studentId,
3760
                    $courseInfo,
3761
                    $sessionId,
3762
                    null,
3763
                    false,
3764
                    null,
3765
                    true
3766
                );
3767
                $lpList = $lpList->get_flat_list();
3768
                if (!empty($lpList)) {
3769
                    /** @var $lp */
3770
                    foreach ($lpList as $lpId => $lp) {
3771
                        $lpIdList[] = $lp['lp_old_id'];
3772
                    }
3773
                }
3774
            }
3775
        }
3776
3777
        if (!empty($sessionId)) {
3778
            $conditions[] = " session_id = $sessionId ";
3779
        } else {
3780
            $conditions[] = ' (session_id = 0 OR session_id IS NULL) ';
3781
        }
3782
3783
        $innerJoin = "";
3784
        if (!empty($startDate) || !empty($endDate)) {
3785
            $lpItemViewTable = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3786
            $innerJoin = " INNER JOIN $lpItemViewTable liv ON liv.lp_view_id = lp_view.iid";
3787
            if (!empty($startDate)) {
3788
                $startDate = api_get_utc_datetime($startDate, false, true);
3789
                $startTime = strtotime($startDate->format('Y-m-d 00:00:00'));
3790
                $conditions[] = " liv.start_time >= '".$startTime."' ";
3791
            }
3792
            if (!empty($endDate)) {
3793
                $endDate = api_get_utc_datetime($endDate, false, true);
3794
                $endTime = strtotime($endDate->format('Y-m-d 23:59:59'));
3795
                $conditions[] = " liv.start_time <= '".$endTime."' ";
3796
            }
3797
        }
3798
3799
        $conditionToString = implode('AND', $conditions);
3800
        $sql = "SELECT lp_view.lp_id, lp_view.view_count, lp_view.progress
3801
                FROM $lpViewTable lp_view
3802
                $innerJoin
3803
                WHERE
3804
                    $conditionToString
3805
                    $groupBy
3806
                ORDER BY view_count DESC";
3807
        $result = Database::query($sql);
3808
3809
        $progress = [];
3810
        $viewCount = [];
3811
        while ($row = Database::fetch_array($result, 'ASSOC')) {
3812
            if (!isset($viewCount[$row['lp_id']])) {
3813
                $progress[$row['lp_id']] = $row['progress'];
3814
            }
3815
            $viewCount[$row['lp_id']] = $row['view_count'];
3816
        }
3817
3818
        // Fill with lp ids
3819
        $newProgress = [];
3820
        if (!empty($lpIdList)) {
3821
            foreach ($lpIdList as $lpId) {
3822
                if (isset($progress[$lpId])) {
3823
                    $newProgress[] = $progress[$lpId];
3824
                }
3825
            }
3826
            $total = count($lpIdList);
3827
        } else {
3828
            $newProgress = $progress;
3829
            $total = count($newProgress);
3830
        }
3831
3832
        $average = 0;
3833
        $sum = 0;
3834
        if (!empty($newProgress)) {
3835
            if ($maxInsteadAvg) {
3836
                // It will return the max progress instead the average
3837
                return max($newProgress);
3838
            } else {
3839
                $sum = array_sum($newProgress);
3840
                $average = $sum / $total;
3841
            }
3842
        }
3843
3844
        if ($returnArray) {
3845
            return [
3846
                $sum,
3847
                $total,
3848
            ];
3849
        }
3850
3851
        return round($average, 1);
3852
    }
3853
3854
    /**
3855
     * This function gets:
3856
     * 1. The score average from all SCORM Test items in all LP in a course-> All the answers / All the max scores.
3857
     * 2. The score average from all Tests (quiz) in all LP in a course-> All the answers / All the max scores.
3858
     * 3. And finally it will return the average between 1. and 2.
3859
     *
3860
     * @todo improve performance, when loading 1500 users with 20 lps the script dies
3861
     * This function does not take the results of a Test out of a LP
3862
     *
3863
     * @param mixed  $student_id                      Array of user ids or an user id
3864
     * @param string $course_code
3865
     * @param array  $lp_ids                          List of LP ids
3866
     * @param int    $session_id                      Session id (optional),
3867
     *                                                if param $session_id is null(default) it'll return results
3868
     *                                                including sessions, 0 = session is not filtered
3869
     * @param bool   $return_array                    Returns an array of the
3870
     *                                                type [sum_score, num_score] if set to true
3871
     * @param bool   $get_only_latest_attempt_results get only the latest attempts or ALL attempts
3872
     * @param bool   $getOnlyBestAttempt
3873
     *
3874
     * @return string value (number %) Which represents a round integer explain in got in 3
3875
     */
3876
    public static function get_avg_student_score(
3877
        $student_id,
3878
        $course_code,
3879
        $lp_ids = [],
3880
        $session_id = null,
3881
        $return_array = false,
3882
        $get_only_latest_attempt_results = false,
3883
        $getOnlyBestAttempt = false
3884
    ) {
3885
        $debug = false;
3886
        if ($debug) {
3887
            echo '<h1>Tracking::get_avg_student_score</h1>';
3888
        }
3889
        $tbl_stats_exercices = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3890
        $tbl_stats_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
3891
        $course = api_get_course_info($course_code);
3892
3893
        if (empty($course)) {
3894
            return null;
3895
        }
3896
3897
        // Get course tables names
3898
        $tbl_quiz_questions = Database::get_course_table(TABLE_QUIZ_QUESTION);
3899
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
3900
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
3901
        $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
3902
        $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3903
        $course_id = $course['real_id'];
3904
3905
        // Compose a filter based on optional learning paths list given
3906
        $condition_lp = '';
3907
        if (count($lp_ids) > 0) {
3908
            $condition_lp = " AND id IN(".implode(',', $lp_ids).") ";
3909
        }
3910
3911
        // Compose a filter based on optional session id
3912
        $session_id = (int) $session_id;
3913
        if (count($lp_ids) > 0) {
3914
            $condition_session = " AND session_id = $session_id ";
3915
        } else {
3916
            $condition_session = " WHERE session_id = $session_id ";
3917
        }
3918
3919
        // Check the real number of LPs corresponding to the filter in the
3920
        // database (and if no list was given, get them all)
3921
        if (empty($session_id)) {
3922
            $sql = "SELECT DISTINCT(id), use_max_score
3923
                    FROM $lp_table
3924
                    WHERE
3925
                        c_id = $course_id AND
3926
                        (session_id = 0 OR session_id IS NULL) $condition_lp ";
3927
        } else {
3928
            $sql = "SELECT DISTINCT(id), use_max_score
3929
                    FROM $lp_table
3930
                    WHERE c_id = $course_id $condition_lp ";
3931
        }
3932
3933
        $res_row_lp = Database::query($sql);
3934
        $count_row_lp = Database::num_rows($res_row_lp);
3935
3936
        $lp_list = $use_max_score = [];
3937
        while ($row_lp = Database::fetch_array($res_row_lp)) {
3938
            $lp_list[] = $row_lp['id'];
3939
            $use_max_score[$row_lp['id']] = $row_lp['use_max_score'];
3940
        }
3941
3942
        // prepare filter on users
3943
        if (is_array($student_id)) {
3944
            array_walk($student_id, 'intval');
3945
            $condition_user1 = " AND user_id IN (".implode(',', $student_id).") ";
3946
        } else {
3947
            $condition_user1 = " AND user_id = $student_id ";
3948
        }
3949
3950
        if (empty($count_row_lp) || empty($student_id)) {
3951
            return null;
3952
        }
3953
3954
        // Getting latest LP result for a student
3955
        // @todo problem when a  course have more than 1500 users
3956
        $sql = "SELECT MAX(view_count) as vc, id, progress, lp_id, user_id
3957
                FROM $lp_view_table
3958
                WHERE
3959
                    c_id = $course_id AND
3960
                    lp_id IN (".implode(',', $lp_list).")
3961
                    $condition_user1 AND
3962
                    session_id = $session_id
3963
                GROUP BY lp_id, user_id";
3964
3965
        $rs_last_lp_view_id = Database::query($sql);
3966
        $global_result = 0;
3967
3968
        if (Database::num_rows($rs_last_lp_view_id) > 0) {
3969
            // Cycle through each line of the results (grouped by lp_id, user_id)
3970
            while ($row_lp_view = Database::fetch_array($rs_last_lp_view_id)) {
3971
                $count_items = 0;
3972
                $lpPartialTotal = 0;
3973
                $list = [];
3974
                $lp_view_id = $row_lp_view['id'];
3975
                $lp_id = $row_lp_view['lp_id'];
3976
                $user_id = $row_lp_view['user_id'];
3977
3978
                if ($debug) {
3979
                    echo '<h2>LP id '.$lp_id.'</h2>';
3980
                    echo "get_only_latest_attempt_results: $get_only_latest_attempt_results <br />";
3981
                    echo "getOnlyBestAttempt: $getOnlyBestAttempt <br />";
3982
                }
3983
3984
                if ($get_only_latest_attempt_results || $getOnlyBestAttempt) {
3985
                    // Getting lp_items done by the user
3986
                    $sql = "SELECT DISTINCT lp_item_id
3987
                            FROM $lp_item_view_table
3988
                            WHERE
3989
                                c_id = $course_id AND
3990
                                lp_view_id = $lp_view_id
3991
                            ORDER BY lp_item_id";
3992
                    $res_lp_item = Database::query($sql);
3993
3994
                    while ($row_lp_item = Database::fetch_array($res_lp_item, 'ASSOC')) {
3995
                        $my_lp_item_id = $row_lp_item['lp_item_id'];
3996
                        $order = ' view_count DESC';
3997
                        if ($getOnlyBestAttempt) {
3998
                            $order = ' lp_iv.score DESC';
3999
                        }
4000
4001
                        // Getting the most recent attempt
4002
                        $sql = "SELECT
4003
                                    lp_iv.id as lp_item_view_id,
4004
                                    lp_iv.score as score,
4005
                                    lp_i.max_score,
4006
                                    lp_iv.max_score as max_score_item_view,
4007
                                    lp_i.path,
4008
                                    lp_i.item_type,
4009
                                    lp_i.id as iid
4010
                                FROM $lp_item_view_table as lp_iv
4011
                                INNER JOIN $lp_item_table as lp_i
4012
                                ON (
4013
                                    lp_i.id = lp_iv.lp_item_id AND
4014
                                    lp_iv.c_id = lp_i.c_id
4015
                                )
4016
                                WHERE
4017
                                    lp_iv.c_id = $course_id AND
4018
                                    lp_i.c_id  = $course_id AND
4019
                                    lp_item_id = $my_lp_item_id AND
4020
                                    lp_view_id = $lp_view_id AND
4021
                                    (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."')
4022
                                ORDER BY $order
4023
                                LIMIT 1";
4024
4025
                        $res_lp_item_result = Database::query($sql);
4026
                        while ($row_max_score = Database::fetch_array($res_lp_item_result, 'ASSOC')) {
4027
                            $list[] = $row_max_score;
4028
                        }
4029
                    }
4030
                } else {
4031
                    // For the currently analysed view, get the score and
4032
                    // max_score of each item if it is a sco or a TOOL_QUIZ
4033
                    $sql = "SELECT
4034
                                lp_iv.id as lp_item_view_id,
4035
                                lp_iv.score as score,
4036
                                lp_i.max_score,
4037
                                lp_iv.max_score as max_score_item_view,
4038
                                lp_i.path,
4039
                                lp_i.item_type,
4040
                                lp_i.id as iid
4041
                              FROM $lp_item_view_table as lp_iv
4042
                              INNER JOIN $lp_item_table as lp_i
4043
                              ON lp_i.id = lp_iv.lp_item_id AND
4044
                                 lp_iv.c_id = lp_i.c_id
4045
                              WHERE
4046
                                lp_iv.c_id = $course_id AND
4047
                                lp_i.c_id  = $course_id AND
4048
                                lp_view_id = $lp_view_id AND
4049
                                (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."')
4050
                            ";
4051
                    $res_max_score = Database::query($sql);
4052
                    while ($row_max_score = Database::fetch_array($res_max_score, 'ASSOC')) {
4053
                        $list[] = $row_max_score;
4054
                    }
4055
                }
4056
4057
                // Go through each scorable element of this view
4058
                $score_of_scorm_calculate = 0;
4059
                foreach ($list as $row_max_score) {
4060
                    // Came from the original lp_item
4061
                    $max_score = $row_max_score['max_score'];
4062
                    // Came from the lp_item_view
4063
                    $max_score_item_view = $row_max_score['max_score_item_view'];
4064
                    $score = $row_max_score['score'];
4065
                    if ($debug) {
4066
                        echo '<h3>Item Type: '.$row_max_score['item_type'].'</h3>';
4067
                    }
4068
4069
                    if ($row_max_score['item_type'] === 'sco') {
4070
                        /* Check if it is sco (easier to get max_score)
4071
                           when there's no max score, we assume 100 as the max score,
4072
                           as the SCORM 1.2 says that the value should always be between 0 and 100.
4073
                        */
4074
                        if ($max_score == 0 || is_null($max_score) || $max_score == '') {
4075
                            // Chamilo style
4076
                            if ($use_max_score[$lp_id]) {
4077
                                $max_score = 100;
4078
                            } else {
4079
                                // Overwrites max score = 100 to use the one that came in the lp_item_view see BT#1613
4080
                                $max_score = $max_score_item_view;
4081
                            }
4082
                        }
4083
                        // Avoid division by zero errors
4084
                        if (!empty($max_score)) {
4085
                            $lpPartialTotal += $score / $max_score;
4086
                        }
4087
                        if ($debug) {
4088
                            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...
4089
                            var_dump("score: $score");
4090
                            var_dump("max_score: $max_score");
4091
                        }
4092
                    } else {
4093
                        // Case of a TOOL_QUIZ element
4094
                        $item_id = $row_max_score['iid'];
4095
                        $item_path = $row_max_score['path'];
4096
                        $lp_item_view_id = (int) $row_max_score['lp_item_view_id'];
4097
4098
                        if (empty($lp_item_view_id)) {
4099
                            $lpItemCondition = ' (orig_lp_item_view_id = 0 OR orig_lp_item_view_id IS NULL) ';
4100
                        } else {
4101
                            $lpItemCondition = " orig_lp_item_view_id = $lp_item_view_id ";
4102
                        }
4103
4104
                        // Get last attempt to this exercise through
4105
                        // the current lp for the current user
4106
                        $order = 'exe_date DESC';
4107
                        if ($getOnlyBestAttempt) {
4108
                            $order = 'exe_result DESC';
4109
                        }
4110
                        $sql = "SELECT exe_id, exe_result
4111
                                FROM $tbl_stats_exercices
4112
                                WHERE
4113
                                    exe_exo_id = '$item_path' AND
4114
                                    exe_user_id = $user_id AND
4115
                                    orig_lp_item_id = $item_id AND
4116
                                    $lpItemCondition AND
4117
                                    c_id = $course_id AND
4118
                                    session_id = $session_id AND
4119
                                    status = ''
4120
                                ORDER BY $order
4121
                                LIMIT 1";
4122
4123
                        $result_last_attempt = Database::query($sql);
4124
                        $num = Database::num_rows($result_last_attempt);
4125
                        if ($num > 0) {
4126
                            $attemptResult = Database::fetch_array($result_last_attempt, 'ASSOC');
4127
                            $id_last_attempt = (int) $attemptResult['exe_id'];
4128
                            // We overwrite the score with the best one not the one saved in the LP (latest)
4129
                            if ($getOnlyBestAttempt && $get_only_latest_attempt_results == false) {
4130
                                if ($debug) {
4131
                                    echo "Following score comes from the track_exercise table not in the LP because the score is the best<br />";
4132
                                }
4133
                                $score = $attemptResult['exe_result'];
4134
                            }
4135
4136
                            if ($debug) {
4137
                                echo "Attempt id: $id_last_attempt with score $score<br />";
4138
                            }
4139
                            // Within the last attempt number tracking, get the sum of
4140
                            // the max_scores of all questions that it was
4141
                            // made of (we need to make this call dynamic because of random questions selection)
4142
                            $sql = "SELECT SUM(t.ponderation) as maxscore FROM
4143
                                    (
4144
                                        SELECT DISTINCT
4145
                                            question_id,
4146
                                            marks,
4147
                                            ponderation
4148
                                        FROM $tbl_stats_attempts AS at
4149
                                        INNER JOIN $tbl_quiz_questions AS q
4150
                                        ON q.iid = at.question_id
4151
                                        WHERE
4152
                                            exe_id = $id_last_attempt AND
4153
                                            at.c_id = $course_id
4154
                                    )
4155
                                    AS t";
4156
4157
                            $res_max_score_bis = Database::query($sql);
4158
                            $row_max_score_bis = Database::fetch_array($res_max_score_bis);
4159
4160
                            if (!empty($row_max_score_bis['maxscore'])) {
4161
                                $max_score = $row_max_score_bis['maxscore'];
4162
                            }
4163
                            if (!empty($max_score) && floatval($max_score) > 0) {
4164
                                $lpPartialTotal += $score / $max_score;
4165
                            }
4166
                            if ($debug) {
4167
                                var_dump("score: $score");
4168
                                var_dump("max_score: $max_score");
4169
                                var_dump("lpPartialTotal: $lpPartialTotal");
4170
                            }
4171
                        }
4172
                    }
4173
4174
                    if (in_array($row_max_score['item_type'], ['quiz', 'sco'])) {
4175
                        // Normal way
4176
                        if ($use_max_score[$lp_id]) {
4177
                            $count_items++;
4178
                        } else {
4179
                            if ($max_score != '') {
4180
                                $count_items++;
4181
                            }
4182
                        }
4183
                        if ($debug) {
4184
                            echo '$count_items: '.$count_items;
4185
                        }
4186
                    }
4187
                } // end for
4188
4189
                $score_of_scorm_calculate += $count_items ? (($lpPartialTotal / $count_items) * 100) : 0;
4190
                $global_result += $score_of_scorm_calculate;
4191
4192
                if ($debug) {
4193
                    var_dump("count_items: $count_items");
4194
                    var_dump("score_of_scorm_calculate: $score_of_scorm_calculate");
4195
                    var_dump("global_result: $global_result");
4196
                }
4197
            } // end while
4198
        }
4199
4200
        $lp_with_quiz = 0;
4201
        foreach ($lp_list as $lp_id) {
4202
            // Check if LP have a score we assume that all SCO have an score
4203
            $sql = "SELECT count(id) as count
4204
                    FROM $lp_item_table
4205
                    WHERE
4206
                        c_id = $course_id AND
4207
                        (item_type = 'quiz' OR item_type = 'sco') AND
4208
                        lp_id = ".$lp_id;
4209
            $result_have_quiz = Database::query($sql);
4210
            if (Database::num_rows($result_have_quiz) > 0) {
4211
                $row = Database::fetch_array($result_have_quiz, 'ASSOC');
4212
                if (is_numeric($row['count']) && $row['count'] != 0) {
4213
                    $lp_with_quiz++;
4214
                }
4215
            }
4216
        }
4217
4218
        if ($debug) {
4219
            echo '<h3>$lp_with_quiz '.$lp_with_quiz.' </h3>';
4220
        }
4221
        if ($debug) {
4222
            echo '<h3>Final return</h3>';
4223
        }
4224
4225
        if ($lp_with_quiz != 0) {
4226
            if (!$return_array) {
4227
                $score_of_scorm_calculate = round($global_result / $lp_with_quiz, 2);
4228
                if ($debug) {
4229
                    var_dump($score_of_scorm_calculate);
4230
                }
4231
                if (empty($lp_ids)) {
4232
                    if ($debug) {
4233
                        echo '<h2>All lps fix: '.$score_of_scorm_calculate.'</h2>';
4234
                    }
4235
                }
4236
4237
                return $score_of_scorm_calculate;
4238
            }
4239
4240
            if ($debug) {
4241
                var_dump($global_result, $lp_with_quiz);
4242
            }
4243
4244
            return [$global_result, $lp_with_quiz];
4245
        }
4246
4247
        return '-';
4248
    }
4249
4250
    /**
4251
     * This function gets:
4252
     * 1. The score average from all SCORM Test items in all LP in a course-> All the answers / All the max scores.
4253
     * 2. The score average from all Tests (quiz) in all LP in a course-> All the answers / All the max scores.
4254
     * 3. And finally it will return the average between 1. and 2.
4255
     * This function does not take the results of a Test out of a LP.
4256
     *
4257
     * @param int|array $student_id  Array of user ids or an user id
4258
     * @param string    $course_code Course code
4259
     * @param array     $lp_ids      List of LP ids
4260
     * @param int       $session_id  Session id (optional), if param $session_id is 0(default)
4261
     *                               it'll return results including sessions, 0 = session is not filtered
4262
     *
4263
     * @return string value (number %) Which represents a round integer explain in got in 3
4264
     */
4265
    public static function getAverageStudentScore(
4266
        $student_id,
4267
        $course_code = '',
4268
        $lp_ids = [],
4269
        $session_id = 0
4270
    ) {
4271
        if (empty($student_id)) {
4272
            return 0;
4273
        }
4274
4275
        $conditions = [];
4276
        if (!empty($course_code)) {
4277
            $course = api_get_course_info($course_code);
4278
            $courseId = $course['real_id'];
4279
            $conditions[] = " lp.c_id = $courseId";
4280
        }
4281
4282
        // Get course tables names
4283
        $lp_table = Database::get_course_table(TABLE_LP_MAIN);
4284
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
4285
        $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
4286
        $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
4287
4288
        // Compose a filter based on optional learning paths list given
4289
        if (!empty($lp_ids) && count($lp_ids) > 0) {
4290
            $conditions[] = ' lp.id IN ('.implode(',', $lp_ids).') ';
4291
        }
4292
4293
        // Compose a filter based on optional session id
4294
        $session_id = (int) $session_id;
4295
        if (!empty($session_id)) {
4296
            $conditions[] = " lp_view.session_id = $session_id ";
4297
        }
4298
4299
        if (is_array($student_id)) {
4300
            array_walk($student_id, 'intval');
4301
            $conditions[] = " lp_view.user_id IN (".implode(',', $student_id).") ";
4302
        } else {
4303
            $student_id = (int) $student_id;
4304
            $conditions[] = " lp_view.user_id = $student_id ";
4305
        }
4306
4307
        $conditionsToString = implode(' AND ', $conditions);
4308
        $sql = "SELECT
4309
                    SUM(lp_iv.score) sum_score,
4310
                    SUM(lp_i.max_score) sum_max_score
4311
                FROM $lp_table as lp
4312
                INNER JOIN $lp_item_table as lp_i
4313
                ON lp.iid = lp_id AND lp.c_id = lp_i.c_id
4314
                INNER JOIN $lp_view_table as lp_view
4315
                ON lp_view.lp_id = lp_i.lp_id AND lp_view.c_id = lp_i.c_id
4316
                INNER JOIN $lp_item_view_table as lp_iv
4317
                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
4318
                WHERE (lp_i.item_type='sco' OR lp_i.item_type='".TOOL_QUIZ."') AND
4319
                $conditionsToString
4320
        ";
4321
        $result = Database::query($sql);
4322
        $row = Database::fetch_array($result, 'ASSOC');
4323
4324
        if (empty($row['sum_max_score'])) {
4325
            return 0;
4326
        }
4327
4328
        return ($row['sum_score'] / $row['sum_max_score']) * 100;
4329
    }
4330
4331
    /**
4332
     * This function gets time spent in learning path for a student inside a course.
4333
     *
4334
     * @param int|array $student_id  Student id(s)
4335
     * @param string    $course_code Course code
4336
     * @param array     $lp_ids      Limit average to listed lp ids
4337
     * @param int       $session_id  Session id (optional), if param $session_id is null(default)
4338
     *                               it'll return results including sessions, 0 = session is not filtered
4339
     *
4340
     * @return int Total time in seconds
4341
     */
4342
    public static function get_time_spent_in_lp(
4343
        $student_id,
4344
        $course_code,
4345
        $lp_ids = [],
4346
        $session_id = 0
4347
    ) {
4348
        $course = api_get_course_info($course_code);
4349
        $student_id = (int) $student_id;
4350
        $session_id = (int) $session_id;
4351
        $total_time = 0;
4352
4353
        if (!empty($course)) {
4354
            $lpTable = Database::get_course_table(TABLE_LP_MAIN);
4355
            $lpItemTable = Database::get_course_table(TABLE_LP_ITEM);
4356
            $lpViewTable = Database::get_course_table(TABLE_LP_VIEW);
4357
            $lpItemViewTable = Database::get_course_table(TABLE_LP_ITEM_VIEW);
4358
            $trackExercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
4359
            $course_id = $course['real_id'];
4360
4361
            // Compose a filter based on optional learning paths list given
4362
            $condition_lp = '';
4363
            if (count($lp_ids) > 0) {
4364
                $condition_lp = " AND id IN(".implode(',', $lp_ids).") ";
4365
            }
4366
4367
            // Check the real number of LPs corresponding to the filter in the
4368
            // database (and if no list was given, get them all)
4369
            $sql = "SELECT DISTINCT(id) FROM $lpTable
4370
                    WHERE c_id = $course_id $condition_lp";
4371
            $result = Database::query($sql);
4372
            $session_condition = api_get_session_condition($session_id);
4373
4374
            // calculates time
4375
            if (Database::num_rows($result) > 0) {
4376
                while ($row = Database::fetch_array($result)) {
4377
                    $lp_id = (int) $row['id'];
4378
4379
                    // Start Exercise in LP total_time
4380
                    // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
4381
                    $list = learnpath::get_flat_ordered_items_list($lp_id, 0, $course_id);
4382
                    foreach ($list as $itemId) {
4383
                        $sql = "SELECT max(view_count)
4384
                                FROM $lpViewTable
4385
                                WHERE
4386
                                    c_id = $course_id AND
4387
                                    lp_id = $lp_id AND
4388
                                    user_id = $student_id
4389
                                    $session_condition";
4390
                        $res = Database::query($sql);
4391
                        $view = '';
4392
                        if (Database::num_rows($res) > 0) {
4393
                            $myrow = Database::fetch_array($res);
4394
                            $view = $myrow[0];
4395
                        }
4396
                        $viewCondition = null;
4397
                        if (!empty($view)) {
4398
                            $viewCondition = " AND v.view_count = $view  ";
4399
                        }
4400
                        $sql = "SELECT
4401
                            iv.iid,
4402
                            iv.total_time as mytime,
4403
                            i.id as myid,
4404
                            iv.view_count as iv_view_count,
4405
                            path
4406
                        FROM $lpItemTable as i
4407
                        INNER JOIN $lpItemViewTable as iv
4408
                        ON (i.id = iv.lp_item_id AND i.c_id = iv.c_id)
4409
                        INNER JOIN $lpViewTable as v
4410
                        ON (iv.lp_view_id = v.id AND v.c_id = iv.c_id)
4411
                        WHERE
4412
                            v.c_id = $course_id AND
4413
                            i.id = $itemId AND
4414
                            i.lp_id = $lp_id  AND
4415
                            v.user_id = $student_id AND
4416
                            item_type = 'quiz' AND
4417
                            path <> '' AND
4418
                            v.session_id = $session_id
4419
                            $viewCondition
4420
                        ORDER BY iv.view_count DESC ";
4421
4422
                        $resultRow = Database::query($sql);
4423
                        if (Database::num_rows($resultRow)) {
4424
                            $row = Database::fetch_array($resultRow);
4425
                            $totalTimeInLpItemView = $row['mytime'];
4426
                            $lpItemViewId = $row['iid'];
4427
4428
                            $sql = 'SELECT SUM(exe_duration) exe_duration
4429
                                    FROM '.$trackExercises.'
4430
                                    WHERE
4431
                                        exe_exo_id="'.$row['path'].'" AND
4432
                                        exe_user_id="'.$student_id.'" AND
4433
                                        orig_lp_id = "'.$lp_id.'" AND
4434
                                        orig_lp_item_id = "'.$row['myid'].'" AND
4435
                                        c_id = '.$course_id.' AND
4436
                                        status <> "incomplete" AND
4437
                                        session_id = '.$session_id.'
4438
                                     ORDER BY exe_date DESC ';
4439
4440
                            $sumScoreResult = Database::query($sql);
4441
                            $durationRow = Database::fetch_array($sumScoreResult, 'ASSOC');
4442
                            if (!empty($durationRow['exe_duration'])) {
4443
                                $exeDuration = $durationRow['exe_duration'];
4444
                                if ($exeDuration != $totalTimeInLpItemView
4445
                                    && !empty($lpItemViewId)
4446
                                    && !empty($exeDuration)
4447
                                ) {
4448
                                    // Update c_lp_item_view.total_time
4449
                                    $sqlUpdate = "UPDATE $lpItemViewTable SET total_time = '$exeDuration'
4450
                                                  WHERE iid = ".$lpItemViewId;
4451
                                    Database::query($sqlUpdate);
4452
                                }
4453
                            }
4454
                        }
4455
                    }
4456
4457
                    // End total_time fix
4458
4459
                    // Calculate total time
4460
                    $sql = "SELECT SUM(total_time)
4461
                            FROM $lpItemViewTable AS item_view
4462
                            INNER JOIN $lpViewTable AS view
4463
                            ON (
4464
                                item_view.lp_view_id = view.id AND
4465
                                item_view.c_id = view.c_id
4466
                            )
4467
                            WHERE
4468
                                item_view.c_id = $course_id AND
4469
                                view.c_id = $course_id AND
4470
                                view.lp_id = $lp_id AND
4471
                                view.user_id = $student_id AND
4472
                                session_id = $session_id";
4473
4474
                    $rs = Database::query($sql);
4475
                    if (Database::num_rows($rs) > 0) {
4476
                        $total_time += Database::result($rs, 0, 0);
4477
                    }
4478
                }
4479
            }
4480
        }
4481
4482
        return $total_time;
4483
    }
4484
4485
    /**
4486
     * This function gets last connection time to one learning path.
4487
     *
4488
     * @param int|array $student_id  Student id(s)
4489
     * @param string    $course_code Course code
4490
     * @param int       $lp_id       Learning path id
4491
     * @param int       $session_id
4492
     *
4493
     * @return int last connection timestamp
4494
     */
4495
    public static function get_last_connection_time_in_lp(
4496
        $student_id,
4497
        $course_code,
4498
        $lp_id,
4499
        $session_id = 0
4500
    ) {
4501
        $course = api_get_course_info($course_code);
4502
4503
        if (empty($course)) {
4504
            return 0;
4505
        }
4506
4507
        $course_id = $course['real_id'];
4508
        $student_id = (int) $student_id;
4509
        $lp_id = (int) $lp_id;
4510
        $session_id = (int) $session_id;
4511
        $lastTime = 0;
4512
4513
        // Use new system
4514
        if (self::minimumTimeAvailable($session_id, $course_id)) {
4515
            $sql = "SELECT MAX(date_reg) max
4516
                    FROM track_e_access_complete
4517
                    WHERE
4518
                        user_id = $student_id AND
4519
                        c_id = $course_id AND
4520
                        session_id = $session_id AND
4521
                        tool = 'learnpath' AND
4522
                        tool_id = $lp_id AND
4523
                        action = 'view' AND
4524
                        login_as = 0
4525
                    ORDER BY date_reg ASC
4526
                    LIMIT 1";
4527
            $rs = Database::query($sql);
4528
4529
            $lastConnection = 0;
4530
            if (Database::num_rows($rs) > 0) {
4531
                $value = Database::fetch_array($rs);
4532
                if (isset($value['max']) && !empty($value['max'])) {
4533
                    $lastConnection = api_strtotime($value['max'], 'UTC');
4534
                }
4535
            }
4536
4537
            if (!empty($lastConnection)) {
4538
                return $lastConnection;
4539
            }
4540
        }
4541
4542
        if (!empty($course)) {
4543
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
4544
            $t_lpv = Database::get_course_table(TABLE_LP_VIEW);
4545
            $t_lpiv = Database::get_course_table(TABLE_LP_ITEM_VIEW);
4546
4547
            // Check the real number of LPs corresponding to the filter in the
4548
            // database (and if no list was given, get them all)
4549
            $sql = "SELECT id FROM $lp_table
4550
                    WHERE c_id = $course_id AND id = $lp_id ";
4551
            $row = Database::query($sql);
4552
            $count = Database::num_rows($row);
4553
4554
            // calculates last connection time
4555
            if ($count > 0) {
4556
                $sql = 'SELECT MAX(start_time)
4557
                        FROM '.$t_lpiv.' AS item_view
4558
                        INNER JOIN '.$t_lpv.' AS view
4559
                        ON (item_view.lp_view_id = view.id AND item_view.c_id = view.c_id)
4560
                        WHERE
4561
                            item_view.c_id = '.$course_id.' AND
4562
                            view.c_id = '.$course_id.' AND
4563
                            view.lp_id = '.$lp_id.' AND
4564
                            view.user_id = '.$student_id.' AND
4565
                            view.session_id = '.$session_id;
4566
                $rs = Database::query($sql);
4567
                if (Database::num_rows($rs) > 0) {
4568
                    $lastTime = Database::result($rs, 0, 0);
4569
                }
4570
            }
4571
        }
4572
4573
        return $lastTime;
4574
    }
4575
4576
    /**
4577
     * Gets the last connection time in the last learning path for a student in a course session.
4578
     */
4579
    public static function getLastConnectionTimeInSessionCourseLp(
4580
        int $studentId,
4581
        string $courseCode,
4582
        int $sessionId = 0
4583
    ): int {
4584
        $course = api_get_course_info($courseCode);
4585
4586
        if (empty($course)) {
4587
            return 0;
4588
        }
4589
4590
        $courseId = $course['real_id'];
4591
        $lastTime = 0;
4592
4593
        $tLpv = Database::get_course_table(TABLE_LP_VIEW);
4594
        $tLpiv = Database::get_course_table(TABLE_LP_ITEM_VIEW);
4595
4596
        $sql = 'SELECT MAX(item_view.start_time) as last_time
4597
            FROM '.$tLpiv.' AS item_view
4598
            INNER JOIN '.$tLpv.' AS view
4599
            ON (item_view.lp_view_id = view.id)
4600
            WHERE
4601
                view.c_id = '.$courseId.' AND
4602
                view.user_id = '.$studentId.' AND
4603
                view.session_id = '.$sessionId;
4604
4605
        $rs = Database::query($sql);
4606
        if ($rs && Database::num_rows($rs) > 0) {
4607
            $lastTime = (int) Database::result($rs, 0, 'last_time');
4608
        }
4609
4610
        return $lastTime;
4611
    }
4612
4613
    public static function getFirstConnectionTimeInLp(
4614
        $student_id,
4615
        $course_code,
4616
        $lp_id,
4617
        $session_id = 0
4618
    ) {
4619
        $course = api_get_course_info($course_code);
4620
        $student_id = (int) $student_id;
4621
        $lp_id = (int) $lp_id;
4622
        $session_id = (int) $session_id;
4623
        $time = 0;
4624
4625
        if (!empty($course)) {
4626
            $course_id = $course['real_id'];
4627
            $lp_table = Database::get_course_table(TABLE_LP_MAIN);
4628
            $t_lpv = Database::get_course_table(TABLE_LP_VIEW);
4629
            $t_lpiv = Database::get_course_table(TABLE_LP_ITEM_VIEW);
4630
4631
            // Check the real number of LPs corresponding to the filter in the
4632
            // database (and if no list was given, get them all)
4633
            $sql = "SELECT id FROM $lp_table
4634
                    WHERE c_id = $course_id AND id = $lp_id ";
4635
            $row = Database::query($sql);
4636
            $count = Database::num_rows($row);
4637
4638
            // calculates first connection time
4639
            if ($count > 0) {
4640
                $sql = 'SELECT MIN(start_time)
4641
                        FROM '.$t_lpiv.' AS item_view
4642
                        INNER JOIN '.$t_lpv.' AS view
4643
                        ON (item_view.lp_view_id = view.id AND item_view.c_id = view.c_id)
4644
                        WHERE
4645
                            status != "not attempted" AND
4646
                            item_view.c_id = '.$course_id.' AND
4647
                            view.c_id = '.$course_id.' AND
4648
                            view.lp_id = '.$lp_id.' AND
4649
                            view.user_id = '.$student_id.' AND
4650
                            view.session_id = '.$session_id;
4651
                $rs = Database::query($sql);
4652
                if (Database::num_rows($rs) > 0) {
4653
                    $time = Database::result($rs, 0, 0);
4654
                }
4655
            }
4656
        }
4657
4658
        return $time;
4659
    }
4660
4661
    /**
4662
     * gets the list of students followed by coach.
4663
     *
4664
     * @param int $coach_id Coach id
4665
     *
4666
     * @return array List of students
4667
     */
4668
    public static function get_student_followed_by_coach($coach_id)
4669
    {
4670
        $coach_id = intval($coach_id);
4671
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4672
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4673
        $tbl_session_user = Database::get_main_table(TABLE_MAIN_SESSION_USER);
4674
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4675
4676
        $students = [];
4677
        // At first, courses where $coach_id is coach of the course //
4678
        $sql = 'SELECT session_id, c_id
4679
                FROM '.$tbl_session_course_user.'
4680
                WHERE user_id='.$coach_id.' AND status=2';
4681
4682
        if (api_is_multiple_url_enabled()) {
4683
            $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4684
            $access_url_id = api_get_current_access_url_id();
4685
            if (-1 != $access_url_id) {
4686
                $sql = 'SELECT scu.session_id, scu.c_id
4687
                        FROM '.$tbl_session_course_user.' scu
4688
                        INNER JOIN '.$tbl_session_rel_access_url.'  sru
4689
                        ON (scu.session_id=sru.session_id)
4690
                        WHERE
4691
                            scu.user_id='.$coach_id.' AND
4692
                            scu.status=2 AND
4693
                            sru.access_url_id = '.$access_url_id;
4694
            }
4695
        }
4696
4697
        $result = Database::query($sql);
4698
4699
        while ($a_courses = Database::fetch_array($result)) {
4700
            $courseId = $a_courses['c_id'];
4701
            $id_session = $a_courses['session_id'];
4702
4703
            $sql = "SELECT DISTINCT srcru.user_id
4704
                    FROM $tbl_session_course_user AS srcru
4705
                    INNER JOIN $tbl_session_user sru
4706
                    ON (srcru.user_id = sru.user_id AND srcru.session_id = sru.session_id)
4707
                    WHERE
4708
                        sru.relation_type <> ".SESSION_RELATION_TYPE_RRHH." AND
4709
                        srcru.c_id = '$courseId' AND
4710
                        srcru.session_id = '$id_session'";
4711
4712
            $rs = Database::query($sql);
4713
            while ($row = Database::fetch_array($rs)) {
4714
                $students[$row['user_id']] = $row['user_id'];
4715
            }
4716
        }
4717
4718
        // Then, courses where $coach_id is coach of the session
4719
        $sql = 'SELECT session_course_user.user_id
4720
                FROM '.$tbl_session_course_user.' as session_course_user
4721
                INNER JOIN '.$tbl_session_user.' sru
4722
                ON session_course_user.user_id = sru.user_id AND session_course_user.session_id = sru.session_id
4723
                INNER JOIN '.$tbl_session_course.' as session_course
4724
                ON session_course.c_id = session_course_user.c_id
4725
                AND session_course_user.session_id = session_course.session_id
4726
                INNER JOIN '.$tbl_session.' as session
4727
                ON session.id = session_course.session_id
4728
                AND session.id_coach = '.$coach_id;
4729
        if (api_is_multiple_url_enabled()) {
4730
            $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4731
            $access_url_id = api_get_current_access_url_id();
4732
            if (-1 != $access_url_id) {
4733
                $sql = 'SELECT session_course_user.user_id
4734
                        FROM '.$tbl_session_course_user.' as session_course_user
4735
                        INNER JOIN '.$tbl_session_user.' sru
4736
                        ON session_course_user.user_id = sru.user_id AND
4737
                           session_course_user.session_id = sru.session_id
4738
                        INNER JOIN '.$tbl_session_course.' as session_course
4739
                        ON session_course.c_id = session_course_user.c_id AND
4740
                        session_course_user.session_id = session_course.session_id
4741
                        INNER JOIN '.$tbl_session.' as session
4742
                        ON session.id = session_course.session_id AND
4743
                        session.id_coach = '.$coach_id.'
4744
                        INNER JOIN '.$tbl_session_rel_access_url.' session_rel_url
4745
                        ON session.id = session_rel_url.session_id
4746
                        WHERE access_url_id = '.$access_url_id;
4747
            }
4748
        }
4749
4750
        $result = Database::query($sql);
4751
        while ($row = Database::fetch_array($result)) {
4752
            $students[$row['user_id']] = $row['user_id'];
4753
        }
4754
4755
        return $students;
4756
    }
4757
4758
    /**
4759
     * Check if a coach is allowed to follow a student.
4760
     *
4761
     * @param    int        Coach id
4762
     * @param    int        Student id
4763
     *
4764
     * @return bool
4765
     */
4766
    public static function is_allowed_to_coach_student($coach_id, $student_id)
4767
    {
4768
        $coach_id = intval($coach_id);
4769
        $student_id = intval($student_id);
4770
4771
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4772
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4773
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4774
4775
        // At first, courses where $coach_id is coach of the course
4776
        $sql = 'SELECT 1 FROM '.$tbl_session_course_user.'
4777
                WHERE user_id='.$coach_id.' AND status=2';
4778
        $result = Database::query($sql);
4779
        if (Database::num_rows($result) > 0) {
4780
            return true;
4781
        }
4782
4783
        // Then, courses where $coach_id is coach of the session
4784
        $sql = 'SELECT session_course_user.user_id
4785
                FROM '.$tbl_session_course_user.' as session_course_user
4786
                INNER JOIN '.$tbl_session_course.' as session_course
4787
                ON session_course.c_id = session_course_user.c_id
4788
                INNER JOIN '.$tbl_session.' as session
4789
                ON session.id = session_course.session_id
4790
                AND session.id_coach = '.$coach_id.'
4791
                WHERE user_id = '.$student_id;
4792
        $result = Database::query($sql);
4793
        if (Database::num_rows($result) > 0) {
4794
            return true;
4795
        }
4796
4797
        return false;
4798
    }
4799
4800
    /**
4801
     * Get courses followed by coach.
4802
     *
4803
     * @param     int        Coach id
4804
     * @param    int        Session id (optional)
4805
     *
4806
     * @return array Courses list
4807
     */
4808
    public static function get_courses_followed_by_coach($coach_id, $id_session = 0)
4809
    {
4810
        $coach_id = intval($coach_id);
4811
        if (!empty($id_session)) {
4812
            $id_session = intval($id_session);
4813
        }
4814
4815
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4816
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
4817
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4818
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
4819
        $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
4820
4821
        // At first, courses where $coach_id is coach of the course.
4822
        $sql = 'SELECT DISTINCT c.code
4823
                FROM '.$tbl_session_course_user.' sc
4824
                INNER JOIN '.$tbl_course.' c
4825
                ON (c.id = sc.c_id)
4826
                WHERE user_id = '.$coach_id.' AND status = 2';
4827
4828
        if (api_is_multiple_url_enabled()) {
4829
            $access_url_id = api_get_current_access_url_id();
4830
            if (-1 != $access_url_id) {
4831
                $sql = 'SELECT DISTINCT c.code
4832
                        FROM '.$tbl_session_course_user.' scu
4833
                        INNER JOIN '.$tbl_course.' c
4834
                        ON (c.code = scu.c_id)
4835
                        INNER JOIN '.$tbl_course_rel_access_url.' cru
4836
                        ON (c.id = cru.c_id)
4837
                        WHERE
4838
                            scu.user_id='.$coach_id.' AND
4839
                            scu.status=2 AND
4840
                            cru.access_url_id = '.$access_url_id;
4841
            }
4842
        }
4843
4844
        if (!empty($id_session)) {
4845
            $sql .= ' AND session_id='.$id_session;
4846
        }
4847
4848
        $courseList = [];
4849
        $result = Database::query($sql);
4850
        while ($row = Database::fetch_array($result)) {
4851
            $courseList[$row['code']] = $row['code'];
4852
        }
4853
4854
        // Then, courses where $coach_id is coach of the session
4855
        $sql = 'SELECT DISTINCT course.code
4856
                FROM '.$tbl_session_course.' as session_course
4857
                INNER JOIN '.$tbl_session.' as session
4858
                    ON session.id = session_course.session_id
4859
                    AND session.id_coach = '.$coach_id.'
4860
                INNER JOIN '.$tbl_course.' as course
4861
                    ON course.id = session_course.c_id';
4862
4863
        if (api_is_multiple_url_enabled()) {
4864
            $tbl_course_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
4865
            $access_url_id = api_get_current_access_url_id();
4866
            if (-1 != $access_url_id) {
4867
                $sql = 'SELECT DISTINCT c.code
4868
                    FROM '.$tbl_session_course.' as session_course
4869
                    INNER JOIN '.$tbl_course.' c
4870
                    ON (c.id = session_course.c_id)
4871
                    INNER JOIN '.$tbl_session.' as session
4872
                    ON session.id = session_course.session_id
4873
                        AND session.id_coach = '.$coach_id.'
4874
                    INNER JOIN '.$tbl_course.' as course
4875
                        ON course.id = session_course.c_id
4876
                     INNER JOIN '.$tbl_course_rel_access_url.' course_rel_url
4877
                    ON (course_rel_url.c_id = c.id)';
4878
            }
4879
        }
4880
4881
        if (!empty($id_session)) {
4882
            $sql .= ' WHERE session_course.session_id='.$id_session;
4883
            if (api_is_multiple_url_enabled()) {
4884
                $sql .= ' AND access_url_id = '.$access_url_id;
4885
            }
4886
        } else {
4887
            if (api_is_multiple_url_enabled()) {
4888
                $sql .= ' WHERE access_url_id = '.$access_url_id;
4889
            }
4890
        }
4891
4892
        $result = Database::query($sql);
4893
        while ($row = Database::fetch_array($result)) {
4894
            $courseList[$row['code']] = $row['code'];
4895
        }
4896
4897
        return $courseList;
4898
    }
4899
4900
    /**
4901
     * Get sessions coached by user.
4902
     *
4903
     * @param int    $coach_id
4904
     * @param int    $start
4905
     * @param int    $limit
4906
     * @param bool   $getCount
4907
     * @param string $keyword
4908
     * @param string $description
4909
     * @param string $orderByName
4910
     * @param string $orderByDirection
4911
     * @param array  $options
4912
     */
4913
    public static function get_sessions_coached_by_user(
4914
        $coach_id,
4915
        $start = 0,
4916
        $limit = 0,
4917
        $getCount = false,
4918
        $keyword = '',
4919
        $description = '',
4920
        $orderByName = '',
4921
        $orderByDirection = '',
4922
        $options = []
4923
    ) {
4924
        // table definition
4925
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
4926
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
4927
        $coach_id = (int) $coach_id;
4928
4929
        $select = ' SELECT * FROM ';
4930
        if ($getCount) {
4931
            $select = ' SELECT count(DISTINCT id) as count FROM ';
4932
        }
4933
4934
        $limitCondition = null;
4935
        if (!empty($start) && !empty($limit)) {
4936
            $limitCondition = " LIMIT ".intval($start).", ".intval($limit);
4937
        }
4938
4939
        $keywordCondition = null;
4940
        if (!empty($keyword)) {
4941
            $keyword = Database::escape_string($keyword);
4942
            $keywordCondition = " AND (name LIKE '%$keyword%' ) ";
4943
4944
            if (!empty($description)) {
4945
                $description = Database::escape_string($description);
4946
                $keywordCondition = " AND (name LIKE '%$keyword%' OR description LIKE '%$description%' ) ";
4947
            }
4948
        }
4949
4950
        $extraFieldModel = new ExtraFieldModel('session');
4951
        $conditions = $extraFieldModel->parseConditions($options);
4952
        $sqlInjectJoins = $conditions['inject_joins'];
4953
        $extraFieldsConditions = $conditions['where'];
4954
        $sqlInjectWhere = $conditions['inject_where'];
4955
        $injectExtraFields = $conditions['inject_extra_fields'];
4956
4957
        $tbl_session_rel_access_url = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
4958
        $access_url_id = api_get_current_access_url_id();
4959
4960
        $orderBy = '';
4961
        if (!empty($orderByName)) {
4962
            if (in_array($orderByName, ['name', 'access_start_date'])) {
4963
                $orderByDirection = in_array(strtolower($orderByDirection), ['asc', 'desc']) ? $orderByDirection : 'asc';
4964
                $orderByName = Database::escape_string($orderByName);
4965
                $orderBy .= " ORDER BY `$orderByName` $orderByDirection";
4966
            }
4967
        }
4968
4969
        $sql = "
4970
            $select
4971
            (
4972
                SELECT DISTINCT
4973
                    s.id,
4974
                    name,
4975
                    $injectExtraFields
4976
                    access_start_date,
4977
                    access_end_date
4978
                FROM $tbl_session s
4979
                INNER JOIN $tbl_session_rel_access_url session_rel_url
4980
                ON (s.id = session_rel_url.session_id)
4981
                $sqlInjectJoins
4982
                WHERE
4983
                    id_coach = $coach_id AND
4984
                    access_url_id = $access_url_id
4985
                    $keywordCondition
4986
                    $extraFieldsConditions
4987
                    $sqlInjectWhere
4988
            UNION
4989
                SELECT DISTINCT
4990
                    s.id,
4991
                    s.name,
4992
                    $injectExtraFields
4993
                    s.access_start_date,
4994
                    s.access_end_date
4995
                FROM $tbl_session as s
4996
                INNER JOIN $tbl_session_course_user as session_course_user
4997
                ON
4998
                    s.id = session_course_user.session_id AND
4999
                    session_course_user.user_id = $coach_id AND
5000
                    session_course_user.status = 2
5001
                INNER JOIN $tbl_session_rel_access_url session_rel_url
5002
                ON (s.id = session_rel_url.session_id)
5003
                $sqlInjectJoins
5004
                WHERE
5005
                    access_url_id = $access_url_id
5006
                    $keywordCondition
5007
                    $extraFieldsConditions
5008
                    $sqlInjectWhere
5009
            ) as sessions $limitCondition $orderBy
5010
            ";
5011
5012
        $rs = Database::query($sql);
5013
        if ($getCount) {
5014
            $row = Database::fetch_array($rs);
5015
5016
            return $row['count'];
5017
        }
5018
5019
        $sessions = [];
5020
        while ($row = Database::fetch_array($rs)) {
5021
            if ($row['access_start_date'] === '0000-00-00 00:00:00') {
5022
                $row['access_start_date'] = null;
5023
            }
5024
5025
            $sessions[$row['id']] = $row;
5026
        }
5027
5028
        if (!empty($sessions)) {
5029
            foreach ($sessions as &$session) {
5030
                if (empty($session['access_start_date'])) {
5031
                    $session['status'] = get_lang('SessionActive');
5032
                } else {
5033
                    $time_start = api_strtotime($session['access_start_date'], 'UTC');
5034
                    $time_end = api_strtotime($session['access_end_date'], 'UTC');
5035
                    if ($time_start < time() && time() < $time_end) {
5036
                        $session['status'] = get_lang('SessionActive');
5037
                    } else {
5038
                        if (time() < $time_start) {
5039
                            $session['status'] = get_lang('SessionFuture');
5040
                        } else {
5041
                            if (time() > $time_end) {
5042
                                $session['status'] = get_lang('SessionPast');
5043
                            }
5044
                        }
5045
                    }
5046
                }
5047
            }
5048
        }
5049
5050
        return $sessions;
5051
    }
5052
5053
    /**
5054
     * Get courses list from a session.
5055
     *
5056
     * @param    int        Session id
5057
     *
5058
     * @return array Courses list
5059
     */
5060
    public static function get_courses_list_from_session($session_id)
5061
    {
5062
        $session_id = (int) $session_id;
5063
5064
        // table definition
5065
        $tbl_session_course = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
5066
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
5067
5068
        $sql = "SELECT DISTINCT code, c_id
5069
                FROM $tbl_session_course sc
5070
                INNER JOIN $courseTable c
5071
                ON sc.c_id = c.id
5072
                WHERE session_id= $session_id";
5073
5074
        $result = Database::query($sql);
5075
5076
        $courses = [];
5077
        while ($row = Database::fetch_array($result)) {
5078
            $courses[$row['code']] = $row;
5079
        }
5080
5081
        return $courses;
5082
    }
5083
5084
    /**
5085
     * Count the number of documents that an user has uploaded to a course.
5086
     *
5087
     * @param    int|array   Student id(s)
5088
     * @param    string      Course code
5089
     * @param    int         Session id (optional),
5090
     * if param $session_id is null(default)
5091
     * return count of assignments including sessions, 0 = session is not filtered
5092
     *
5093
     * @return int Number of documents
5094
     */
5095
    public static function count_student_uploaded_documents(
5096
        $student_id,
5097
        $course_code,
5098
        $session_id = null
5099
    ) {
5100
        // get the information of the course
5101
        $a_course = api_get_course_info($course_code);
5102
        if (!empty($a_course)) {
5103
            // table definition
5104
            $tbl_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
5105
            $tbl_document = Database::get_course_table(TABLE_DOCUMENT);
5106
            $course_id = $a_course['real_id'];
5107
            if (is_array($student_id)) {
5108
                $studentList = array_map('intval', $student_id);
5109
                $condition_user = " AND ip.insert_user_id IN ('".implode(',', $studentList)."') ";
5110
            } else {
5111
                $student_id = (int) $student_id;
5112
                $condition_user = " AND ip.insert_user_id = '$student_id' ";
5113
            }
5114
5115
            $condition_session = null;
5116
            if (isset($session_id)) {
5117
                $session_id = (int) $session_id;
5118
                $condition_session = " AND pub.session_id = $session_id ";
5119
            }
5120
5121
            $sql = "SELECT count(ip.tool) AS count
5122
                    FROM $tbl_item_property ip
5123
                    INNER JOIN $tbl_document pub
5124
                    ON (ip.ref = pub.iid AND ip.c_id = pub.c_id)
5125
                    WHERE
5126
                        ip.c_id  = $course_id AND
5127
                        pub.c_id  = $course_id AND
5128
                        pub.filetype ='file' AND
5129
                        ip.tool = 'document'
5130
                        $condition_user $condition_session ";
5131
            $rs = Database::query($sql);
5132
            $row = Database::fetch_array($rs, 'ASSOC');
5133
5134
            return $row['count'];
5135
        }
5136
5137
        return null;
5138
    }
5139
5140
    /**
5141
     * Count assignments per student.
5142
     *
5143
     * @param array|int $student_id
5144
     * @param string    $course_code
5145
     * @param int       $session_id  if param is null(default) return count of assignments including sessions,
5146
     *                               0 = session is not filtered
5147
     *
5148
     * @return int Count of assignments
5149
     */
5150
    public static function count_student_assignments(
5151
        $student_id,
5152
        $course_code = null,
5153
        $session_id = null
5154
    ) {
5155
        if (empty($student_id)) {
5156
            return 0;
5157
        }
5158
5159
        $conditions = [];
5160
5161
        // Get the information of the course
5162
        $a_course = api_get_course_info($course_code);
5163
        if (!empty($a_course)) {
5164
            $course_id = $a_course['real_id'];
5165
            $conditions[] = " ip.c_id  = $course_id AND pub.c_id  = $course_id ";
5166
        }
5167
5168
        // table definition
5169
        $tbl_item_property = Database::get_course_table(TABLE_ITEM_PROPERTY);
5170
        $tbl_student_publication = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
5171
5172
        if (is_array($student_id)) {
5173
            $studentList = array_map('intval', $student_id);
5174
            $conditions[] = " ip.insert_user_id IN ('".implode("','", $studentList)."') ";
5175
        } else {
5176
            $student_id = (int) $student_id;
5177
            $conditions[] = " ip.insert_user_id = '$student_id' ";
5178
        }
5179
5180
        $conditions[] = ' pub.active <> 2 ';
5181
        $conditionToString = implode(' AND ', $conditions);
5182
        $sessionCondition = api_get_session_condition($session_id, true, false, 'pub.session_id');
5183
        $conditionToString .= $sessionCondition;
5184
5185
        $sql = "SELECT count(ip.tool) as count
5186
                FROM $tbl_item_property ip
5187
                INNER JOIN $tbl_student_publication pub
5188
                ON (ip.ref = pub.iid AND ip.c_id = pub.c_id)
5189
                WHERE
5190
                    ip.tool='work' AND
5191
                    $conditionToString";
5192
        $rs = Database::query($sql);
5193
        $row = Database::fetch_array($rs, 'ASSOC');
5194
5195
        return $row['count'];
5196
    }
5197
5198
    /**
5199
     * Count messages per student inside forum tool.
5200
     *
5201
     * @param int|array  Student id
5202
     * @param string     Course code
5203
     * @param int        Session id if null(default) return count of messages including sessions, 0 = session is not
5204
     *                           filtered
5205
     *
5206
     * @return int Count of messages
5207
     */
5208
    public static function count_student_messages($student_id, $courseCode = null, $session_id = null)
5209
    {
5210
        if (empty($student_id)) {
5211
            return 0;
5212
        }
5213
5214
        // Table definition.
5215
        $tbl_forum_post = Database::get_course_table(TABLE_FORUM_POST);
5216
        $tbl_forum = Database::get_course_table(TABLE_FORUM);
5217
5218
        $conditions = [];
5219
        if (is_array($student_id)) {
5220
            $studentList = array_map('intval', $student_id);
5221
            $conditions[] = " post.poster_id IN ('".implode("','", $studentList)."') ";
5222
        } else {
5223
            $student_id = (int) $student_id;
5224
            $conditions[] = " post.poster_id = '$student_id' ";
5225
        }
5226
5227
        $conditionsToString = implode('AND ', $conditions);
5228
5229
        if (empty($courseCode)) {
5230
            $sql = "SELECT count(poster_id) as count
5231
                    FROM $tbl_forum_post post
5232
                    INNER JOIN $tbl_forum forum
5233
                    ON (forum.forum_id = post.forum_id AND forum.c_id = post.c_id)
5234
                    WHERE $conditionsToString";
5235
5236
            $rs = Database::query($sql);
5237
            $row = Database::fetch_array($rs, 'ASSOC');
5238
5239
            return $row['count'];
5240
        }
5241
5242
        require_once api_get_path(SYS_CODE_PATH).'forum/forumfunction.inc.php';
5243
5244
        $courseInfo = api_get_course_info($courseCode);
5245
5246
        $forums = [];
5247
        if (!empty($courseInfo)) {
5248
            $forums = get_forums('', $courseCode, true, $session_id);
5249
            $course_id = $courseInfo['real_id'];
5250
            $conditions[] = " post.c_id  = $course_id ";
5251
        }
5252
5253
        if (!empty($forums)) {
5254
            $idList = array_column($forums, 'forum_id');
5255
            $idListToString = implode("', '", $idList);
5256
            $conditions[] = " post.forum_id  IN ('$idListToString')";
5257
        }
5258
5259
        $conditionsToString = implode('AND ', $conditions);
5260
        $sql = "SELECT count(poster_id) as count
5261
                FROM $tbl_forum_post post
5262
                WHERE $conditionsToString";
5263
5264
        $rs = Database::query($sql);
5265
        $row = Database::fetch_array($rs, 'ASSOC');
5266
        $count = $row['count'];
5267
5268
        return $count;
5269
    }
5270
5271
    /**
5272
     * This function counts the number of post by course.
5273
     *
5274
     * @param string $course_code
5275
     * @param int    $session_id  (optional), if is null(default) it'll return results including sessions,
5276
     *                            0 = session is not filtered
5277
     * @param int    $groupId
5278
     *
5279
     * @return int The number of post by course
5280
     */
5281
    public static function count_number_of_posts_by_course($course_code, $session_id = null, $groupId = 0)
5282
    {
5283
        $courseInfo = api_get_course_info($course_code);
5284
        if (!empty($courseInfo)) {
5285
            $tbl_posts = Database::get_course_table(TABLE_FORUM_POST);
5286
            $tbl_forums = Database::get_course_table(TABLE_FORUM);
5287
5288
            $condition_session = '';
5289
            if (isset($session_id)) {
5290
                $session_id = (int) $session_id;
5291
                $condition_session = api_get_session_condition(
5292
                    $session_id,
5293
                    true,
5294
                    false,
5295
                    'f.session_id'
5296
                );
5297
            }
5298
5299
            $course_id = $courseInfo['real_id'];
5300
            $groupId = (int) $groupId;
5301
            if (!empty($groupId)) {
5302
                $groupCondition = " i.to_group_id = $groupId ";
5303
            } else {
5304
                $groupCondition = ' (i.to_group_id = 0 OR i.to_group_id IS NULL) ';
5305
            }
5306
5307
            $item = Database::get_course_table(TABLE_ITEM_PROPERTY);
5308
            $sql = "SELECT count(*) FROM $tbl_posts p
5309
                    INNER JOIN $tbl_forums f
5310
                    ON f.forum_id = p.forum_id AND p.c_id = f.c_id
5311
                    INNER JOIN $item i
5312
                    ON (tool = '".TOOL_FORUM."' AND f.c_id = i.c_id AND f.iid = i.ref)
5313
                    WHERE
5314
                        p.c_id = $course_id AND
5315
                        f.c_id = $course_id AND
5316
                        $groupCondition
5317
                        $condition_session
5318
                    ";
5319
            $result = Database::query($sql);
5320
            $row = Database::fetch_row($result);
5321
            $count = $row[0];
5322
5323
            return $count;
5324
        }
5325
5326
        return 0;
5327
    }
5328
5329
    /**
5330
     * This function counts the number of threads by course.
5331
     *
5332
     * @param      string     Course code
5333
     * @param    int        Session id (optional),
5334
     * if param $session_id is null(default) it'll return results including
5335
     * sessions, 0 = session is not filtered
5336
     * @param int $groupId
5337
     *
5338
     * @return int The number of threads by course
5339
     */
5340
    public static function count_number_of_threads_by_course(
5341
        $course_code,
5342
        $session_id = null,
5343
        $groupId = 0
5344
    ) {
5345
        $course_info = api_get_course_info($course_code);
5346
        if (empty($course_info)) {
5347
            return null;
5348
        }
5349
5350
        $course_id = $course_info['real_id'];
5351
        $tbl_threads = Database::get_course_table(TABLE_FORUM_THREAD);
5352
        $tbl_forums = Database::get_course_table(TABLE_FORUM);
5353
5354
        $condition_session = '';
5355
        if (isset($session_id)) {
5356
            $session_id = (int) $session_id;
5357
            $condition_session = ' AND f.session_id = '.$session_id;
5358
        }
5359
5360
        $groupId = (int) $groupId;
5361
5362
        if (!empty($groupId)) {
5363
            $groupCondition = " i.to_group_id = $groupId ";
5364
        } else {
5365
            $groupCondition = " (i.to_group_id = 0 OR i.to_group_id IS NULL) ";
5366
        }
5367
5368
        $item = Database::get_course_table(TABLE_ITEM_PROPERTY);
5369
        $sql = "SELECT count(*)
5370
                FROM $tbl_threads t
5371
                INNER JOIN $tbl_forums f
5372
                ON f.iid = t.forum_id AND f.c_id = t.c_id
5373
                INNER JOIN $item i
5374
                ON (
5375
                    tool = '".TOOL_FORUM_THREAD."' AND
5376
                    f.c_id = i.c_id AND
5377
                    t.iid = i.ref
5378
                )
5379
                WHERE
5380
                    t.c_id = $course_id AND
5381
                    f.c_id = $course_id AND
5382
                    $groupCondition
5383
                    $condition_session
5384
                ";
5385
5386
        $result = Database::query($sql);
5387
        if (Database::num_rows($result)) {
5388
            $row = Database::fetch_row($result);
5389
            $count = $row[0];
5390
5391
            return $count;
5392
        }
5393
5394
        return 0;
5395
    }
5396
5397
    /**
5398
     * This function counts the number of forums by course.
5399
     *
5400
     * @param      string     Course code
5401
     * @param    int        Session id (optional),
5402
     * if param $session_id is null(default) it'll return results
5403
     * including sessions, 0 = session is not filtered
5404
     * @param int $groupId
5405
     *
5406
     * @return int The number of forums by course
5407
     */
5408
    public static function count_number_of_forums_by_course(
5409
        $course_code,
5410
        $session_id = null,
5411
        $groupId = 0
5412
    ) {
5413
        $course_info = api_get_course_info($course_code);
5414
        if (empty($course_info)) {
5415
            return null;
5416
        }
5417
        $course_id = $course_info['real_id'];
5418
5419
        $condition_session = '';
5420
        if (isset($session_id)) {
5421
            $session_id = (int) $session_id;
5422
            $condition_session = ' AND f.session_id = '.$session_id;
5423
        }
5424
5425
        $groupId = (int) $groupId;
5426
        if (!empty($groupId)) {
5427
            $groupCondition = " i.to_group_id = $groupId ";
5428
        } else {
5429
            $groupCondition = " (i.to_group_id = 0 OR i.to_group_id IS NULL) ";
5430
        }
5431
5432
        $tbl_forums = Database::get_course_table(TABLE_FORUM);
5433
        $item = Database::get_course_table(TABLE_ITEM_PROPERTY);
5434
5435
        $sql = "SELECT count(*)
5436
                FROM $tbl_forums f
5437
                INNER JOIN $item i
5438
                ON f.c_id = i.c_id AND f.iid = i.ref AND tool = '".TOOL_FORUM."'
5439
                WHERE
5440
                    f.c_id = $course_id AND
5441
                    $groupCondition
5442
                    $condition_session
5443
                ";
5444
        $result = Database::query($sql);
5445
        if (Database::num_rows($result)) {
5446
            $row = Database::fetch_row($result);
5447
            $count = $row[0];
5448
5449
            return $count;
5450
        }
5451
5452
        return 0;
5453
    }
5454
5455
    /**
5456
     * This function counts the chat last connections by course in x days.
5457
     *
5458
     * @param      string     Course code
5459
     * @param      int     Last x days
5460
     * @param    int        Session id (optional)
5461
     *
5462
     * @return int Chat last connections by course in x days
5463
     */
5464
    public static function chat_connections_during_last_x_days_by_course(
5465
        $course_code,
5466
        $last_days,
5467
        $session_id = 0
5468
    ) {
5469
        $course_info = api_get_course_info($course_code);
5470
        if (empty($course_info)) {
5471
            return null;
5472
        }
5473
        $course_id = $course_info['real_id'];
5474
5475
        // Protect data
5476
        $last_days = (int) $last_days;
5477
        $session_id = (int) $session_id;
5478
5479
        $tbl_stats_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
5480
        $now = api_get_utc_datetime();
5481
5482
        $sql = "SELECT count(*) FROM $tbl_stats_access
5483
                WHERE
5484
                    DATE_SUB('$now',INTERVAL $last_days DAY) <= access_date AND
5485
                    c_id = '$course_id' AND
5486
                    access_tool='".TOOL_CHAT."' AND
5487
                    access_session_id = '$session_id' ";
5488
        $result = Database::query($sql);
5489
        if (Database::num_rows($result)) {
5490
            $row = Database::fetch_row($result);
5491
            $count = $row[0];
5492
5493
            return $count;
5494
        }
5495
5496
        return 0;
5497
    }
5498
5499
    /**
5500
     * This function gets the last student's connection in chat.
5501
     *
5502
     * @param      int     Student id
5503
     * @param      string     Course code
5504
     * @param    int        Session id (optional)
5505
     *
5506
     * @return string datetime formatted without day (e.g: February 23, 2010 10:20:50 )
5507
     */
5508
    public static function chat_last_connection(
5509
        $student_id,
5510
        $courseId,
5511
        $session_id = 0
5512
    ) {
5513
        $student_id = (int) $student_id;
5514
        $courseId = (int) $courseId;
5515
        $session_id = (int) $session_id;
5516
        $date_time = '';
5517
5518
        // table definition
5519
        $tbl_stats_access = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
5520
        $sql = "SELECT access_date
5521
                FROM $tbl_stats_access
5522
                WHERE
5523
                     access_tool='".TOOL_CHAT."' AND
5524
                     access_user_id='$student_id' AND
5525
                     c_id = $courseId AND
5526
                     access_session_id = '$session_id'
5527
                ORDER BY access_date DESC limit 1";
5528
        $rs = Database::query($sql);
5529
        if (Database::num_rows($rs) > 0) {
5530
            $row = Database::fetch_array($rs);
5531
            $date_time = api_convert_and_format_date(
5532
                $row['access_date'],
5533
                null,
5534
                date_default_timezone_get()
5535
            );
5536
        }
5537
5538
        return $date_time;
5539
    }
5540
5541
    /**
5542
     * Get count student's visited links.
5543
     *
5544
     * @param int $student_id Student id
5545
     * @param int $courseId
5546
     * @param int $session_id Session id (optional)
5547
     *
5548
     * @return int count of visited links
5549
     */
5550
    public static function count_student_visited_links($student_id, $courseId, $session_id = 0)
5551
    {
5552
        $student_id = (int) $student_id;
5553
        $courseId = (int) $courseId;
5554
        $session_id = (int) $session_id;
5555
5556
        // table definition
5557
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
5558
5559
        $sql = 'SELECT 1
5560
                FROM '.$table.'
5561
                WHERE
5562
                    links_user_id= '.$student_id.' AND
5563
                    c_id = "'.$courseId.'" AND
5564
                    links_session_id = '.$session_id.' ';
5565
5566
        $rs = Database::query($sql);
5567
5568
        return Database::num_rows($rs);
5569
    }
5570
5571
    /**
5572
     * Get count student downloaded documents.
5573
     *
5574
     * @param    int        Student id
5575
     * @param int $courseId
5576
     * @param    int        Session id (optional)
5577
     *
5578
     * @return int Count downloaded documents
5579
     */
5580
    public static function count_student_downloaded_documents($student_id, $courseId, $session_id = 0)
5581
    {
5582
        $student_id = (int) $student_id;
5583
        $courseId = (int) $courseId;
5584
        $session_id = (int) $session_id;
5585
5586
        // table definition
5587
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
5588
5589
        $sql = 'SELECT 1
5590
                FROM '.$table.'
5591
                WHERE down_user_id = '.$student_id.'
5592
                AND c_id  = "'.$courseId.'"
5593
                AND down_session_id = '.$session_id.' ';
5594
        $rs = Database::query($sql);
5595
5596
        return Database::num_rows($rs);
5597
    }
5598
5599
    /**
5600
     * Get course list inside a session from a student.
5601
     *
5602
     * @param int $user_id    Student id
5603
     * @param int $id_session Session id (optional)
5604
     *
5605
     * @return array Courses list
5606
     */
5607
    public static function get_course_list_in_session_from_student($user_id, $id_session = 0)
5608
    {
5609
        $user_id = intval($user_id);
5610
        $id_session = intval($id_session);
5611
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5612
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
5613
5614
        $sql = "SELECT c.code
5615
                FROM $tbl_session_course_user sc
5616
                INNER JOIN $courseTable c
5617
                WHERE
5618
                    user_id= $user_id  AND
5619
                    session_id = $id_session";
5620
        $result = Database::query($sql);
5621
        $courses = [];
5622
        while ($row = Database::fetch_array($result)) {
5623
            $courses[$row['code']] = $row['code'];
5624
        }
5625
5626
        return $courses;
5627
    }
5628
5629
    /**
5630
     * Get inactive students in course.
5631
     *
5632
     * @param int        $courseId
5633
     * @param string|int $since      Since login course date (optional, default = 'never')
5634
     * @param int        $session_id (optional)
5635
     *
5636
     * @return array Inactive users
5637
     */
5638
    public static function getInactiveStudentsInCourse(
5639
        $courseId,
5640
        $since = 'never',
5641
        $session_id = 0,
5642
        $userActive = null
5643
    ) {
5644
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
5645
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5646
        $table_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5647
        $tableCourse = Database::get_main_table(TABLE_MAIN_COURSE);
5648
        $now = api_get_utc_datetime();
5649
        $courseId = (int) $courseId;
5650
        $session_id = (int) $session_id;
5651
5652
        if (empty($courseId)) {
5653
            return false;
5654
        }
5655
5656
        if ($since === 'never') {
5657
            if (empty($session_id)) {
5658
                $sql = 'SELECT course_user.user_id
5659
                        FROM '.$table_course_rel_user.' course_user
5660
                        LEFT JOIN '.$tbl_track_login.' stats_login
5661
                        ON course_user.user_id = stats_login.user_id AND
5662
                        relation_type<>'.COURSE_RELATION_TYPE_RRHH.'
5663
                        INNER JOIN '.$tableCourse.' c
5664
                        ON (c.id = course_user.c_id)
5665
                        WHERE
5666
                            course_user.c_id = '.$courseId.' AND
5667
                            stats_login.login_course_date IS NULL
5668
                        GROUP BY course_user.user_id';
5669
            } else {
5670
                $sql = 'SELECT session_course_user.user_id
5671
                        FROM '.$tbl_session_course_user.' session_course_user
5672
                        LEFT JOIN '.$tbl_track_login.' stats_login
5673
                        ON session_course_user.user_id = stats_login.user_id
5674
                        INNER JOIN '.$tableCourse.' c
5675
                        ON (c.id = session_course_user.c_id)
5676
                        WHERE
5677
                            session_course_user.c_id = '.$courseId.' AND
5678
                            stats_login.login_course_date IS NULL
5679
                        GROUP BY session_course_user.user_id';
5680
            }
5681
        } else {
5682
            $since = (int) $since;
5683
            if (empty($session_id)) {
5684
                $inner = 'INNER JOIN '.$table_course_rel_user.' course_user
5685
                          ON course_user.user_id = stats_login.user_id AND course_user.c_id = c.id ';
5686
            } else {
5687
                $inner = 'INNER JOIN '.$tbl_session_course_user.' session_course_user
5688
                          ON
5689
                            c.id = session_course_user.c_id AND
5690
                            session_course_user.session_id = '.$session_id.' AND
5691
                            session_course_user.user_id = stats_login.user_id ';
5692
            }
5693
5694
            $sql = 'SELECT
5695
                    stats_login.user_id,
5696
                    MAX(login_course_date) max_date
5697
                FROM '.$tbl_track_login.' stats_login
5698
                INNER JOIN '.$tableCourse.' c
5699
                ON (c.id = stats_login.c_id)
5700
                '.$inner.'
5701
                WHERE c.id = '.$courseId.'
5702
                GROUP BY stats_login.user_id
5703
                HAVING DATE_SUB("'.$now.'", INTERVAL '.$since.' DAY) > max_date ';
5704
        }
5705
5706
        $rs = Database::query($sql);
5707
5708
        $allow = api_get_plugin_setting('pausetraining', 'tool_enable') === 'true';
5709
        $allowPauseFormation = api_get_plugin_setting('pausetraining', 'allow_users_to_edit_pause_formation') === 'true';
5710
5711
        $extraFieldValue = new ExtraFieldValue('user');
5712
        $users = [];
5713
        while ($user = Database::fetch_array($rs)) {
5714
            $userId = $user['user_id'];
5715
            if (isset($userActive)) {
5716
                $userActive = (int) $userActive;
5717
                $uInfo = api_get_user_info($userId);
5718
                if ((int) $uInfo['active'] !== $userActive) {
5719
                    continue;
5720
                }
5721
            }
5722
            if ($allow && $allowPauseFormation) {
5723
                $pause = $extraFieldValue->get_values_by_handler_and_field_variable($userId, 'pause_formation');
5724
                if (!empty($pause) && isset($pause['value']) && 1 == $pause['value']) {
5725
                    // Skip user because he paused his formation.
5726
                    continue;
5727
                }
5728
            }
5729
5730
            $users[] = $userId;
5731
        }
5732
5733
        return $users;
5734
    }
5735
5736
    /**
5737
     * get count clicks about tools most used by course.
5738
     *
5739
     * @param int $courseId
5740
     * @param    int        Session id (optional),
5741
     * if param $session_id is null(default) it'll return results
5742
     * including sessions, 0 = session is not filtered
5743
     *
5744
     * @return array tools data
5745
     */
5746
    public static function get_tools_most_used_by_course($courseId, $session_id = null)
5747
    {
5748
        $courseId = (int) $courseId;
5749
        $data = [];
5750
        $TABLETRACK_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
5751
        $condition_session = '';
5752
        if (isset($session_id)) {
5753
            $session_id = (int) $session_id;
5754
            $condition_session = ' AND access_session_id = '.$session_id;
5755
        }
5756
        $sql = "SELECT
5757
                    access_tool,
5758
                    COUNT(DISTINCT access_user_id),
5759
                    count(access_tool) as count_access_tool
5760
                FROM $TABLETRACK_ACCESS
5761
                WHERE
5762
                    access_tool IS NOT NULL AND
5763
                    access_tool != '' AND
5764
                    c_id = '$courseId'
5765
                    $condition_session
5766
                GROUP BY access_tool
5767
                ORDER BY count_access_tool DESC
5768
                LIMIT 0, 3";
5769
        $rs = Database::query($sql);
5770
        if (Database::num_rows($rs) > 0) {
5771
            while ($row = Database::fetch_array($rs)) {
5772
                $data[] = $row;
5773
            }
5774
        }
5775
5776
        return $data;
5777
    }
5778
5779
    /**
5780
     * get documents most downloaded by course.
5781
     *
5782
     * @param      string     Course code
5783
     * @param    int        Session id (optional),
5784
     * if param $session_id is null(default) it'll return results including
5785
     * sessions, 0 = session is not filtered
5786
     * @param    int        Limit (optional, default = 0, 0 = without limit)
5787
     *
5788
     * @return array documents downloaded
5789
     */
5790
    public static function get_documents_most_downloaded_by_course(
5791
        $course_code,
5792
        $session_id = 0,
5793
        $limit = 0
5794
    ) {
5795
        $courseId = api_get_course_int_id($course_code);
5796
        $data = [];
5797
5798
        $TABLETRACK_DOWNLOADS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
5799
        $condition_session = '';
5800
        $session_id = intval($session_id);
5801
        if (!empty($session_id)) {
5802
            $condition_session = ' AND down_session_id = '.$session_id;
5803
        }
5804
        $sql = "SELECT
5805
                    down_doc_path,
5806
                    COUNT(DISTINCT down_user_id),
5807
                    COUNT(down_doc_path) as count_down
5808
                FROM $TABLETRACK_DOWNLOADS
5809
                WHERE c_id = $courseId
5810
                    $condition_session
5811
                GROUP BY down_doc_path
5812
                ORDER BY count_down DESC
5813
                LIMIT 0,  $limit";
5814
        $rs = Database::query($sql);
5815
5816
        if (Database::num_rows($rs) > 0) {
5817
            while ($row = Database::fetch_array($rs)) {
5818
                $data[] = $row;
5819
            }
5820
        }
5821
5822
        return $data;
5823
    }
5824
5825
    /**
5826
     * get links most visited by course.
5827
     *
5828
     * @param      string     Course code
5829
     * @param    int        Session id (optional),
5830
     * if param $session_id is null(default) it'll
5831
     * return results including sessions, 0 = session is not filtered
5832
     *
5833
     * @return array links most visited
5834
     */
5835
    public static function get_links_most_visited_by_course($course_code, $session_id = null)
5836
    {
5837
        $course_code = Database::escape_string($course_code);
5838
        $course_info = api_get_course_info($course_code);
5839
        $course_id = $course_info['real_id'];
5840
        $data = [];
5841
5842
        $TABLETRACK_LINKS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
5843
        $TABLECOURSE_LINKS = Database::get_course_table(TABLE_LINK);
5844
5845
        $condition_session = '';
5846
        if (isset($session_id)) {
5847
            $session_id = intval($session_id);
5848
            $condition_session = ' AND cl.session_id = '.$session_id;
5849
        }
5850
5851
        $sql = "SELECT cl.title, cl.url,count(DISTINCT sl.links_user_id), count(cl.title) as count_visits
5852
                FROM $TABLETRACK_LINKS AS sl, $TABLECOURSE_LINKS AS cl
5853
                WHERE
5854
                    cl.c_id = $course_id AND
5855
                    sl.links_link_id = cl.id AND
5856
                    sl.c_id = $course_id
5857
                    $condition_session
5858
                GROUP BY cl.title, cl.url
5859
                ORDER BY count_visits DESC
5860
                LIMIT 0, 3";
5861
        $rs = Database::query($sql);
5862
        if (Database::num_rows($rs) > 0) {
5863
            while ($row = Database::fetch_array($rs)) {
5864
                $data[] = $row;
5865
            }
5866
        }
5867
5868
        return $data;
5869
    }
5870
5871
    /**
5872
     * Shows the user progress (when clicking in the Progress tab).
5873
     *
5874
     * @param int    $user_id
5875
     * @param int    $session_id
5876
     * @param string $extra_params
5877
     * @param bool   $show_courses
5878
     * @param bool   $showAllSessions
5879
     * @param bool   $returnArray
5880
     *
5881
     * @return string|array
5882
     */
5883
    public static function showUserProgress(
5884
        $user_id,
5885
        $session_id = 0,
5886
        $extra_params = '',
5887
        $show_courses = true,
5888
        $showAllSessions = true,
5889
        $returnArray = false,
5890
        $showGraph = true
5891
    ) {
5892
        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
5893
        $tbl_session = Database::get_main_table(TABLE_MAIN_SESSION);
5894
        $tbl_course_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
5895
        $tbl_access_rel_course = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_COURSE);
5896
        $tbl_session_course_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
5897
        $tbl_access_rel_session = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_SESSION);
5898
5899
        $trackingColumns = [
5900
            'course_session' => [
5901
                'course_title' => true,
5902
                'published_exercises' => true,
5903
                'new_exercises' => true,
5904
                'my_average' => true,
5905
                'average_exercise_result' => true,
5906
                'time_spent' => true,
5907
                'lp_progress' => true,
5908
                'score' => true,
5909
                'best_score' => true,
5910
                'last_connection' => true,
5911
                'details' => true,
5912
            ],
5913
        ];
5914
5915
        $trackingColumnsConfig = api_get_configuration_value('tracking_columns');
5916
        if (!empty($trackingColumnsConfig)) {
5917
            $trackingColumns = $trackingColumnsConfig;
5918
        }
5919
5920
        $user_id = (int) $user_id;
5921
        $session_id = (int) $session_id;
5922
        $urlId = api_get_current_access_url_id();
5923
5924
        if (api_is_multiple_url_enabled()) {
5925
            $sql = "SELECT c.id, c.code, title
5926
                    FROM $tbl_course_user cu
5927
                    INNER JOIN $tbl_course c
5928
                    ON (cu.c_id = c.id)
5929
                    INNER JOIN $tbl_access_rel_course a
5930
                    ON (a.c_id = c.id)
5931
                    WHERE
5932
                        cu.user_id = $user_id AND
5933
                        relation_type<> ".COURSE_RELATION_TYPE_RRHH." AND
5934
                        access_url_id = $urlId
5935
                    ORDER BY title";
5936
        } else {
5937
            $sql = "SELECT c.id, c.code, title
5938
                    FROM $tbl_course_user u
5939
                    INNER JOIN $tbl_course c ON (c_id = c.id)
5940
                    WHERE
5941
                        u.user_id= $user_id AND
5942
                        relation_type <> ".COURSE_RELATION_TYPE_RRHH."
5943
                    ORDER BY title";
5944
        }
5945
5946
        $rs = Database::query($sql);
5947
        $courses = $course_in_session = $temp_course_in_session = [];
5948
        $courseIdList = [];
5949
        while ($row = Database::fetch_array($rs, 'ASSOC')) {
5950
            $courses[$row['code']] = $row['title'];
5951
            $courseIdList[] = $row['id'];
5952
        }
5953
5954
        $orderBy = ' ORDER BY display_end_date DESC, name ';
5955
        $extraInnerJoin = null;
5956
5957
        if (SessionManager::orderCourseIsEnabled() && !empty($session_id)) {
5958
            $orderBy = ' ORDER BY s.id, src.position ';
5959
            $tableSessionRelCourse = Database::get_main_table(TABLE_MAIN_SESSION_COURSE);
5960
            $extraInnerJoin = " INNER JOIN $tableSessionRelCourse src
5961
                                ON (cu.c_id = src.c_id AND src.session_id = $session_id) ";
5962
        }
5963
5964
        $sessionCondition = '';
5965
        if (!empty($session_id)) {
5966
            $sessionCondition = " AND s.id = $session_id";
5967
        }
5968
5969
        // Get the list of sessions where the user is subscribed as student
5970
        if (api_is_multiple_url_enabled()) {
5971
            $sql = "SELECT DISTINCT c.code, s.id as session_id, name
5972
                    FROM $tbl_session_course_user cu
5973
                    INNER JOIN $tbl_access_rel_session a
5974
                    ON (a.session_id = cu.session_id)
5975
                    INNER JOIN $tbl_session s
5976
                    ON (s.id = a.session_id)
5977
                    INNER JOIN $tbl_course c
5978
                    ON (c.id = cu.c_id)
5979
                    $extraInnerJoin
5980
                    WHERE
5981
                        cu.user_id = $user_id AND
5982
                        access_url_id = ".$urlId."
5983
                        $sessionCondition
5984
                    $orderBy ";
5985
        } else {
5986
            $sql = "SELECT DISTINCT c.code, s.id as session_id, name
5987
                    FROM $tbl_session_course_user cu
5988
                    INNER JOIN $tbl_session s
5989
                    ON (s.id = cu.session_id)
5990
                    INNER JOIN $tbl_course c
5991
                    ON (c.id = cu.c_id)
5992
                    $extraInnerJoin
5993
                    WHERE
5994
                        cu.user_id = $user_id
5995
                        $sessionCondition
5996
                    $orderBy ";
5997
        }
5998
5999
        $rs = Database::query($sql);
6000
        $simple_session_array = [];
6001
        while ($row = Database::fetch_array($rs, 'ASSOC')) {
6002
            $course_info = api_get_course_info($row['code']);
6003
            $temp_course_in_session[$row['session_id']]['course_list'][$course_info['real_id']] = $course_info;
6004
            $temp_course_in_session[$row['session_id']]['name'] = $row['name'];
6005
            $simple_session_array[$row['session_id']] = $row['name'];
6006
        }
6007
6008
        foreach ($simple_session_array as $my_session_id => $session_name) {
6009
            $course_list = $temp_course_in_session[$my_session_id]['course_list'];
6010
            $my_course_data = [];
6011
            foreach ($course_list as $courseId => $course_data) {
6012
                $my_course_data[$courseId] = $course_data['title'];
6013
            }
6014
6015
            if (empty($session_id)) {
6016
                $my_course_data = utf8_sort($my_course_data);
6017
            }
6018
6019
            $final_course_data = [];
6020
            foreach ($my_course_data as $course_id => $value) {
6021
                if (isset($course_list[$course_id])) {
6022
                    $final_course_data[$course_id] = $course_list[$course_id];
6023
                }
6024
            }
6025
            $course_in_session[$my_session_id]['course_list'] = $final_course_data;
6026
            $course_in_session[$my_session_id]['name'] = $session_name;
6027
        }
6028
6029
        if ($returnArray) {
6030
            $course_in_session[0] = $courseIdList;
6031
6032
            return $course_in_session;
6033
        }
6034
6035
        $html = '';
6036
        // Course list.
6037
        if ($show_courses) {
6038
            if (!empty($courses)) {
6039
                $html .= Display::page_header(
6040
                    Display::return_icon('course.png', get_lang('MyCourses')).PHP_EOL.get_lang('MyCourses')
6041
                );
6042
6043
                $columns = [
6044
                    'course_title' => get_lang('Course'),
6045
                    'time_spent' => get_lang('TimeSpentInTheCourse'),
6046
                    'progress' => get_lang('Progress'),
6047
                    'best_score_in_lp' => get_lang('BestScoreInLearningPath'),
6048
                    'best_score_not_in_lp' => get_lang('BestScoreNotInLearningPath'),
6049
                    'latest_login' => get_lang('LastConnexion'),
6050
                    'details' => get_lang('Details'),
6051
                ];
6052
                $availableColumns = [];
6053
                if (isset($trackingColumns['my_progress_courses'])) {
6054
                    $availableColumns = $trackingColumns['my_progress_courses'];
6055
                }
6056
6057
                $columns = array_filter(
6058
                    $columns,
6059
                    function ($column, $columnKey) use ($availableColumns) {
6060
                        if (isset($availableColumns[$columnKey]) && $availableColumns[$columnKey] == false) {
6061
                            return false;
6062
                        }
6063
6064
                        return true;
6065
                    },
6066
                    ARRAY_FILTER_USE_BOTH
6067
                );
6068
6069
                $coursesTable = new SortableTableFromArray([], 0, 0, 'courses');
6070
                $coursesTable->setHeaders($columns);
6071
6072
                foreach ($courses as $course_code => $course_title) {
6073
                    $courseInfo = api_get_course_info($course_code);
6074
                    $courseId = $courseInfo['real_id'];
6075
                    $lpShowMaxProgress = api_get_configuration_value('lp_show_max_progress_instead_of_average');
6076
                    if (api_get_configuration_value('lp_show_max_progress_or_average_enable_course_level_redefinition')) {
6077
                        $lpShowProgressCourseSetting = api_get_course_setting('lp_show_max_or_average_progress', $courseInfo, true);
6078
                        if (in_array($lpShowProgressCourseSetting, ['max', 'average'])) {
6079
                            $lpShowMaxProgress = ('max' === $lpShowProgressCourseSetting);
6080
                        }
6081
                    }
6082
6083
                    $total_time_login = self::get_time_spent_on_the_course(
6084
                        $user_id,
6085
                        $courseId
6086
                    );
6087
                    $time = api_time_to_hms($total_time_login);
6088
                    $progress = self::get_avg_student_progress(
6089
                        $user_id,
6090
                        $course_code,
6091
                        [],
6092
                        null,
6093
                        false,
6094
                        false,
6095
                        $lpShowMaxProgress
6096
                    );
6097
                    $bestScore = self::get_avg_student_score(
6098
                        $user_id,
6099
                        $course_code,
6100
                        [],
6101
                        null,
6102
                        false,
6103
                        false,
6104
                        true
6105
                    );
6106
6107
                    $exerciseList = ExerciseLib::get_all_exercises(
6108
                        $courseInfo,
6109
                        0,
6110
                        false,
6111
                        null,
6112
                        false,
6113
                        1
6114
                    );
6115
6116
                    $bestScoreAverageNotInLP = 0;
6117
                    if (!empty($exerciseList)) {
6118
                        foreach ($exerciseList as $exerciseData) {
6119
                            $results = Event::get_best_exercise_results_by_user(
6120
                                $exerciseData['iid'],
6121
                                $courseInfo['real_id'],
6122
                                0,
6123
                                $user_id
6124
                            );
6125
                            $best = 0;
6126
                            if (!empty($results)) {
6127
                                foreach ($results as $result) {
6128
                                    if (!empty($result['exe_weighting'])) {
6129
                                        $score = $result['exe_result'] / $result['exe_weighting'];
6130
                                        if ($score > $best) {
6131
                                            $best = $score;
6132
                                        }
6133
                                    }
6134
                                }
6135
                            }
6136
                            $bestScoreAverageNotInLP += $best;
6137
                        }
6138
                        $bestScoreAverageNotInLP = round($bestScoreAverageNotInLP / count($exerciseList) * 100, 2);
6139
                    }
6140
6141
                    $last_connection = self::get_last_connection_date_on_the_course($user_id, $courseInfo);
6142
6143
                    if (is_null($progress) || empty($progress)) {
6144
                        $progress = '0%';
6145
                    } else {
6146
                        $progress = $progress.'%';
6147
                    }
6148
6149
                    $filterByCourse = isset($_GET['course']) && $course_code == $_GET['course']
6150
                        && empty($_GET['session_id']);
6151
6152
                    $url = api_get_course_url($course_code, $session_id);
6153
                    $course_url = Display::url($course_title, $url, ['target' => SESSION_LINK_TARGET]);
6154
                    $bestScoreResult = empty($bestScore) ? '-' : sprintf(get_lang('XPercent'), $bestScore);
6155
                    $bestScoreNotInLP = empty($bestScoreAverageNotInLP)
6156
                        ? '-'
6157
                        : sprintf(get_lang('XPercent'), $bestScoreAverageNotInLP);
6158
6159
                    $detailsLink = '';
6160
                    if ($filterByCourse) {
6161
                        $detailsLink .= '<a href="#course_session_data">';
6162
                        $detailsLink .= Display::return_icon('2rightarrow_na.png', get_lang('Details'));
6163
                        $detailsLink .= '</a>';
6164
                    } else {
6165
                        $detailsLink .= '<a href="'.api_get_self().'?course='.$course_code.$extra_params.'#course_session_data">';
6166
                        $detailsLink .= Display::return_icon('2rightarrow.png', get_lang('Details'));
6167
                        $detailsLink .= '</a>';
6168
                    }
6169
6170
                    $result = array_filter(
6171
                        [
6172
                            'course_title' => $course_url,
6173
                            'time_spent' => $time,
6174
                            'progress' => $progress,
6175
                            'best_score_in_lp' => $bestScoreResult,
6176
                            'best_score_not_in_lp' => $bestScoreNotInLP,
6177
                            'latest_login' => $last_connection,
6178
                            'details' => $detailsLink,
6179
                        ],
6180
                        function ($data, $columnKey) {
6181
                            if (isset($availableColumns[$columnKey]) && $availableColumns[$columnKey] == false) {
6182
                                return false;
6183
                            }
6184
6185
                            return true;
6186
                        },
6187
                        ARRAY_FILTER_USE_BOTH
6188
                    );
6189
6190
                    $coursesTable->addRow(
6191
                        array_values($result),
6192
                        ['style' => $filterByCourse ? 'background-color: #FBF09D;' : '']
6193
                    );
6194
                }
6195
6196
                $html .= Display::div($coursesTable->toHtml(), ['class' => 'table-responsive']);
6197
            }
6198
        }
6199
6200
        $allowCareerUser = api_get_configuration_value('allow_career_users');
6201
6202
        // Session list.
6203
        $visibleSessions = [];
6204
        if (!empty($course_in_session)) {
6205
            $main_session_graph = '';
6206
            // Load graphics only when calling to an specific session
6207
            $all_exercise_graph_name_list = [];
6208
            $my_results = [];
6209
            $all_exercise_graph_list = [];
6210
            $all_exercise_start_time = [];
6211
            foreach ($course_in_session as $my_session_id => $session_data) {
6212
                $course_list = $session_data['course_list'];
6213
                $user_count = count(SessionManager::get_users_by_session($my_session_id));
6214
                $exercise_graph_name_list = [];
6215
                $exercise_graph_list = [];
6216
6217
                $visibility = api_get_session_visibility($my_session_id, null, false, $user_id);
6218
6219
                if (SESSION_AVAILABLE === $visibility) {
6220
                    $visibleSessions[] = $my_session_id;
6221
                }
6222
6223
                foreach ($course_list as $course_data) {
6224
                    $exercise_list = ExerciseLib::get_all_exercises(
6225
                        $course_data,
6226
                        $my_session_id,
6227
                        false,
6228
                        null,
6229
                        false,
6230
                        1
6231
                    );
6232
6233
                    foreach ($exercise_list as $exercise_data) {
6234
                        $exercise_obj = new Exercise($course_data['real_id']);
6235
                        $exercise_obj->read($exercise_data['iid']);
6236
                        // Exercise is not necessary to be visible to show results check the result_disable configuration instead
6237
                        // $visible_return = $exercise_obj->is_visible();
6238
                        if ($exercise_data['results_disabled'] == 0 || $exercise_data['results_disabled'] == 2) {
6239
                            $best_average = (int)
6240
                                ExerciseLib::get_best_average_score_by_exercise(
6241
                                    $exercise_data['iid'],
6242
                                    $course_data['real_id'],
6243
                                    $my_session_id,
6244
                                    $user_count
6245
                                )
6246
                            ;
6247
6248
                            $exercise_graph_list[] = $best_average;
6249
                            $all_exercise_graph_list[] = $best_average;
6250
6251
                            $user_result_data = ExerciseLib::get_best_attempt_by_user(
6252
                                api_get_user_id(),
6253
                                $exercise_data['iid'],
6254
                                $course_data['real_id'],
6255
                                $my_session_id
6256
                            );
6257
6258
                            $score = 0;
6259
                            if (!empty($user_result_data['exe_weighting']) && intval($user_result_data['exe_weighting']) != 0) {
6260
                                $score = intval($user_result_data['exe_result'] / $user_result_data['exe_weighting'] * 100);
6261
                            }
6262
                            $time = api_strtotime($exercise_data['start_time']) ? api_strtotime($exercise_data['start_time'], 'UTC') : 0;
6263
                            $all_exercise_start_time[] = $time;
6264
                            $my_results[] = $score;
6265
                            if (count($exercise_list) <= 10) {
6266
                                $title = cut($course_data['title'], 30)." \n ".cut($exercise_data['title'], 30);
6267
                                $exercise_graph_name_list[] = $title;
6268
                                $all_exercise_graph_name_list[] = $title;
6269
                            } else {
6270
                                // if there are more than 10 results, space becomes difficult to find,
6271
                                // so only show the title of the exercise, not the tool
6272
                                $title = cut($exercise_data['title'], 30);
6273
                                $exercise_graph_name_list[] = $title;
6274
                                $all_exercise_graph_name_list[] = $title;
6275
                            }
6276
                        }
6277
                    }
6278
                }
6279
            }
6280
6281
            // Complete graph
6282
            if (!empty($my_results) && !empty($all_exercise_graph_list)) {
6283
                asort($all_exercise_start_time);
6284
6285
                // Fix exams order
6286
                $final_all_exercise_graph_name_list = [];
6287
                $my_results_final = [];
6288
                $final_all_exercise_graph_list = [];
6289
6290
                foreach ($all_exercise_start_time as $key => $time) {
6291
                    $label_time = '';
6292
                    if (!empty($time)) {
6293
                        $label_time = date('d-m-y', $time);
6294
                    }
6295
                    $final_all_exercise_graph_name_list[] = $all_exercise_graph_name_list[$key].' '.$label_time;
6296
                    $my_results_final[] = $my_results[$key];
6297
                    $final_all_exercise_graph_list[] = $all_exercise_graph_list[$key];
6298
                }
6299
                if ($showGraph) {
6300
                    $main_session_graph = '<div class="row"><div class="col-md-10 col-md-offset-1">'
6301
                        .self::generate_session_exercise_graph(
6302
                            $final_all_exercise_graph_name_list,
6303
                            $my_results_final,
6304
                            $final_all_exercise_graph_list
6305
                        )
6306
                        .'</div></div>';
6307
                }
6308
            }
6309
6310
            $sessionIcon = Display::return_icon('session.png', get_lang('Sessions'));
6311
6312
            $anchor = Display::url('', '', ['name' => 'course_session_header']);
6313
            $html .= $anchor.Display::page_header(
6314
                $sessionIcon.PHP_EOL.get_lang('Sessions')
6315
            );
6316
6317
            $sessionsTable = new SortableTableFromArray([], 0, 0, 'sessions');
6318
            $sessionsTable->setHeaders(
6319
                [
6320
                    get_lang('Session'),
6321
                    get_lang('PublishedExercises'),
6322
                    get_lang('NewExercises'),
6323
                    get_lang('AverageExerciseResult'),
6324
                    get_lang('Details'),
6325
                ]
6326
            );
6327
6328
            foreach ($course_in_session as $my_session_id => $session_data) {
6329
                $course_list = $session_data['course_list'];
6330
                $session_name = $session_data['name'];
6331
                if ($showAllSessions == false) {
6332
                    if (isset($session_id) && !empty($session_id)) {
6333
                        if ($session_id != $my_session_id) {
6334
                            continue;
6335
                        }
6336
                    }
6337
                }
6338
6339
                $all_exercises = 0;
6340
                $all_unanswered_exercises_by_user = 0;
6341
                $all_average = 0;
6342
                $stats_array = [];
6343
6344
                foreach ($course_list as $course_data) {
6345
                    // All exercises in the course @todo change for a real count
6346
                    $exercises = ExerciseLib::get_all_exercises($course_data, $my_session_id);
6347
                    $count_exercises = 0;
6348
                    if (is_array($exercises) && !empty($exercises)) {
6349
                        $count_exercises = count($exercises);
6350
                    }
6351
6352
                    // Count of user results
6353
                    $done_exercises = null;
6354
                    $courseInfo = api_get_course_info($course_data['code']);
6355
6356
                    $answered_exercises = 0;
6357
                    if (!empty($exercises)) {
6358
                        foreach ($exercises as $exercise_item) {
6359
                            $attempts = Event::count_exercise_attempts_by_user(
6360
                                api_get_user_id(),
6361
                                $exercise_item['iid'],
6362
                                $courseInfo['real_id'],
6363
                                $my_session_id
6364
                            );
6365
                            if ($attempts > 1) {
6366
                                $answered_exercises++;
6367
                            }
6368
                        }
6369
                    }
6370
6371
                    // Average
6372
                    $average = ExerciseLib::get_average_score_by_course(
6373
                        $courseInfo['real_id'],
6374
                        $my_session_id
6375
                    );
6376
                    $all_exercises += $count_exercises;
6377
                    $all_unanswered_exercises_by_user += $count_exercises - $answered_exercises;
6378
                    $all_average += $average;
6379
                }
6380
6381
                if (!empty($course_list)) {
6382
                    $all_average = $all_average / count($course_list);
6383
                }
6384
6385
                $filterBySession = isset($_GET['session_id']) && $my_session_id == $_GET['session_id'];
6386
6387
                $url = api_get_path(WEB_CODE_PATH)."session/index.php?session_id={$my_session_id}";
6388
6389
                $sessionsTable->addRow(
6390
                    [
6391
                        Display::url($session_name, $url, ['target' => SESSION_LINK_TARGET]),
6392
                        $all_exercises,
6393
                        $all_unanswered_exercises_by_user,
6394
                        ExerciseLib::convert_to_percentage($all_average),
6395
                        Display::url(
6396
                            Display::return_icon(
6397
                                $filterBySession ? '2rightarrow_na.png' : '2rightarrow.png',
6398
                                get_lang('Details')
6399
                            ),
6400
                            api_get_self().'?session_id='.$my_session_id.'#course_session_list'
6401
                        ),
6402
                    ],
6403
                    ['style' => $filterBySession ? 'background-color: #FBF09D;' : '']
6404
                );
6405
            }
6406
6407
            if ($allowCareerUser) {
6408
                $diagrams = '';
6409
                if (!empty($visibleSessions)) {
6410
                    $diagrams .= SessionManager::getCareerDiagramPerSessionList($visibleSessions, $user_id);
6411
                }
6412
                $html .= $diagrams.MyStudents::userCareersTable($user_id);
6413
            }
6414
6415
            $html .= Display::div($sessionsTable->toHtml(), ['class' => 'table-responsive']);
6416
6417
            if ($showGraph) {
6418
                $html .= Display::div(
6419
                    $main_session_graph,
6420
                    [
6421
                        'id' => 'session_graph',
6422
                        'class' => 'chart-session',
6423
                    ]
6424
                );
6425
            }
6426
6427
            // Checking selected session.
6428
            if (isset($_GET['session_id'])) {
6429
                $session_id_from_get = (int) $_GET['session_id'];
6430
                $session_data = $course_in_session[$session_id_from_get];
6431
                $course_list = $session_data['course_list'];
6432
6433
                $html .= '<a name="course_session_list"></a>';
6434
                $html .= Display::page_subheader($session_data['name'], get_lang('CourseList'));
6435
6436
                $columnHeaders = array_filter(
6437
                    [
6438
                        'course_title' => get_lang('Course'),
6439
                        'published_exercises' => get_lang('PublishedExercises'),
6440
                        'new_exercises' => get_lang('NewExercises'),
6441
                        'my_average' => get_lang('MyAverage'),
6442
                        'average_exercise_result' => get_lang('AverageExerciseResult'),
6443
                        'time_spent' => get_lang('TimeSpentInTheCourse'),
6444
                        'lp_progress' => get_lang('LPProgress'),
6445
                        'score' => get_lang('Score')
6446
                            .Display::return_icon('info3.gif', get_lang('ScormAndLPTestTotalAverage')),
6447
                        'best_score' => get_lang('BestScore'),
6448
                        'last_connection' => get_lang('LastConnexion'),
6449
                        'details' => get_lang('Details'),
6450
                    ],
6451
                    function ($column, $key) use ($trackingColumns) {
6452
                        if (isset($trackingColumns['course_session'])
6453
                            && in_array($key, $trackingColumns['course_session'])
6454
                            && $trackingColumns['course_session'][$key]
6455
                        ) {
6456
                            return true;
6457
                        }
6458
6459
                        return false;
6460
                    },
6461
                    ARRAY_FILTER_USE_BOTH
6462
                );
6463
6464
                $sessionCoursesTable = new SortableTableFromArray([], 0, 0, 'session_courses');
6465
                $sessionCoursesTable->setHeaders($columnHeaders);
6466
6467
                foreach ($course_list as $course_data) {
6468
                    $course_code = $course_data['code'];
6469
                    $course_title = $course_data['title'];
6470
                    $courseId = $course_data['real_id'];
6471
6472
                    // All exercises in the course @todo change for a real count
6473
                    $exercises = ExerciseLib::get_all_exercises(
6474
                        $course_data,
6475
                        $session_id_from_get
6476
                    );
6477
                    $count_exercises = 0;
6478
                    if (!empty($exercises)) {
6479
                        $count_exercises = count($exercises);
6480
                    }
6481
                    $answered_exercises = 0;
6482
                    foreach ($exercises as $exercise_item) {
6483
                        $attempts = Event::count_exercise_attempts_by_user(
6484
                            api_get_user_id(),
6485
                            $exercise_item['iid'],
6486
                            $courseId,
6487
                            $session_id_from_get
6488
                        );
6489
                        if ($attempts > 1) {
6490
                            $answered_exercises++;
6491
                        }
6492
                    }
6493
6494
                    $unanswered_exercises = $count_exercises - $answered_exercises;
6495
6496
                    // Average
6497
                    $average = ExerciseLib::get_average_score_by_course(
6498
                        $courseId,
6499
                        $session_id_from_get
6500
                    );
6501
                    $my_average = ExerciseLib::get_average_score_by_course_by_user(
6502
                        api_get_user_id(),
6503
                        $courseId,
6504
                        $session_id_from_get
6505
                    );
6506
6507
                    $bestScore = self::get_avg_student_score(
6508
                        $user_id,
6509
                        $course_code,
6510
                        [],
6511
                        $session_id_from_get,
6512
                        false,
6513
                        false,
6514
                        true
6515
                    );
6516
6517
                    $stats_array[$course_code] = [
6518
                        'exercises' => $count_exercises,
6519
                        'unanswered_exercises_by_user' => $unanswered_exercises,
6520
                        'done_exercises' => $done_exercises,
6521
                        'average' => $average,
6522
                        'my_average' => $my_average,
6523
                        'best_score' => $bestScore,
6524
                    ];
6525
6526
                    $last_connection = self::get_last_connection_date_on_the_course(
6527
                        $user_id,
6528
                        $course_data,
6529
                        $session_id_from_get
6530
                    );
6531
6532
                    $progress = self::get_avg_student_progress(
6533
                        $user_id,
6534
                        $course_code,
6535
                        [],
6536
                        $session_id_from_get,
6537
                        false,
6538
                        false,
6539
                        $lpShowMaxProgress
6540
                    );
6541
6542
                    $total_time_login = self::get_time_spent_on_the_course(
6543
                        $user_id,
6544
                        $courseId,
6545
                        $session_id_from_get
6546
                    );
6547
                    $time = api_time_to_hms($total_time_login);
6548
6549
                    $percentage_score = self::get_avg_student_score(
6550
                        $user_id,
6551
                        $course_code,
6552
                        [],
6553
                        $session_id_from_get
6554
                    );
6555
                    $courseCodeFromGet = $_GET['course'] ?? null;
6556
6557
                    $filterByCourse = $course_code == $courseCodeFromGet && $_GET['session_id'] == $session_id_from_get;
6558
6559
                    $url = api_get_course_url($course_code, $session_id_from_get);
6560
                    $course_url = Display::url(
6561
                        $course_title,
6562
                        $url,
6563
                        ['target' => SESSION_LINK_TARGET]
6564
                    );
6565
6566
                    if (is_numeric($progress)) {
6567
                        $progress = $progress.'%';
6568
                    } else {
6569
                        $progress = '0%';
6570
                    }
6571
                    if (is_numeric($percentage_score)) {
6572
                        $percentage_score = $percentage_score.'%';
6573
                    } else {
6574
                        $percentage_score = '0%';
6575
                    }
6576
6577
                    if (is_numeric($stats_array[$course_code]['best_score'])) {
6578
                        $bestScore = $stats_array[$course_code]['best_score'].'%';
6579
                    } else {
6580
                        $bestScore = '-';
6581
                    }
6582
6583
                    if (empty($last_connection) || is_bool($last_connection)) {
6584
                        $last_connection = '';
6585
                    }
6586
6587
                    if ($course_code == $courseCodeFromGet
6588
                        && $_GET['session_id'] == $session_id_from_get
6589
                    ) {
6590
                        $details = Display::url(
6591
                            Display::return_icon('2rightarrow_na.png', get_lang('Details')),
6592
                            '#course_session_data'
6593
                        );
6594
                    } else {
6595
                        $url = api_get_self().'?course='.$course_code.'&session_id='.$session_id_from_get.$extra_params.'#course_session_data';
6596
                        $details = Display::url(
6597
                            Display::return_icon(
6598
                                '2rightarrow.png',
6599
                                get_lang('Details')
6600
                            ),
6601
                            $url
6602
                        );
6603
                    }
6604
6605
                    $data = array_filter(
6606
                        [
6607
                            'course_title' => $course_url,
6608
                            'published_exercises' => $stats_array[$course_code]['exercises'], // exercise available
6609
                            'new_exercises' => $stats_array[$course_code]['unanswered_exercises_by_user'],
6610
                            'my_average' => ExerciseLib::convert_to_percentage($stats_array[$course_code]['my_average']),
6611
                            'average_exercise_result' => $stats_array[$course_code]['average'] == 0 ? '-' : '('.ExerciseLib::convert_to_percentage($stats_array[$course_code]['average']).')',
6612
                            'time_spent' => $time,
6613
                            'lp_progress' => $progress,
6614
                            'score' => $percentage_score,
6615
                            'best_score' => $bestScore,
6616
                            'last_connection' => $last_connection,
6617
                            'details' => $details,
6618
                        ],
6619
                        function ($value, $key) use ($trackingColumns) {
6620
                            if (in_array($key, $trackingColumns['course_session']) && $trackingColumns['course_session'][$key]) {
6621
                                return true;
6622
                            }
6623
6624
                            return false;
6625
                        },
6626
                        ARRAY_FILTER_USE_BOTH
6627
                    );
6628
6629
                    $sessionCoursesTable->addRow(
6630
                        array_values($data),
6631
                        ['style' => $filterByCourse ? 'background-color: #FBF09D;' : '']
6632
                    );
6633
                }
6634
6635
                $html .= Display::div($sessionCoursesTable->toHtml(), ['class' => 'table-responsive']);
6636
            }
6637
        }
6638
6639
        $pluginCalendar = api_get_plugin_setting('learning_calendar', 'enabled') === 'true';
6640
        if ($pluginCalendar) {
6641
            $course_in_session[0] = $courseIdList;
6642
            $plugin = LearningCalendarPlugin::create();
6643
            $html .= $plugin->getUserStatsPanel($user_id, $course_in_session);
6644
        }
6645
6646
        return $html;
6647
    }
6648
6649
    /**
6650
     * Shows the user detail progress (when clicking in the details link).
6651
     *
6652
     * @param int    $user_id
6653
     * @param string $course_code
6654
     * @param int    $session_id
6655
     */
6656
    public static function show_course_detail($user_id, $course_code, $session_id, $isAllowedToEdit = true): string
6657
    {
6658
        if (empty($user_id) || empty($course_code)) {
6659
            return '';
6660
        }
6661
6662
        $course_info = api_get_course_info($course_code);
6663
        if (empty($course_info)) {
6664
            return '';
6665
        }
6666
6667
        $user_id = (int) $user_id;
6668
        $user = api_get_user_entity($user_id);
6669
        $session_id = (int) $session_id;
6670
6671
        $html = '<a name="course_session_data"></a>';
6672
        $html .= Display::page_subheader2($course_info['title']);
6673
        // Show exercise results of invisible exercises? see BT#4091
6674
        $quizzesHtml = self::generateQuizzesTable($course_info, $session_id);
6675
        // LP table results
6676
        $learningPathsHtml = self::generateLearningPathsTable($user, $course_info, $session_id, $isAllowedToEdit);
6677
        $skillsHtml = self::displayUserSkills($user_id, $course_info['id'], $session_id);
6678
6679
        $toolsHtml = [
6680
            'quizzes' => $quizzesHtml,
6681
            'learning_paths' => $learningPathsHtml,
6682
            'skills' => $skillsHtml,
6683
        ];
6684
6685
        $toolsOrder = api_get_configuration_value('my_progress_course_tools_order');
6686
6687
        if (empty($toolsOrder)) {
6688
            $html .= implode(PHP_EOL, $toolsHtml);
6689
        } else {
6690
            foreach ($toolsOrder['order'] as $tool) {
6691
                $html .= $toolsHtml[$tool].PHP_EOL;
6692
            }
6693
        }
6694
6695
        return $html;
6696
    }
6697
6698
    /**
6699
     * Generates an histogram.
6700
     *
6701
     * @param array $names      list of exercise names
6702
     * @param array $my_results my results 0 to 100
6703
     * @param array $average    average scores 0-100
6704
     *
6705
     * @return string
6706
     */
6707
    public static function generate_session_exercise_graph($names, $my_results, $average)
6708
    {
6709
        $html = api_get_js('chartjs/Chart.js');
6710
        $canvas = Display::tag('canvas', '', ['id' => 'session_graph_chart']);
6711
        $html .= Display::tag('div', $canvas, ['style' => 'width:100%']);
6712
        $jsStr = " var data = {
6713
                       labels:".json_encode($names).",
6714
                       datasets: [
6715
                       {
6716
                         label: '".get_lang('MyResults')."',
6717
                         backgroundColor: 'rgb(255, 99, 132)',
6718
                         stack: 'Stack1',
6719
                         data: ".json_encode($my_results).",
6720
                        },
6721
                        {
6722
                         label: '".get_lang('AverageScore')."',
6723
                         backgroundColor: 'rgb(75, 192, 192)',
6724
                         stack: 'Stack2',
6725
                         data: ".json_encode($average).",
6726
                        },
6727
                        ],
6728
                    };
6729
                    var ctx = document.getElementById('session_graph_chart').getContext('2d');
6730
                    var myBarChart = new Chart(ctx, {
6731
                    type: 'bar',
6732
                    data: data,
6733
                    options: {
6734
                            title: {
6735
                                    display: true,
6736
                                    text: '".get_lang('ExercisesInTimeProgressChart')."'
6737
                            },
6738
                            tooltips: {
6739
                                    mode: 'index',
6740
                                    intersect: false
6741
                            },
6742
                            responsive: true,
6743
                            scales: {
6744
                                yAxes: [{
6745
                                    ticks: {
6746
                                        // Include a dollar sign in the ticks
6747
                                        callback: function(value, index, values) {
6748
                                            return value + '%';
6749
                                        }
6750
                                    }
6751
                                }]
6752
                            }
6753
                    }
6754
                });";
6755
        $html .= Display::tag('script', $jsStr);
6756
6757
        return $html;
6758
    }
6759
6760
    /**
6761
     * Returns a thumbnail of the function generate_exercise_result_graph.
6762
     *
6763
     * @param array $attempts
6764
     */
6765
    public static function generate_exercise_result_thumbnail_graph($attempts)
6766
    {
6767
        // $exercise_title = $attempts['title'];
6768
        $attempts = $attempts['data'];
6769
        $my_exercise_result_array = $exercise_result = [];
6770
        if (empty($attempts)) {
6771
            return null;
6772
        }
6773
6774
        foreach ($attempts as $attempt) {
6775
            if (api_get_user_id() == $attempt['exe_user_id']) {
6776
                if ($attempt['exe_weighting'] != 0) {
6777
                    $my_exercise_result_array[] = $attempt['exe_result'] / $attempt['exe_weighting'];
6778
                }
6779
            } else {
6780
                if ($attempt['exe_weighting'] != 0) {
6781
                    $exercise_result[] = $attempt['exe_result'] / $attempt['exe_weighting'];
6782
                }
6783
            }
6784
        }
6785
6786
        // Getting best result
6787
        rsort($my_exercise_result_array);
6788
        $my_exercise_result = 0;
6789
        if (isset($my_exercise_result_array[0])) {
6790
            $my_exercise_result = $my_exercise_result_array[0] * 100;
6791
        }
6792
6793
        $max = 100;
6794
        $pieces = 5;
6795
        $part = round($max / $pieces);
6796
        $x_axis = [];
6797
        $final_array = [];
6798
        $my_final_array = [];
6799
6800
        for ($i = 1; $i <= $pieces; $i++) {
6801
            $sum = 1;
6802
            if ($i == 1) {
6803
                $sum = 0;
6804
            }
6805
            $min = ($i - 1) * $part + $sum;
6806
            $max = $i * $part;
6807
            $x_axis[] = $min." - ".$max;
6808
            $count = 0;
6809
            foreach ($exercise_result as $result) {
6810
                $percentage = $result * 100;
6811
                if ($percentage >= $min && $percentage <= $max) {
6812
                    // echo ' is > ';
6813
                    $count++;
6814
                }
6815
            }
6816
            // echo '<br />';
6817
            $final_array[] = $count;
6818
6819
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
6820
                $my_final_array[] = 1;
6821
            } else {
6822
                $my_final_array[] = 0;
6823
            }
6824
        }
6825
6826
        // Fix to remove the data of the user with my data
6827
        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...
6828
            if (!empty($my_final_array[$i])) {
6829
                $my_final_array[$i] = $final_array[$i] + 1; // Add my result
6830
                $final_array[$i] = 0;
6831
            }
6832
        }
6833
6834
        // Dataset definition
6835
        $dataSet = new pData();
6836
        $dataSet->addPoints($final_array, 'Serie1');
6837
        $dataSet->addPoints($my_final_array, 'Serie2');
6838
        $dataSet->normalize(100, "%");
6839
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true);
6840
6841
        // Cache definition
6842
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
6843
        $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
6844
        $chartHash = $myCache->getHash($dataSet);
6845
        if ($myCache->isInCache($chartHash)) {
6846
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6847
            $myCache->saveFromCache($chartHash, $imgPath);
6848
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6849
        } else {
6850
            /* Create the pChart object */
6851
            $widthSize = 80;
6852
            $heightSize = 35;
6853
            $fontSize = 2;
6854
6855
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
6856
6857
            /* Turn of Antialiasing */
6858
            $myPicture->Antialias = false;
6859
6860
            /* Add a border to the picture */
6861
            $myPicture->drawRectangle(
6862
                0,
6863
                0,
6864
                $widthSize - 1,
6865
                $heightSize - 1,
6866
                ['R' => 0, 'G' => 0, 'B' => 0]
6867
            );
6868
6869
            /* Set the default font */
6870
            $myPicture->setFontProperties(
6871
                [
6872
                    'FontName' => api_get_path(
6873
                        SYS_FONTS_PATH
6874
                    ).'opensans/OpenSans-Regular.ttf',
6875
                    'FontSize' => $fontSize,
6876
                ]
6877
            );
6878
6879
            /* Do not write the chart title */
6880
            /* Define the chart area */
6881
            $myPicture->setGraphArea(5, 5, $widthSize - 5, $heightSize - 5);
6882
6883
            /* Draw the scale */
6884
            $scaleSettings = [
6885
                'GridR' => 200,
6886
                'GridG' => 200,
6887
                'GridB' => 200,
6888
                'DrawSubTicks' => true,
6889
                'CycleBackground' => true,
6890
                'Mode' => SCALE_MODE_MANUAL,
6891
                'ManualScale' => [
6892
                    '0' => [
6893
                        'Min' => 0,
6894
                        'Max' => 100,
6895
                    ],
6896
                ],
6897
            ];
6898
            $myPicture->drawScale($scaleSettings);
6899
6900
            /* Turn on shadow computing */
6901
            $myPicture->setShadow(
6902
                true,
6903
                [
6904
                    'X' => 1,
6905
                    'Y' => 1,
6906
                    'R' => 0,
6907
                    'G' => 0,
6908
                    'B' => 0,
6909
                    'Alpha' => 10,
6910
                ]
6911
            );
6912
6913
            /* Draw the chart */
6914
            $myPicture->setShadow(
6915
                true,
6916
                [
6917
                    'X' => 1,
6918
                    'Y' => 1,
6919
                    'R' => 0,
6920
                    'G' => 0,
6921
                    'B' => 0,
6922
                    'Alpha' => 10,
6923
                ]
6924
            );
6925
            $settings = [
6926
                'DisplayValues' => true,
6927
                'DisplaySize' => $fontSize,
6928
                'DisplayR' => 0,
6929
                'DisplayG' => 0,
6930
                'DisplayB' => 0,
6931
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
6932
                'Gradient' => false,
6933
                'Surrounding' => 5,
6934
                'InnerSurrounding' => 5,
6935
            ];
6936
            $myPicture->drawStackedBarChart($settings);
6937
6938
            /* Save and write in cache */
6939
            $myCache->writeToCache($chartHash, $myPicture);
6940
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
6941
            $myCache->saveFromCache($chartHash, $imgPath);
6942
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
6943
        }
6944
6945
        return $imgPath;
6946
    }
6947
6948
    /**
6949
     * Generates a big graph with the number of best results.
6950
     *
6951
     * @param	array
6952
     */
6953
    public static function generate_exercise_result_graph($attempts)
6954
    {
6955
        $exercise_title = strip_tags($attempts['title']);
6956
        $attempts = $attempts['data'];
6957
        $my_exercise_result_array = $exercise_result = [];
6958
        if (empty($attempts)) {
6959
            return null;
6960
        }
6961
        foreach ($attempts as $attempt) {
6962
            if (api_get_user_id() == $attempt['exe_user_id']) {
6963
                if ($attempt['exe_weighting'] != 0) {
6964
                    $my_exercise_result_array[] = $attempt['exe_result'] / $attempt['exe_weighting'];
6965
                }
6966
            } else {
6967
                if ($attempt['exe_weighting'] != 0) {
6968
                    $exercise_result[] = $attempt['exe_result'] / $attempt['exe_weighting'];
6969
                }
6970
            }
6971
        }
6972
6973
        // Getting best result
6974
        rsort($my_exercise_result_array);
6975
        $my_exercise_result = 0;
6976
        if (isset($my_exercise_result_array[0])) {
6977
            $my_exercise_result = $my_exercise_result_array[0] * 100;
6978
        }
6979
6980
        $max = 100;
6981
        $pieces = 5;
6982
        $part = round($max / $pieces);
6983
        $x_axis = [];
6984
        $final_array = [];
6985
        $my_final_array = [];
6986
6987
        for ($i = 1; $i <= $pieces; $i++) {
6988
            $sum = 1;
6989
            if ($i == 1) {
6990
                $sum = 0;
6991
            }
6992
            $min = ($i - 1) * $part + $sum;
6993
            $max = $i * $part;
6994
            $x_axis[] = $min." - ".$max;
6995
            $count = 0;
6996
            foreach ($exercise_result as $result) {
6997
                $percentage = $result * 100;
6998
                if ($percentage >= $min && $percentage <= $max) {
6999
                    $count++;
7000
                }
7001
            }
7002
            $final_array[] = $count;
7003
7004
            if ($my_exercise_result >= $min && $my_exercise_result <= $max) {
7005
                $my_final_array[] = 1;
7006
            } else {
7007
                $my_final_array[] = 0;
7008
            }
7009
        }
7010
7011
        // Fix to remove the data of the user with my data
7012
7013
        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...
7014
            if (!empty($my_final_array[$i])) {
7015
                $my_final_array[$i] = $final_array[$i] + 1; // Add my result
7016
                $final_array[$i] = 0;
7017
            }
7018
        }
7019
7020
        // Dataset definition
7021
        $dataSet = new pData();
7022
        $dataSet->addPoints($final_array, 'Serie1');
7023
        $dataSet->addPoints($my_final_array, 'Serie2');
7024
        $dataSet->addPoints($x_axis, 'Serie3');
7025
7026
        $dataSet->setSerieDescription('Serie1', get_lang('Score'));
7027
        $dataSet->setSerieDescription('Serie2', get_lang('MyResults'));
7028
        $dataSet->setAbscissa('Serie3');
7029
7030
        $dataSet->setXAxisName(get_lang('Score'));
7031
        $dataSet->normalize(100, "%");
7032
7033
        $dataSet->loadPalette(api_get_path(SYS_CODE_PATH).'palettes/pchart/default.color', true);
7034
7035
        // Cache definition
7036
        $cachePath = api_get_path(SYS_ARCHIVE_PATH);
7037
        $myCache = new pCache(['CacheFolder' => substr($cachePath, 0, strlen($cachePath) - 1)]);
7038
        $chartHash = $myCache->getHash($dataSet);
7039
7040
        if ($myCache->isInCache($chartHash)) {
7041
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
7042
            $myCache->saveFromCache($chartHash, $imgPath);
7043
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
7044
        } else {
7045
            /* Create the pChart object */
7046
            $widthSize = 480;
7047
            $heightSize = 250;
7048
            $fontSize = 8;
7049
            $myPicture = new pImage($widthSize, $heightSize, $dataSet);
7050
7051
            /* Turn of Antialiasing */
7052
            $myPicture->Antialias = false;
7053
7054
            /* Add a border to the picture */
7055
            $myPicture->drawRectangle(0, 0, $widthSize - 1, $heightSize - 1, ['R' => 0, 'G' => 0, 'B' => 0]);
7056
7057
            /* Set the default font */
7058
            $myPicture->setFontProperties(
7059
                [
7060
                    'FontName' => api_get_path(
7061
                        SYS_FONTS_PATH
7062
                    ).'opensans/OpenSans-Regular.ttf',
7063
                    'FontSize' => 10,
7064
                ]
7065
            );
7066
7067
            /* Write the chart title */
7068
            $myPicture->drawText(
7069
                250,
7070
                20,
7071
                $exercise_title,
7072
                [
7073
                    'FontSize' => 12,
7074
                    'Align' => TEXT_ALIGN_BOTTOMMIDDLE,
7075
                ]
7076
            );
7077
7078
            /* Define the chart area */
7079
            $myPicture->setGraphArea(50, 50, $widthSize - 20, $heightSize - 30);
7080
7081
            /* Draw the scale */
7082
            $scaleSettings = [
7083
                'GridR' => 200,
7084
                'GridG' => 200,
7085
                'GridB' => 200,
7086
                'DrawSubTicks' => true,
7087
                'CycleBackground' => true,
7088
                'Mode' => SCALE_MODE_MANUAL,
7089
                'ManualScale' => [
7090
                    '0' => [
7091
                        'Min' => 0,
7092
                        'Max' => 100,
7093
                    ],
7094
                ],
7095
            ];
7096
            $myPicture->drawScale($scaleSettings);
7097
7098
            /* Turn on shadow computing */
7099
            $myPicture->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]);
7100
7101
            /* Draw the chart */
7102
            $myPicture->setShadow(true, ['X' => 1, 'Y' => 1, 'R' => 0, 'G' => 0, 'B' => 0, 'Alpha' => 10]);
7103
            $settings = [
7104
                'DisplayValues' => true,
7105
                'DisplaySize' => $fontSize,
7106
                'DisplayR' => 0,
7107
                'DisplayG' => 0,
7108
                'DisplayB' => 0,
7109
                'DisplayOrientation' => ORIENTATION_HORIZONTAL,
7110
                'Gradient' => false,
7111
                'Surrounding' => 30,
7112
                'InnerSurrounding' => 25,
7113
            ];
7114
            $myPicture->drawStackedBarChart($settings);
7115
7116
            $legendSettings = [
7117
                'Mode' => LEGEND_HORIZONTAL,
7118
                'Style' => LEGEND_NOBORDER,
7119
            ];
7120
            $myPicture->drawLegend($widthSize / 2, 30, $legendSettings);
7121
7122
            /* Write and save into cache */
7123
            $myCache->writeToCache($chartHash, $myPicture);
7124
            $imgPath = api_get_path(SYS_ARCHIVE_PATH).$chartHash;
7125
            $myCache->saveFromCache($chartHash, $imgPath);
7126
            $imgPath = api_get_path(WEB_ARCHIVE_PATH).$chartHash;
7127
        }
7128
7129
        return $imgPath;
7130
    }
7131
7132
    /**
7133
     * @param FormValidator $form
7134
     */
7135
    public static function setUserSearchForm($form, $displayExtraFields = false)
7136
    {
7137
        global $_configuration;
7138
        $form->addElement('text', 'keyword', get_lang('Keyword'));
7139
        $form->addElement(
7140
            'select',
7141
            'active',
7142
            get_lang('Status'),
7143
            [1 => get_lang('Active'), 0 => get_lang('Inactive')]
7144
        );
7145
7146
        $form->addElement(
7147
            'select',
7148
            'sleeping_days',
7149
            get_lang('InactiveDays'),
7150
            [
7151
                '',
7152
                1 => 1,
7153
                5 => 5,
7154
                15 => 15,
7155
                30 => 30,
7156
                60 => 60,
7157
                90 => 90,
7158
                120 => 120,
7159
            ]
7160
        );
7161
7162
        if ($displayExtraFields) {
7163
            $extraField = new ExtraField('user');
7164
            $extraField->addElements($form, 0, [], true, false, [], [], [], false, true);
7165
        }
7166
7167
        $form->addButtonSearch(get_lang('Search'));
7168
7169
        return $form;
7170
    }
7171
7172
    /**
7173
     * Get the progress of a exercise.
7174
     *
7175
     * @param int    $sessionId  The session ID (session.id)
7176
     * @param int    $courseId   The course ID (course.id)
7177
     * @param int    $exerciseId The quiz ID (c_quiz.id)
7178
     * @param string $date_from
7179
     * @param string $date_to
7180
     * @param array  $options    An array of options you can pass to the query (limit, where and order)
7181
     *
7182
     * @return array An array with the data of exercise(s) progress
7183
     */
7184
    public static function get_exercise_progress(
7185
        $sessionId = 0,
7186
        $courseId = 0,
7187
        $exerciseId = 0,
7188
        $date_from = null,
7189
        $date_to = null,
7190
        $options = []
7191
    ) {
7192
        $sessionId = intval($sessionId);
7193
        $courseId = intval($courseId);
7194
        $exerciseId = intval($exerciseId);
7195
        $date_from = Database::escape_string($date_from);
7196
        $date_to = Database::escape_string($date_to);
7197
        /*
7198
         * This method gets the data by blocks, as previous attempts at one single
7199
         * query made it take ages. The logic of query division is described below
7200
         */
7201
        // Get tables names
7202
        $tuser = Database::get_main_table(TABLE_MAIN_USER);
7203
        $tquiz = Database::get_course_table(TABLE_QUIZ_TEST);
7204
        $tquiz_answer = Database::get_course_table(TABLE_QUIZ_ANSWER);
7205
        $tquiz_question = Database::get_course_table(TABLE_QUIZ_QUESTION);
7206
        $tquiz_rel_question = Database::get_course_table(TABLE_QUIZ_TEST_QUESTION);
7207
        $ttrack_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
7208
        $ttrack_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
7209
7210
        $sessions = [];
7211
        $courses = [];
7212
        // if session ID is defined but course ID is empty, get all the courses
7213
        // from that session
7214
        if (!empty($sessionId) && empty($courseId)) {
7215
            // $courses is an array of course int id as index and course details hash as value
7216
            $courses = SessionManager::get_course_list_by_session_id($sessionId);
7217
            $sessions[$sessionId] = api_get_session_info($sessionId);
7218
        } elseif (empty($sessionId) && !empty($courseId)) {
7219
            // if, to the contrary, course is defined but not sessions, get the sessions that include this course
7220
            // $sessions is an array like: [0] => ('id' => 3, 'name' => 'Session 35'), [1] => () etc;
7221
            $course = api_get_course_info_by_id($courseId);
7222
            $sessionsTemp = SessionManager::get_session_by_course($courseId);
7223
            $courses[$courseId] = $course;
7224
            foreach ($sessionsTemp as $sessionItem) {
7225
                $sessions[$sessionItem['id']] = $sessionItem;
7226
            }
7227
        } elseif (!empty($courseId) && !empty($sessionId)) {
7228
            // none is empty
7229
            $course = api_get_course_info_by_id($courseId);
7230
            $courses[$courseId] = [$course['code']];
7231
            $courses[$courseId]['code'] = $course['code'];
7232
            $sessions[$sessionId] = api_get_session_info($sessionId);
7233
        } else {
7234
            // both are empty, not enough data, return an empty array
7235
            return [];
7236
        }
7237
        // Now we have two arrays of courses and sessions with enough data to proceed
7238
        // If no course could be found, we shouldn't return anything.
7239
        // Sessions can be empty (then we only return the pure-course-context results)
7240
        if (count($courses) < 1) {
7241
            return [];
7242
        }
7243
7244
        $data = [];
7245
        // The following loop is less expensive than what it seems:
7246
        // - if a course was defined, then we only loop through sessions
7247
        // - if a session was defined, then we only loop through courses
7248
        // - if a session and a course were defined, then we only loop once
7249
        foreach ($courses as $courseIdx => $courseData) {
7250
            $where = '';
7251
            $whereParams = [];
7252
            $whereSessionParams = '';
7253
            if (count($sessions > 0)) {
7254
                foreach ($sessions as $sessionIdx => $sessionData) {
7255
                    if (!empty($sessionIdx)) {
7256
                        $whereSessionParams .= $sessionIdx.',';
7257
                    }
7258
                }
7259
                $whereSessionParams = substr($whereSessionParams, 0, -1);
7260
            }
7261
7262
            if (!empty($exerciseId)) {
7263
                $exerciseId = intval($exerciseId);
7264
                $where .= ' AND q.id = %d ';
7265
                $whereParams[] = $exerciseId;
7266
            }
7267
7268
            /*
7269
             * This feature has been disabled for now, to avoid having to
7270
             * join two very large tables
7271
            //2 = show all questions (wrong and correct answered)
7272
            if ($answer != 2) {
7273
                $answer = intval($answer);
7274
                //$where .= ' AND qa.correct = %d';
7275
                //$whereParams[] = $answer;
7276
            }
7277
            */
7278
7279
            $limit = '';
7280
            if (!empty($options['limit'])) {
7281
                $limit = " LIMIT ".$options['limit'];
7282
            }
7283
7284
            if (!empty($options['where'])) {
7285
                $where .= ' AND '.Database::escape_string($options['where']);
7286
            }
7287
7288
            $order = '';
7289
            if (!empty($options['order'])) {
7290
                $order = " ORDER BY ".$options['order'];
7291
            }
7292
7293
            if (!empty($date_to) && !empty($date_from)) {
7294
                $where .= sprintf(" AND (te.start_date BETWEEN '%s 00:00:00' AND '%s 23:59:59')", $date_from, $date_to);
7295
            }
7296
7297
            $sql = "SELECT
7298
                te.session_id,
7299
                ta.id as attempt_id,
7300
                te.exe_user_id as user_id,
7301
                te.exe_id as exercise_attempt_id,
7302
                ta.question_id,
7303
                ta.answer as answer_id,
7304
                ta.tms as time,
7305
                te.exe_exo_id as quiz_id,
7306
                CONCAT ('c', q.c_id, '_e', q.iid) as exercise_id,
7307
                q.title as quiz_title,
7308
                qq.description as description
7309
                FROM $ttrack_exercises te
7310
                INNER JOIN $ttrack_attempt ta
7311
                ON ta.exe_id = te.exe_id
7312
                INNER JOIN $tquiz q
7313
                ON q.iid = te.exe_exo_id
7314
                INNER JOIN $tquiz_rel_question rq
7315
                ON rq.exercice_id = q.iid AND rq.c_id = q.c_id
7316
                INNER JOIN $tquiz_question qq
7317
                ON
7318
                    qq.iid = rq.question_id AND
7319
                    qq.position = rq.question_order AND
7320
                    ta.question_id = rq.question_id
7321
                WHERE
7322
                    te.c_id = $courseIdx ".(empty($whereSessionParams) ? '' : "AND te.session_id IN ($whereSessionParams)")."
7323
                    AND q.c_id = $courseIdx
7324
                    $where $order $limit";
7325
            $sql_query = vsprintf($sql, $whereParams);
7326
7327
            // Now browse through the results and get the data
7328
            $rs = Database::query($sql_query);
7329
            $userIds = [];
7330
            $questionIds = [];
7331
            $answerIds = [];
7332
            while ($row = Database::fetch_array($rs)) {
7333
                // only show if exercise is visible
7334
                if (api_get_item_visibility($courseData, 'quiz', $row['exercise_id'])) {
7335
                    $userIds[$row['user_id']] = $row['user_id'];
7336
                    $questionIds[$row['question_id']] = $row['question_id'];
7337
                    $answerIds[$row['question_id']][$row['answer_id']] = $row['answer_id'];
7338
                    $row['session'] = $sessions[$row['session_id']];
7339
                    $data[] = $row;
7340
                }
7341
            }
7342
            // Now fill questions data. Query all questions and answers for this test to avoid
7343
            $sqlQuestions = "SELECT tq.c_id, tq.iid as question_id, tq.question, tqa.id_auto,
7344
                            tqa.answer, tqa.correct, tq.position, tqa.id_auto as answer_id
7345
                            FROM $tquiz_question tq, $tquiz_answer tqa
7346
                            WHERE
7347
                                tqa.question_id = tq.iid AND
7348
                                tq.c_id = $courseIdx AND
7349
                                tq.id IN (".implode(',', $questionIds).")";
7350
7351
            $resQuestions = Database::query($sqlQuestions);
7352
            $answer = [];
7353
            $question = [];
7354
            while ($rowQuestion = Database::fetch_assoc($resQuestions)) {
7355
                $questionId = $rowQuestion['question_id'];
7356
                $answerId = $rowQuestion['answer_id'];
7357
                $answer[$questionId][$answerId] = [
7358
                    'position' => $rowQuestion['position'],
7359
                    'question' => $rowQuestion['question'],
7360
                    'answer' => $rowQuestion['answer'],
7361
                    'correct' => $rowQuestion['correct'],
7362
                ];
7363
                $question[$questionId]['question'] = $rowQuestion['question'];
7364
            }
7365
7366
            // Now fill users data
7367
            $sqlUsers = "SELECT user_id, username, lastname, firstname
7368
                         FROM $tuser
7369
                         WHERE user_id IN (".implode(',', $userIds).")";
7370
            $resUsers = Database::query($sqlUsers);
7371
            while ($rowUser = Database::fetch_assoc($resUsers)) {
7372
                $users[$rowUser['user_id']] = $rowUser;
7373
            }
7374
7375
            foreach ($data as $id => $row) {
7376
                $rowQuestId = $row['question_id'];
7377
                $rowAnsId = $row['answer_id'];
7378
                $data[$id]['session'] = $sessions[$row['session_id']]['name'];
7379
                $data[$id]['firstname'] = $users[$row['user_id']]['firstname'];
7380
                $data[$id]['lastname'] = $users[$row['user_id']]['lastname'];
7381
                $data[$id]['username'] = $users[$row['user_id']]['username'];
7382
                $data[$id]['answer'] = $answer[$rowQuestId][$rowAnsId]['answer'];
7383
                $data[$id]['correct'] = ($answer[$rowQuestId][$rowAnsId]['correct'] == 0 ? get_lang('No') : get_lang('Yes'));
7384
                $data[$id]['question'] = $question[$rowQuestId]['question'];
7385
                $data[$id]['question_id'] = $rowQuestId;
7386
                $data[$id]['description'] = $row['description'];
7387
            }
7388
7389
            /*
7390
            The minimum expected array structure at the end is:
7391
            attempt_id,
7392
            session name,
7393
            exercise_id,
7394
            quiz_title,
7395
            username,
7396
            lastname,
7397
            firstname,
7398
            time,
7399
            question_id,
7400
            question,
7401
            answer,
7402
            */
7403
        }
7404
7405
        return $data;
7406
    }
7407
7408
    /**
7409
     * @param string             $tool
7410
     * @param sessionEntity|null $session Optional
7411
     *
7412
     * @throws \Doctrine\ORM\NonUniqueResultException
7413
     *
7414
     * @return \Chamilo\CourseBundle\Entity\CStudentPublication|null
7415
     */
7416
    public static function getLastStudentPublication(
7417
        User $user,
7418
        $tool,
7419
        Course $course,
7420
        SessionEntity $session = null
7421
    ) {
7422
        return Database::getManager()
7423
            ->createQuery("
7424
                SELECT csp
7425
                FROM ChamiloCourseBundle:CStudentPublication csp
7426
                INNER JOIN ChamiloCourseBundle:CItemProperty cip
7427
                    WITH (
7428
                        csp.iid = cip.ref AND
7429
                        csp.session = cip.session AND
7430
                        csp.cId = cip.course AND
7431
                        csp.userId = cip.lasteditUserId
7432
                    )
7433
                WHERE
7434
                    cip.session = :session AND cip.course = :course AND cip.lasteditUserId = :user AND cip.tool = :tool
7435
                ORDER BY csp.iid DESC
7436
            ")
7437
            ->setMaxResults(1)
7438
            ->setParameters([
7439
                'tool' => $tool,
7440
                'session' => $session,
7441
                'course' => $course,
7442
                'user' => $user,
7443
            ])
7444
            ->getOneOrNullResult();
7445
    }
7446
7447
    /**
7448
     * Get the HTML code for show a block with the achieved user skill on course/session.
7449
     *
7450
     * @param int  $userId
7451
     * @param int  $courseId
7452
     * @param int  $sessionId
7453
     * @param bool $forceView forces the view of the skills, not checking for deeper access
7454
     *
7455
     * @return string
7456
     */
7457
    public static function displayUserSkills($userId, $courseId = 0, $sessionId = 0, $forceView = false)
7458
    {
7459
        if (Skill::isAllowed($userId, false) === false && $forceView == false) {
7460
            return '';
7461
        }
7462
        $skillManager = new Skill();
7463
        $html = $skillManager->getUserSkillsTable($userId, $courseId, $sessionId)['table'];
7464
7465
        return $html;
7466
    }
7467
7468
    /**
7469
     * @param int $userId
7470
     * @param int $courseId
7471
     * @param int $sessionId
7472
     *
7473
     * @return array
7474
     */
7475
    public static function getCalculateTime($userId, $courseId, $sessionId)
7476
    {
7477
        $userId = (int) $userId;
7478
        $courseId = (int) $courseId;
7479
        $sessionId = (int) $sessionId;
7480
7481
        if (empty($userId) || empty($courseId)) {
7482
            return [];
7483
        }
7484
7485
        $sql = "SELECT MIN(date_reg) min, MAX(date_reg) max
7486
                FROM track_e_access_complete
7487
                WHERE
7488
                    user_id = $userId AND
7489
                    c_id = $courseId AND
7490
                    session_id = $sessionId AND
7491
                    login_as = 0
7492
                ORDER BY date_reg ASC
7493
                LIMIT 1";
7494
        $rs = Database::query($sql);
7495
7496
        $firstConnection = '';
7497
        $lastConnection = '';
7498
        if (Database::num_rows($rs) > 0) {
7499
            $value = Database::fetch_array($rs);
7500
            $firstConnection = $value['min'];
7501
            $lastConnection = $value['max'];
7502
        }
7503
7504
        $sql = "SELECT * FROM track_e_access_complete
7505
                WHERE
7506
                    user_id = $userId AND
7507
                    c_id = $courseId AND
7508
                    session_id = $sessionId AND
7509
                    login_as = 0 AND current_id <> 0";
7510
7511
        $res = Database::query($sql);
7512
        $reg = [];
7513
        while ($row = Database::fetch_assoc($res)) {
7514
            $reg[$row['id']] = $row;
7515
            $reg[$row['id']]['date_reg'] = strtotime($row['date_reg']);
7516
        }
7517
7518
        $sessions = [];
7519
        foreach ($reg as $key => $value) {
7520
            $sessions[$value['current_id']][$value['tool']][] = $value;
7521
        }
7522
7523
        $quizTime = 0;
7524
        $result = [];
7525
        $totalTime = 0;
7526
        $lpTime = [];
7527
        $lpDetailTime = [];
7528
        foreach ($sessions as $listPerTool) {
7529
            $min = 0;
7530
            $max = 0;
7531
            $sessionDiff = 0;
7532
            foreach ($listPerTool as $tool => $results) {
7533
                $beforeItem = [];
7534
                foreach ($results as $item) {
7535
                    if (empty($beforeItem)) {
7536
                        $beforeItem = $item;
7537
                        if (empty($min)) {
7538
                            $min = $item['date_reg'];
7539
                        }
7540
7541
                        if (empty($max)) {
7542
                            $max = $item['date_reg'];
7543
                        }
7544
                        continue;
7545
                    }
7546
7547
                    $partialTime = $item['date_reg'] - $beforeItem['date_reg'];
7548
                    if ($item['date_reg'] > $max) {
7549
                        $max = $item['date_reg'];
7550
                    }
7551
7552
                    if (empty($min)) {
7553
                        $min = $item['date_reg'];
7554
                    }
7555
7556
                    if ($item['date_reg'] < $min) {
7557
                        $min = $item['date_reg'];
7558
                    }
7559
7560
                    switch ($tool) {
7561
                        case TOOL_AGENDA:
7562
                        case TOOL_FORUM:
7563
                        case TOOL_ANNOUNCEMENT:
7564
                        case TOOL_COURSE_DESCRIPTION:
7565
                        case TOOL_SURVEY:
7566
                        case TOOL_NOTEBOOK:
7567
                        case TOOL_GRADEBOOK:
7568
                        case TOOL_DROPBOX:
7569
                        case 'Reports':
7570
                        case 'Videoconference':
7571
                        case TOOL_LINK:
7572
                        case TOOL_CHAT:
7573
                        case 'course-main':
7574
                            if (!isset($result[$tool])) {
7575
                                $result[$tool] = 0;
7576
                            }
7577
                            $result[$tool] += $partialTime;
7578
                            break;
7579
                        case TOOL_LEARNPATH:
7580
                            if ($item['tool_id'] != $beforeItem['tool_id']) {
7581
                                break;
7582
                            }
7583
                            if (!isset($lpTime[$item['tool_id']])) {
7584
                                $lpTime[$item['tool_id']] = 0;
7585
                            }
7586
7587
                            // Saving the attempt id "action_details"
7588
                            if (!empty($item['tool_id'])) {
7589
                                if (!empty($item['tool_id_detail'])) {
7590
                                    if (!isset($lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']])) {
7591
                                        $lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']] = 0;
7592
                                    }
7593
                                    $lpDetailTime[$item['tool_id']][$item['tool_id_detail']][$item['action_details']] += $partialTime;
7594
                                }
7595
                                $lpTime[$item['tool_id']] += $partialTime;
7596
                            }
7597
                            break;
7598
                        case TOOL_QUIZ:
7599
                            if (!isset($lpTime[$item['action_details']])) {
7600
                                $lpTime[$item['action_details']] = 0;
7601
                            }
7602
                            if ($beforeItem['action'] === 'learnpath_id') {
7603
                                $lpTime[$item['action_details']] += $partialTime;
7604
                            } else {
7605
                                $quizTime += $partialTime;
7606
                            }
7607
                            break;
7608
                    }
7609
                    $beforeItem = $item;
7610
                }
7611
            }
7612
7613
            $sessionDiff += $max - $min;
7614
            if ($sessionDiff > 0) {
7615
                $totalTime += $sessionDiff;
7616
            }
7617
        }
7618
7619
        $totalLp = 0;
7620
        foreach ($lpTime as $value) {
7621
            $totalLp += $value;
7622
        }
7623
7624
        $result['learnpath_detailed'] = $lpDetailTime;
7625
        $result[TOOL_LEARNPATH] = $lpTime;
7626
        $result[TOOL_QUIZ] = $quizTime;
7627
        $result['total_learnpath'] = $totalLp;
7628
        $result['total_time'] = $totalTime;
7629
        $result['number_connections'] = count($sessions);
7630
        $result['first'] = $firstConnection;
7631
        $result['last'] = $lastConnection;
7632
7633
        return $result;
7634
    }
7635
7636
    /**
7637
     * Gets the IP of a given user, using the last login before the given date.
7638
     *
7639
     * @param int User ID
7640
     * @param string Datetime
7641
     * @param bool Whether to return the IP as a link or just as an IP
7642
     * @param string If defined and return_as_link if true, will be used as the text to be shown as the link
7643
     *
7644
     * @return string IP address (or false on error)
7645
     *
7646
     * @assert (0,0) === false
7647
     */
7648
    public static function get_ip_from_user_event(
7649
        $user_id,
7650
        $event_date,
7651
        $return_as_link = false,
7652
        $body_replace = null
7653
    ) {
7654
        if (empty($user_id) || empty($event_date)) {
7655
            return false;
7656
        }
7657
        $user_id = intval($user_id);
7658
        $event_date = Database::escape_string($event_date);
7659
        $table_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
7660
        $sql_ip = "SELECT login_date, user_ip
7661
                   FROM $table_login
7662
                   WHERE login_user_id = $user_id AND login_date < '$event_date'
7663
                   ORDER BY login_date DESC LIMIT 1";
7664
        $ip = '';
7665
        $res_ip = Database::query($sql_ip);
7666
        if ($res_ip !== false && Database::num_rows($res_ip) > 0) {
7667
            $row_ip = Database::fetch_row($res_ip);
7668
            if ($return_as_link) {
7669
                $ip = Display::url(
7670
                    empty($body_replace) ? $row_ip[1] : $body_replace,
7671
                    'http://www.whatsmyip.org/ip-geo-location/?ip='.$row_ip[1],
7672
                    ['title' => get_lang('TraceIP'), 'target' => '_blank']
7673
                );
7674
            } else {
7675
                $ip = $row_ip[1];
7676
            }
7677
        }
7678
7679
        return $ip;
7680
    }
7681
7682
    /**
7683
     * @param int   $userId
7684
     * @param array $courseInfo
7685
     * @param int   $sessionId
7686
     *
7687
     * @return array
7688
     */
7689
    public static function getToolInformation(
7690
        $userId,
7691
        $courseInfo,
7692
        $sessionId = 0
7693
    ) {
7694
        $csvContent = [];
7695
        $courseToolInformation = '';
7696
        $headerTool = [
7697
            [get_lang('Title')],
7698
            [get_lang('CreatedAt')],
7699
            [get_lang('UpdatedAt')],
7700
        ];
7701
7702
        $headerListForCSV = [];
7703
        foreach ($headerTool as $item) {
7704
            $headerListForCSV[] = $item[0];
7705
        }
7706
7707
        $courseForumInformationArray = getForumCreatedByUser(
7708
            $userId,
7709
            $courseInfo,
7710
            $sessionId
7711
        );
7712
7713
        if (!empty($courseForumInformationArray)) {
7714
            $csvContent[] = [];
7715
            $csvContent[] = [get_lang('Forums')];
7716
            $csvContent[] = $headerListForCSV;
7717
            foreach ($courseForumInformationArray as $row) {
7718
                $csvContent[] = $row;
7719
            }
7720
7721
            $courseToolInformation .= Display::page_subheader2(
7722
                get_lang('Forums')
7723
            );
7724
            $courseToolInformation .= Display::return_sortable_table(
7725
                $headerTool,
7726
                $courseForumInformationArray
7727
            );
7728
        }
7729
7730
        $courseWorkInformationArray = getWorkCreatedByUser(
7731
            $userId,
7732
            $courseInfo['real_id'],
7733
            $sessionId
7734
        );
7735
7736
        if (!empty($courseWorkInformationArray)) {
7737
            $csvContent[] = null;
7738
            $csvContent[] = [get_lang('Works')];
7739
            $csvContent[] = $headerListForCSV;
7740
7741
            foreach ($courseWorkInformationArray as $row) {
7742
                $csvContent[] = $row;
7743
            }
7744
            $csvContent[] = null;
7745
7746
            $courseToolInformation .= Display::page_subheader2(
7747
                get_lang('Works')
7748
            );
7749
            $courseToolInformation .= Display::return_sortable_table(
7750
                $headerTool,
7751
                $courseWorkInformationArray
7752
            );
7753
        }
7754
7755
        $courseToolInformationTotal = null;
7756
        if (!empty($courseToolInformation)) {
7757
            $sessionTitle = null;
7758
            if (!empty($sessionId)) {
7759
                $sessionTitle = ' ('.api_get_session_name($sessionId).')';
7760
            }
7761
7762
            $courseToolInformationTotal .= Display::page_subheader(
7763
                $courseInfo['title'].$sessionTitle
7764
            );
7765
            $courseToolInformationTotal .= $courseToolInformation;
7766
        }
7767
7768
        return [
7769
            'array' => $csvContent,
7770
            'html' => $courseToolInformationTotal,
7771
        ];
7772
    }
7773
7774
    /**
7775
     * @param int $sessionId
7776
     *
7777
     * @return bool
7778
     */
7779
    public static function isAllowToTrack($sessionId)
7780
    {
7781
        return
7782
            api_is_platform_admin(true, true)
7783
            || SessionManager::user_is_general_coach(api_get_user_id(), $sessionId)
7784
            || api_is_allowed_to_create_course()
7785
            || api_is_course_tutor()
7786
            || api_is_course_admin();
7787
    }
7788
7789
    public static function getCourseLpProgress($userId, $sessionId)
7790
    {
7791
        $controller = new IndexManager(get_lang('MyCourses'));
7792
        $data = $controller->returnCoursesAndSessions($userId);
7793
        $courseList = $data['courses'];
7794
        $result = [];
7795
        if ($courseList) {
7796
            // $counter = 1;
7797
            foreach ($courseList as $course) {
7798
                $courseId = $course['course_id'];
7799
                $courseInfo = api_get_course_info_by_id($courseId);
7800
                if (empty($courseInfo)) {
7801
                    continue;
7802
                }
7803
                $courseCode = $courseInfo['code'];
7804
                $lpTimeList = self::getCalculateTime($userId, $courseId, $sessionId);
7805
7806
                // total progress
7807
                $list = new LearnpathList(
7808
                    $userId,
7809
                    $courseInfo,
7810
                    0,
7811
                    'lp.publicatedOn ASC',
7812
                    true,
7813
                    null,
7814
                    true
7815
                );
7816
7817
                $list = $list->get_flat_list();
7818
                $totalProgress = 0;
7819
                $totalTime = 0;
7820
                if (!empty($list)) {
7821
                    foreach ($list as $lp_id => $learnpath) {
7822
                        if (!$learnpath['lp_visibility']) {
7823
                            continue;
7824
                        }
7825
                        $lpProgress = self::get_avg_student_progress($userId, $courseCode, [$lp_id], $sessionId);
7826
                        $time = isset($lpTimeList[TOOL_LEARNPATH][$lp_id]) ? $lpTimeList[TOOL_LEARNPATH][$lp_id] : 0;
7827
                        if ($lpProgress == 100) {
7828
                            if (!empty($time)) {
7829
                                $timeInMinutes = $time / 60;
7830
                                $min = (int) learnpath::getAccumulateWorkTimePrerequisite($lp_id, $courseId);
7831
                                if ($timeInMinutes >= $min) {
7832
                                    $totalProgress++;
7833
                                }
7834
                            }
7835
                        }
7836
                        $totalTime += $time;
7837
                    }
7838
7839
                    if (!empty($totalProgress)) {
7840
                        $totalProgress = (float) api_number_format($totalProgress / count($list) * 100, 2);
7841
                    }
7842
                }
7843
7844
                $progress = self::get_avg_student_progress($userId, $courseCode, [], $sessionId);
7845
7846
                $result[] = [
7847
                    'module' => $courseInfo['name'],
7848
                    'progress' => $progress,
7849
                    'qualification' => $totalProgress,
7850
                    'activeTime' => $totalTime,
7851
                ];
7852
            }
7853
        }
7854
7855
        return $result;
7856
    }
7857
7858
    /**
7859
     * @param int $userId
7860
     * @param int $courseId
7861
     * @param int $sessionId
7862
     *
7863
     * @return int
7864
     */
7865
    public static function getNumberOfCourseAccessDates($userId, $courseId, $sessionId)
7866
    {
7867
        $tblTrackCourseAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
7868
        $sessionCondition = api_get_session_condition($sessionId);
7869
        $courseId = (int) $courseId;
7870
        $userId = (int) $userId;
7871
7872
        $sql = "SELECT COUNT(DISTINCT (DATE(login_course_date))) AS c
7873
                FROM $tblTrackCourseAccess
7874
                WHERE c_id = $courseId $sessionCondition AND user_id = $userId";
7875
7876
        $result = Database::fetch_assoc(Database::query($sql));
7877
7878
        return (int) $result['c'];
7879
    }
7880
7881
    public static function processUserDataMove(
7882
        $user_id,
7883
        $course_info,
7884
        $origin_session_id,
7885
        $new_session_id,
7886
        $update_database,
7887
        $debug = false
7888
    ) {
7889
        // Begin with the import process
7890
        $origin_course_code = $course_info['code'];
7891
        $course_id = $course_info['real_id'];
7892
        $user_id = (int) $user_id;
7893
        $origin_session_id = (int) $origin_session_id;
7894
        $new_session_id = (int) $new_session_id;
7895
        $session = api_get_session_entity($new_session_id);
7896
        $em = Database::getManager();
7897
7898
        $TABLETRACK_EXERCICES = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
7899
        $TBL_TRACK_ATTEMPT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
7900
        $attemptRecording = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
7901
        $TBL_TRACK_E_COURSE_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
7902
        $TBL_TRACK_E_LAST_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
7903
        $TBL_LP_VIEW = Database::get_course_table(TABLE_LP_VIEW);
7904
        $TBL_NOTEBOOK = Database::get_course_table(TABLE_NOTEBOOK);
7905
        $TBL_STUDENT_PUBLICATION = Database::get_course_table(TABLE_STUDENT_PUBLICATION);
7906
        $TBL_STUDENT_PUBLICATION_ASSIGNMENT = Database::get_course_table(TABLE_STUDENT_PUBLICATION_ASSIGNMENT);
7907
        $TBL_ITEM_PROPERTY = Database::get_course_table(TABLE_ITEM_PROPERTY);
7908
7909
        $TBL_DROPBOX_FILE = Database::get_course_table(TABLE_DROPBOX_FILE);
7910
        $TBL_DROPBOX_POST = Database::get_course_table(TABLE_DROPBOX_POST);
7911
        $TBL_AGENDA = Database::get_course_table(TABLE_AGENDA);
7912
7913
        // 1. track_e_exercises
7914
        // ORIGINAL COURSE
7915
        $sql = "SELECT * FROM $TABLETRACK_EXERCICES
7916
                WHERE c_id = $course_id AND  session_id = $origin_session_id AND exe_user_id = $user_id ";
7917
        $res = Database::query($sql);
7918
        $list = [];
7919
        while ($row = Database::fetch_array($res, 'ASSOC')) {
7920
            $list[$row['exe_id']] = $row;
7921
        }
7922
7923
        $result_message = [];
7924
        $result_message_compare = [];
7925
        if (!empty($list)) {
7926
            foreach ($list as $exe_id => $data) {
7927
                if ($update_database) {
7928
                    $sql = "UPDATE $TABLETRACK_EXERCICES SET session_id = '$new_session_id' WHERE exe_id = $exe_id";
7929
                    Database::query($sql);
7930
7931
                    $sql = "UPDATE $TBL_TRACK_ATTEMPT SET session_id = '$new_session_id' WHERE exe_id = $exe_id";
7932
                    Database::query($sql);
7933
7934
                    $sql = "UPDATE $attemptRecording SET session_id = '$new_session_id' WHERE exe_id = $exe_id";
7935
                    Database::query($sql);
7936
7937
                    if (!isset($result_message[$TABLETRACK_EXERCICES])) {
7938
                        $result_message[$TABLETRACK_EXERCICES] = 0;
7939
                    }
7940
                    $result_message[$TABLETRACK_EXERCICES]++;
7941
                } else {
7942
                    if (!empty($data['orig_lp_id']) && !empty($data['orig_lp_item_id'])) {
7943
                        $result_message['TRACK_E_EXERCISES'][$exe_id] = $data;
7944
                    } else {
7945
                        $result_message['TRACK_E_EXERCISES_IN_LP'][$exe_id] = $data;
7946
                    }
7947
                }
7948
            }
7949
        }
7950
7951
        // DESTINY COURSE
7952
        if (!$update_database) {
7953
            $sql = "SELECT * FROM $TABLETRACK_EXERCICES
7954
                    WHERE
7955
                        c_id = $course_id AND
7956
                        session_id = $new_session_id AND
7957
                        exe_user_id = $user_id ";
7958
            $res = Database::query($sql);
7959
            $list = [];
7960
            while ($row = Database::fetch_array($res, 'ASSOC')) {
7961
                $list[$row['exe_id']] = $row;
7962
            }
7963
7964
            if (!empty($list)) {
7965
                foreach ($list as $exe_id => $data) {
7966
                    if ($update_database) {
7967
                        $sql = "UPDATE $TABLETRACK_EXERCICES
7968
                                SET session_id = '$new_session_id'
7969
                                WHERE exe_id = $exe_id";
7970
                        Database::query($sql);
7971
                        $result_message[$TABLETRACK_EXERCICES]++;
7972
                    } else {
7973
                        if (!empty($data['orig_lp_id']) && !empty($data['orig_lp_item_id'])) {
7974
                            $result_message_compare['TRACK_E_EXERCISES'][$exe_id] = $data;
7975
                        } else {
7976
                            $result_message_compare['TRACK_E_EXERCISES_IN_LP'][$exe_id] = $data;
7977
                        }
7978
                    }
7979
                }
7980
            }
7981
        }
7982
7983
        // 2.track_e_attempt, track_e_attempt_recording, track_e_downloads
7984
        // Nothing to do because there are not relationship with a session
7985
        // 3. track_e_course_access
7986
        $sql = "SELECT * FROM $TBL_TRACK_E_COURSE_ACCESS
7987
                WHERE c_id = $course_id AND session_id = $origin_session_id  AND user_id = $user_id ";
7988
        $res = Database::query($sql);
7989
        $list = [];
7990
        while ($row = Database::fetch_array($res, 'ASSOC')) {
7991
            $list[$row['course_access_id']] = $row;
7992
        }
7993
7994
        if (!empty($list)) {
7995
            foreach ($list as $id => $data) {
7996
                if ($update_database) {
7997
                    $sql = "UPDATE $TBL_TRACK_E_COURSE_ACCESS
7998
                            SET session_id = $new_session_id
7999
                            WHERE course_access_id = $id";
8000
                    if ($debug) {
8001
                        echo $sql;
8002
                    }
8003
                    Database::query($sql);
8004
                    if (!isset($result_message[$TBL_TRACK_E_COURSE_ACCESS])) {
8005
                        $result_message[$TBL_TRACK_E_COURSE_ACCESS] = 0;
8006
                    }
8007
                    $result_message[$TBL_TRACK_E_COURSE_ACCESS]++;
8008
                }
8009
            }
8010
        }
8011
8012
        // 4. track_e_lastaccess
8013
        $sql = "SELECT access_id FROM $TBL_TRACK_E_LAST_ACCESS
8014
                WHERE
8015
                    c_id = $course_id AND
8016
                    access_session_id = $origin_session_id AND
8017
                    access_user_id = $user_id ";
8018
        $res = Database::query($sql);
8019
        $list = [];
8020
        while ($row = Database::fetch_array($res, 'ASSOC')) {
8021
            $list[] = $row['access_id'];
8022
        }
8023
8024
        if (!empty($list)) {
8025
            foreach ($list as $id) {
8026
                if ($update_database) {
8027
                    $sql = "UPDATE $TBL_TRACK_E_LAST_ACCESS
8028
                            SET access_session_id = $new_session_id
8029
                            WHERE access_id = $id";
8030
                    if ($debug) {
8031
                        echo $sql;
8032
                    }
8033
                    Database::query($sql);
8034
                    if (!isset($result_message[$TBL_TRACK_E_LAST_ACCESS])) {
8035
                        $result_message[$TBL_TRACK_E_LAST_ACCESS] = 0;
8036
                    }
8037
                    $result_message[$TBL_TRACK_E_LAST_ACCESS]++;
8038
                }
8039
            }
8040
        }
8041
8042
        // 5. lp_item_view
8043
        // CHECK ORIGIN
8044
        $sql = "SELECT * FROM $TBL_LP_VIEW
8045
                WHERE user_id = $user_id AND session_id = $origin_session_id AND c_id = $course_id ";
8046
        $res = Database::query($sql);
8047
8048
        // Getting the list of LPs in the new session
8049
        $lp_list = new LearnpathList($user_id, $course_info, $new_session_id);
8050
        $flat_list = $lp_list->get_flat_list();
8051
        $list = [];
8052
        while ($row = Database::fetch_array($res, 'ASSOC')) {
8053
            // Checking if the LP exist in the new session
8054
            // if (in_array($row['lp_id'], array_keys($flat_list))) {
8055
            $list[$row['id']] = $row;
8056
            // }
8057
        }
8058
8059
        if (!empty($list)) {
8060
            foreach ($list as $id => $data) {
8061
                if ($update_database) {
8062
                    $sql = "UPDATE $TBL_LP_VIEW
8063
                            SET session_id = $new_session_id
8064
                            WHERE c_id = $course_id AND id = $id ";
8065
                    if ($debug) {
8066
                        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...
8067
                    }
8068
                    $res = Database::query($sql);
8069
                    if ($debug) {
8070
                        var_dump($res);
8071
                    }
8072
                    if (!isset($result_message[$TBL_LP_VIEW])) {
8073
                        $result_message[$TBL_LP_VIEW] = 0;
8074
                    }
8075
                    $result_message[$TBL_LP_VIEW]++;
8076
                } else {
8077
                    // Getting all information of that lp_item_id
8078
                    $score = self::get_avg_student_score(
8079
                        $user_id,
8080
                        $origin_course_code,
8081
                        [$data['lp_id']],
8082
                        $origin_session_id
8083
                    );
8084
                    $progress = self::get_avg_student_progress(
8085
                        $user_id,
8086
                        $origin_course_code,
8087
                        [$data['lp_id']],
8088
                        $origin_session_id
8089
                    );
8090
                    $result_message['LP_VIEW'][$data['lp_id']] = [
8091
                        'score' => $score,
8092
                        'progress' => $progress,
8093
                    ];
8094
                }
8095
            }
8096
        }
8097
8098
        // Check destination.
8099
        if (!$update_database) {
8100
            $sql = "SELECT * FROM $TBL_LP_VIEW
8101
                    WHERE user_id = $user_id AND session_id = $new_session_id AND c_id = $course_id";
8102
            $res = Database::query($sql);
8103
8104
            // Getting the list of LPs in the new session
8105
            $lp_list = new LearnpathList($user_id, $course_info, $new_session_id);
8106
            $flat_list = $lp_list->get_flat_list();
8107
8108
            $list = [];
8109
            while ($row = Database::fetch_array($res, 'ASSOC')) {
8110
                // Checking if the LP exist in the new session
8111
                // if (in_array($row['lp_id'], array_keys($flat_list))) {
8112
                $list[$row['id']] = $row;
8113
                // }
8114
            }
8115
8116
            if (!empty($list)) {
8117
                foreach ($list as $id => $data) {
8118
                    // Getting all information of that lp_item_id
8119
                    $score = self::get_avg_student_score(
8120
                        $user_id,
8121
                        $origin_course_code,
8122
                        [$data['lp_id']],
8123
                        $new_session_id
8124
                    );
8125
                    $progress = self::get_avg_student_progress(
8126
                        $user_id,
8127
                        $origin_course_code,
8128
                        [$data['lp_id']],
8129
                        $new_session_id
8130
                    );
8131
                    $result_message_compare['LP_VIEW'][$data['lp_id']] = [
8132
                        'score' => $score,
8133
                        'progress' => $progress,
8134
                    ];
8135
                }
8136
            }
8137
        }
8138
8139
        // 6. Agenda
8140
        // calendar_event_attachment no problems no session_id
8141
        $sql = "SELECT ref FROM $TBL_ITEM_PROPERTY
8142
                WHERE tool = 'calendar_event' AND insert_user_id = $user_id AND c_id = $course_id ";
8143
        $res = Database::query($sql);
8144
        while ($row = Database::fetch_array($res, 'ASSOC')) {
8145
            $id = $row['ref'];
8146
            if ($update_database) {
8147
                $sql = "UPDATE $TBL_AGENDA SET session_id = $new_session_id WHERE c_id = $course_id AND id = $id ";
8148
                if ($debug) {
8149
                    var_dump($sql);
8150
                }
8151
                $res_update = Database::query($sql);
8152
                if ($debug) {
8153
                    var_dump($res_update);
8154
                }
8155
                if (!isset($result_message['agenda'])) {
8156
                    $result_message['agenda'] = 0;
8157
                }
8158
                $result_message['agenda']++;
8159
            }
8160
        }
8161
8162
        // 7. Forum ?? So much problems when trying to import data
8163
        // 8. Student publication - Works
8164
        $sql = "SELECT ref FROM $TBL_ITEM_PROPERTY
8165
                WHERE tool = 'work' AND insert_user_id = $user_id AND c_id = $course_id";
8166
        if ($debug) {
8167
            echo $sql;
8168
        }
8169
        $res = Database::query($sql);
8170
        while ($row = Database::fetch_array($res, 'ASSOC')) {
8171
            $id = $row['ref'];
8172
            $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION
8173
                    WHERE id = $id AND session_id = $origin_session_id AND c_id = $course_id";
8174
            if ($debug) {
8175
                var_dump($sql);
8176
            }
8177
            $sub_res = Database::query($sql);
8178
            if (Database::num_rows($sub_res) > 0) {
8179
                $data = Database::fetch_array($sub_res, 'ASSOC');
8180
                if ($debug) {
8181
                    var_dump($data);
8182
                }
8183
                $parent_id = $data['parent_id'];
8184
                if (isset($data['parent_id']) && !empty($data['parent_id'])) {
8185
                    $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION
8186
                            WHERE id = $parent_id AND c_id = $course_id";
8187
                    $select_res = Database::query($sql);
8188
                    $parent_data = Database::fetch_array($select_res, 'ASSOC');
8189
                    if ($debug) {
8190
                        var_dump($parent_data);
8191
                    }
8192
8193
                    $sys_course_path = api_get_path(SYS_COURSE_PATH);
8194
                    $course_dir = $sys_course_path.$course_info['path'];
8195
                    $base_work_dir = $course_dir.'/work';
8196
8197
                    // Creating the parent folder in the session if does not exists already
8198
                    // @todo ugly fix
8199
                    $search_this = "folder_moved_from_session_id_$origin_session_id";
8200
                    $search_this2 = $parent_data['url'];
8201
                    $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION
8202
                            WHERE description like '%$search_this%' AND
8203
                                  url LIKE '%$search_this2%' AND
8204
                                  session_id = $new_session_id AND
8205
                                  c_id = $course_id
8206
                            ORDER BY id desc  LIMIT 1";
8207
                    if ($debug) {
8208
                        echo $sql;
8209
                    }
8210
                    $sub_res = Database::query($sql);
8211
                    $num_rows = Database::num_rows($sub_res);
8212
                    $new_parent_id = 0;
8213
                    if ($num_rows > 0) {
8214
                        $new_result = Database::fetch_array($sub_res, 'ASSOC');
8215
                        $created_dir = $new_result['url'];
8216
                        $new_parent_id = $new_result['id'];
8217
                    } else {
8218
                        if ($update_database) {
8219
                            $dir_name = substr($parent_data['url'], 1);
8220
                            $created_dir = create_unexisting_work_directory($base_work_dir, $dir_name);
8221
                            $created_dir = '/'.$created_dir;
8222
                            $now = new DateTime(api_get_utc_datetime(), new DateTimeZone('UTC'));
8223
                            // Creating directory
8224
                            $publication = new \Chamilo\CourseBundle\Entity\CStudentPublication();
8225
                            $publication
8226
                                ->setUrl($created_dir)
8227
                                ->setCId($course_id)
8228
                                ->setTitle($parent_data['title'])
8229
                                ->setDescription(
8230
                                    $parent_data['description']."folder_moved_from_session_id_$origin_session_id"
8231
                                )
8232
                                ->setActive(false)
8233
                                ->setAccepted(true)
8234
                                ->setPostGroupId(0)
8235
                                ->setHasProperties($parent_data['has_properties'])
8236
                                ->setWeight($parent_data['weight'])
8237
                                ->setContainsFile($parent_data['contains_file'])
8238
                                ->setFiletype('folder')
8239
                                ->setSentDate($now)
8240
                                ->setQualification($parent_data['qualification'])
8241
                                ->setParentId(0)
8242
                                ->setQualificatorId(0)
8243
                                ->setUserId($parent_data['user_id'])
8244
                                ->setAllowTextAssignment($parent_data['allow_text_assignment'])
8245
                                ->setSession($session);
8246
8247
                            $publication->setDocumentId($parent_data['document_id']);
8248
8249
                            Database::getManager()->persist($publication);
8250
                            Database::getManager()->flush();
8251
                            $id = $publication->getIid();
8252
                            // Folder created
8253
                            api_item_property_update(
8254
                                $course_info,
8255
                                'work',
8256
                                $id,
8257
                                'DirectoryCreated',
8258
                                api_get_user_id(),
8259
                                null,
8260
                                null,
8261
                                null,
8262
                                null,
8263
                                $new_session_id
8264
                            );
8265
                            $new_parent_id = $id;
8266
                            if (!isset($result_message[$TBL_STUDENT_PUBLICATION.' - new folder created called: '.$created_dir])) {
8267
                                $result_message[$TBL_STUDENT_PUBLICATION.' - new folder created called: '.$created_dir] = 0;
8268
                            }
8269
                            $result_message[$TBL_STUDENT_PUBLICATION.' - new folder created called: '.$created_dir]++;
8270
                        }
8271
                    }
8272
8273
                    // Creating student_publication_assignment if exists
8274
                    $sql = "SELECT * FROM $TBL_STUDENT_PUBLICATION_ASSIGNMENT
8275
                            WHERE publication_id = $parent_id AND c_id = $course_id";
8276
                    if ($debug) {
8277
                        var_dump($sql);
8278
                    }
8279
                    $rest_select = Database::query($sql);
8280
                    if (Database::num_rows($rest_select) > 0) {
8281
                        if ($update_database && $new_parent_id) {
8282
                            $assignment_data = Database::fetch_array($rest_select, 'ASSOC');
8283
                            $sql_add_publication = "INSERT INTO ".$TBL_STUDENT_PUBLICATION_ASSIGNMENT." SET
8284
                                c_id = '$course_id',
8285
                                expires_on = '".$assignment_data['expires_on']."',
8286
                                ends_on = '".$assignment_data['ends_on']."',
8287
                                add_to_calendar = '".$assignment_data['add_to_calendar']."',
8288
                                enable_qualification = '".$assignment_data['enable_qualification']."',
8289
                                publication_id = '".$new_parent_id."'";
8290
                            if ($debug) {
8291
                                echo $sql_add_publication;
8292
                            }
8293
                            Database::query($sql_add_publication);
8294
                            $id = (int) Database::insert_id();
8295
                            if ($id) {
8296
                                $sql_update = "UPDATE $TBL_STUDENT_PUBLICATION
8297
                                    SET  has_properties = '".$id."',
8298
                                        view_properties = '1'
8299
                                    WHERE id = ".$new_parent_id;
8300
                                if ($debug) {
8301
                                    echo $sql_update;
8302
                                }
8303
                                Database::query($sql_update);
8304
                                if (!isset($result_message[$TBL_STUDENT_PUBLICATION_ASSIGNMENT])) {
8305
                                    $result_message[$TBL_STUDENT_PUBLICATION_ASSIGNMENT] = 0;
8306
                                }
8307
                                $result_message[$TBL_STUDENT_PUBLICATION_ASSIGNMENT]++;
8308
                            }
8309
                        }
8310
                    }
8311
8312
                    $doc_url = $data['url'];
8313
                    $new_url = str_replace($parent_data['url'], $created_dir, $doc_url);
8314
8315
                    if ($update_database) {
8316
                        // Creating a new work
8317
                        $data['sent_date'] = new DateTime($data['sent_date'], new DateTimeZone('UTC'));
8318
                        $data['post_group_id'] = (int) $data['post_group_id'];
8319
                        $publication = new \Chamilo\CourseBundle\Entity\CStudentPublication();
8320
                        $publication
8321
                            ->setUrl($new_url)
8322
                            ->setCId($course_id)
8323
                            ->setTitle($data['title'])
8324
                            ->setDescription($data['description'].' file moved')
8325
                            ->setActive($data['active'])
8326
                            ->setAccepted($data['accepted'])
8327
                            ->setPostGroupId($data['post_group_id'])
8328
                            ->setSentDate($data['sent_date'])
8329
                            ->setParentId($new_parent_id)
8330
                            ->setWeight($data['weight'])
8331
                            ->setHasProperties(0)
8332
                            ->setWeight($data['weight'])
8333
                            ->setContainsFile($data['contains_file'])
8334
                            ->setSession($session)
8335
                            ->setUserId($data['user_id'])
8336
                            ->setFiletype('file')
8337
                            ->setDocumentId(0)
8338
                        ;
8339
8340
                        $em->persist($publication);
8341
                        $em->flush();
8342
8343
                        $id = $publication->getIid();
8344
                        api_item_property_update(
8345
                            $course_info,
8346
                            'work',
8347
                            $id,
8348
                            'DocumentAdded',
8349
                            $user_id,
8350
                            null,
8351
                            null,
8352
                            null,
8353
                            null,
8354
                            $new_session_id
8355
                        );
8356
                        if (!isset($result_message[$TBL_STUDENT_PUBLICATION])) {
8357
                            $result_message[$TBL_STUDENT_PUBLICATION] = 0;
8358
                        }
8359
                        $result_message[$TBL_STUDENT_PUBLICATION]++;
8360
                        $full_file_name = $course_dir.'/'.$doc_url;
8361
                        $new_file = $course_dir.'/'.$new_url;
8362
8363
                        if (file_exists($full_file_name)) {
8364
                            // deleting old assignment
8365
                            $result = copy($full_file_name, $new_file);
8366
                            if ($result) {
8367
                                unlink($full_file_name);
8368
                                if (isset($data['id'])) {
8369
                                    $sql = "DELETE FROM $TBL_STUDENT_PUBLICATION WHERE id= ".$data['id'];
8370
                                    if ($debug) {
8371
                                        var_dump($sql);
8372
                                    }
8373
                                    Database::query($sql);
8374
                                }
8375
                                api_item_property_update(
8376
                                    $course_info,
8377
                                    'work',
8378
                                    $data['id'],
8379
                                    'DocumentDeleted',
8380
                                    api_get_user_id()
8381
                                );
8382
                            }
8383
                        }
8384
                    }
8385
                }
8386
            }
8387
        }
8388
8389
        // 9. Survey   Pending
8390
        // 10. Dropbox - not neccesary to move categories (no presence of session_id)
8391
        $sql = "SELECT id FROM $TBL_DROPBOX_FILE
8392
                WHERE uploader_id = $user_id AND session_id = $origin_session_id AND c_id = $course_id";
8393
        if ($debug) {
8394
            var_dump($sql);
8395
        }
8396
        $res = Database::query($sql);
8397
        while ($row = Database::fetch_array($res, 'ASSOC')) {
8398
            $id = (int) $row['id'];
8399
            if ($update_database) {
8400
                $sql = "UPDATE $TBL_DROPBOX_FILE SET session_id = $new_session_id WHERE c_id = $course_id AND id = $id";
8401
                if ($debug) {
8402
                    var_dump($sql);
8403
                }
8404
                Database::query($sql);
8405
                if ($debug) {
8406
                    var_dump($res);
8407
                }
8408
8409
                $sql = "UPDATE $TBL_DROPBOX_POST SET session_id = $new_session_id WHERE file_id = $id";
8410
                if ($debug) {
8411
                    var_dump($sql);
8412
                }
8413
                Database::query($sql);
8414
                if ($debug) {
8415
                    var_dump($res);
8416
                }
8417
                if (!isset($result_message[$TBL_DROPBOX_FILE])) {
8418
                    $result_message[$TBL_DROPBOX_FILE] = 0;
8419
                }
8420
                $result_message[$TBL_DROPBOX_FILE]++;
8421
            }
8422
        }
8423
8424
        // 11. Notebook
8425
        /*$sql = "SELECT notebook_id FROM $TBL_NOTEBOOK
8426
                WHERE
8427
                    user_id = $user_id AND
8428
                    session_id = $origin_session_id AND
8429
                    course = '$origin_course_code' AND
8430
                    c_id = $course_id";
8431
        if ($debug) {
8432
            var_dump($sql);
8433
        }
8434
        $res = Database::query($sql);
8435
        while ($row = Database::fetch_array($res, 'ASSOC')) {
8436
            $id = $row['notebook_id'];
8437
            if ($update_database) {
8438
                $sql = "UPDATE $TBL_NOTEBOOK
8439
                        SET session_id = $new_session_id
8440
                        WHERE c_id = $course_id AND notebook_id = $id";
8441
                if ($debug) {
8442
                    var_dump($sql);
8443
                }
8444
                $res = Database::query($sql);
8445
                if ($debug) {
8446
                    var_dump($res);
8447
                }
8448
            }
8449
        }*/
8450
8451
        if ($update_database) {
8452
            echo Display::return_message(get_lang('StatsMoved'));
8453
            if (is_array($result_message)) {
8454
                foreach ($result_message as $table => $times) {
8455
                    echo 'Table '.$table.' - '.$times.' records updated <br />';
8456
                }
8457
            }
8458
        } else {
8459
            echo '<p class="lead">'.get_lang('UserInformationOfThisCourse').'</p>';
8460
            echo '<table class="table" width="100%">';
8461
            echo '<tr>';
8462
            echo '<th width="50%" valign="top">';
8463
            if ($origin_session_id == 0) {
8464
                echo '<p><strong>'.get_lang('OriginCourse').'</strong></p>';
8465
            } else {
8466
                echo '<p><strong>'.get_lang('OriginSession').' #'.$origin_session_id.'</strong></p>';
8467
            }
8468
            echo '</th>';
8469
            echo '<th width="50%" valign="top">';
8470
            if ($new_session_id == 0) {
8471
                echo '<p><strong>'.get_lang('DestinyCourse').'</strong></p>';
8472
            } else {
8473
                echo '<p><strong>'.get_lang('DestinySession').' #'.$new_session_id.'</strong></p>';
8474
            }
8475
            echo '</th>';
8476
            echo '</tr>';
8477
            echo '<tr>';
8478
            echo '<td>';
8479
            self::compareUserData($result_message);
8480
            echo '</td>';
8481
            echo '<td>';
8482
            self::compareUserData($result_message_compare);
8483
            echo '</td>';
8484
            echo '</tr>';
8485
            echo '</table>';
8486
        }
8487
    }
8488
8489
    public static function compareUserData($result_message)
8490
    {
8491
        foreach ($result_message as $table => $data) {
8492
            $title = $table;
8493
            if ($table === 'TRACK_E_EXERCISES') {
8494
                $title = get_lang('Exercises');
8495
            } elseif ($table === 'TRACK_E_EXERCISES_IN_LP') {
8496
                $title = get_lang('ExercisesInLp');
8497
            } elseif ($table === 'LP_VIEW') {
8498
                $title = get_lang('LearningPaths');
8499
            }
8500
            echo '<h3 class="page-header">'.get_lang($title).' </h3>';
8501
8502
            if (is_array($data)) {
8503
                echo '<ul>';
8504
                foreach ($data as $id => $item) {
8505
                    echo '<li>';
8506
                    if ($table === 'TRACK_E_EXERCISES' || $table === 'TRACK_E_EXERCISES_IN_LP') {
8507
                        echo '<p class="lead">';
8508
                        echo get_lang('Exercise').' #'.$item['exe_exo_id'];
8509
                        if (!empty($item['orig_lp_id'])) {
8510
                            echo PHP_EOL.'<small>(';
8511
                            echo get_lang('LearningPath').' #'.$item['orig_lp_id'];
8512
                            echo ')</small>';
8513
                        }
8514
                        echo '</p>';
8515
                        echo "<p><strong>".get_lang('Attempt')." #$id</strong></p>";
8516
                        // Process data.
8517
                        $array = [
8518
                            'exe_date' => get_lang('Date'),
8519
                            'exe_result' => get_lang('Score'),
8520
                            'exe_weighting' => get_lang('Weighting'),
8521
                        ];
8522
                        foreach ($item as $key => $value) {
8523
                            if (in_array($key, array_keys($array))) {
8524
                                $key = $array[$key];
8525
                                echo "<p>$key =  $value </p>";
8526
                            }
8527
                        }
8528
                    } else {
8529
                        echo '<p class="lead">'.get_lang('Id')." #$id</p>";
8530
                        // process data
8531
                        foreach ($item as $key => $value) {
8532
                            echo "<p>$key = $value</p>";
8533
                        }
8534
                    }
8535
                    echo '</li>';
8536
                }
8537
                echo '</ul>';
8538
            } else {
8539
                echo '<p>'.get_lang('NoResults').'</p>';
8540
            }
8541
        }
8542
    }
8543
8544
    public static function updateUserLastLogin($userId)
8545
    {
8546
        $tblTrackLogin = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
8547
        $sql = "SELECT login_id, login_date
8548
            FROM $tblTrackLogin
8549
            WHERE
8550
                login_user_id='".$userId."'
8551
            ORDER BY login_date DESC
8552
            LIMIT 0,1";
8553
8554
        $qLastConnection = Database::query($sql);
8555
        if (Database::num_rows($qLastConnection) > 0) {
8556
            $now = api_get_utc_datetime();
8557
            $iIdLastConnection = Database::result($qLastConnection, 0, 'login_id');
8558
8559
            // is the latest logout_date still relevant?
8560
            $sql = "SELECT logout_date FROM $tblTrackLogin
8561
                WHERE login_id = $iIdLastConnection";
8562
            $qLogoutDate = Database::query($sql);
8563
            $resLogoutDate = convert_sql_date(Database::result($qLogoutDate, 0, 'logout_date'));
8564
            $lifeTime = api_get_configuration_value('session_lifetime');
8565
8566
            if ($resLogoutDate < time() - $lifeTime) {
8567
                // it isn't, we should create a fresh entry
8568
                Event::eventLogin($userId);
8569
            // now that it's created, we can get its ID and carry on
8570
            } else {
8571
                $sql = "UPDATE $tblTrackLogin SET logout_date = '$now'
8572
                    WHERE login_id = '$iIdLastConnection'";
8573
                Database::query($sql);
8574
            }
8575
8576
            $tableUser = Database::get_main_table(TABLE_MAIN_USER);
8577
            $sql = "UPDATE $tableUser SET last_login = '$now'
8578
                WHERE user_id = ".$userId;
8579
            Database::query($sql);
8580
        }
8581
    }
8582
8583
    /**
8584
     * Get results of user in exercises by dates.
8585
     *
8586
     * @return array
8587
     */
8588
    public static function getUserTrackExerciseByDates(
8589
        int $userId,
8590
        int $courseId,
8591
        string $startDate,
8592
        string $endDate
8593
    ) {
8594
        $startDate = Database::escape_string($startDate);
8595
        $endDate = Database::escape_string($endDate);
8596
8597
        $tblTrackExercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
8598
        $tblQuiz = Database::get_course_table(TABLE_QUIZ_TEST);
8599
        $sql = "SELECT
8600
                    te.exe_exo_id,
8601
                    q.title,
8602
                    MAX((te.exe_result/te.exe_weighting) * 100) as score
8603
                FROM
8604
                    $tblTrackExercises te
8605
                INNER JOIN
8606
                    $tblQuiz q ON (q.iid = te.exe_exo_id AND q.c_id = te.c_id)
8607
                WHERE
8608
                    te.exe_user_id = $userId AND
8609
                    te.c_id = $courseId AND
8610
                    te.status = '' AND
8611
                    te.start_date BETWEEN '$startDate 00:00:00' AND '$endDate 23:59:59'
8612
                GROUP BY
8613
                    te.exe_exo_id,
8614
                    q.title
8615
                ";
8616
        $rs = Database::query($sql);
8617
        $result = [];
8618
        if (Database::num_rows($rs) > 0) {
8619
            while ($row = Database::fetch_array($rs)) {
8620
                $result[] = $row;
8621
            }
8622
        }
8623
8624
        return $result;
8625
    }
8626
8627
    private static function generateQuizzesTable(array $courseInfo, int $sessionId = 0): string
8628
    {
8629
        if (empty($sessionId)) {
8630
            $userList = CourseManager::get_user_list_from_course_code(
8631
                $courseInfo['code'],
8632
                $sessionId,
8633
                null,
8634
                null,
8635
                STUDENT
8636
            );
8637
        } else {
8638
            $userList = CourseManager::get_user_list_from_course_code($courseInfo['code'], $sessionId, null, null, 0);
8639
        }
8640
8641
        $active = 3;
8642
        if (true === api_get_configuration_value('tracking_my_progress_show_deleted_exercises')) {
8643
            $active = 2;
8644
        }
8645
8646
        $exerciseList = ExerciseLib::get_all_exercises($courseInfo, $sessionId, false, null, false, $active);
8647
8648
        if (empty($exerciseList)) {
8649
            return Display::return_message(get_lang('NoEx'));
8650
        }
8651
8652
        $toGraphExerciseResult = [];
8653
8654
        $quizzesTable = new SortableTableFromArray([], 0, 0, 'quizzes');
8655
        $quizzesTable->setHeaders(
8656
            [
8657
                get_lang('Title'),
8658
                get_lang('InLp'),
8659
                get_lang('Attempts'),
8660
                get_lang('BestAttempt'),
8661
                get_lang('Ranking'),
8662
                get_lang('BestResultInCourse'),
8663
                get_lang('Statistics').Display::return_icon('info3.gif', get_lang('OnlyBestResultsPerStudent')),
8664
            ]
8665
        );
8666
8667
        $webCodePath = api_get_path(WEB_CODE_PATH);
8668
8669
        foreach ($exerciseList as $exercices) {
8670
            $objExercise = new Exercise($courseInfo['real_id']);
8671
            $objExercise->read($exercices['iid']);
8672
            $visibleReturn = $objExercise->is_visible();
8673
8674
            // Getting count of attempts by user
8675
            $attempts = Event::count_exercise_attempts_by_user(
8676
                api_get_user_id(),
8677
                $exercices['iid'],
8678
                $courseInfo['real_id'],
8679
                $sessionId,
8680
                false
8681
            );
8682
8683
            $url = $webCodePath.'exercise/overview.php?'
8684
                .http_build_query(
8685
                    ['cidReq' => $courseInfo['code'], 'id_session' => $sessionId, 'exerciseId' => $exercices['iid']]
8686
                );
8687
8688
            if ($visibleReturn['value'] == true) {
8689
                $exercices['title'] = Display::url(
8690
                    $exercices['title'],
8691
                    $url,
8692
                    ['target' => SESSION_LINK_TARGET]
8693
                );
8694
            } elseif ($exercices['active'] == -1) {
8695
                $exercices['title'] = sprintf(get_lang('XParenthesisDeleted'), $exercices['title']);
8696
            }
8697
8698
            $lpList = Exercise::getLpListFromExercise($exercices['iid'], $courseInfo['real_id']);
8699
            $inLp = !empty($lpList) ? get_lang('Yes') : get_lang('No');
8700
8701
            $quizData = [
8702
                $exercices['title'],
8703
                $inLp,
8704
                $attempts,
8705
                '-',
8706
                '-',
8707
                '-',
8708
                '-',
8709
            ];
8710
8711
            // Exercise configuration show results or show only score
8712
            if (!in_array($exercices['results_disabled'], [0, 2])
8713
                || empty($attempts)
8714
            ) {
8715
                $quizzesTable->addRow($quizData);
8716
8717
                continue;
8718
            }
8719
8720
            // For graphics
8721
            $bestExerciseAttempts = Event::get_best_exercise_results_by_user(
8722
                $exercices['iid'],
8723
                $courseInfo['real_id'],
8724
                $sessionId,
8725
                0,
8726
                false
8727
            );
8728
8729
            $toGraphExerciseResult[$exercices['iid']] = [
8730
                'title' => $exercices['title'],
8731
                'data' => $bestExerciseAttempts,
8732
            ];
8733
8734
            // Getting best results
8735
            $bestScoreData = ExerciseLib::get_best_attempt_in_course(
8736
                $exercices['iid'],
8737
                $courseInfo['real_id'],
8738
                $sessionId,
8739
                false
8740
            );
8741
8742
            if (!empty($bestScoreData)) {
8743
                $quizData[6] = ExerciseLib::show_score(
8744
                    $bestScoreData['exe_result'],
8745
                    $bestScoreData['exe_weighting']
8746
                );
8747
            }
8748
8749
            $exerciseAttempt = ExerciseLib::get_best_attempt_by_user(
8750
                api_get_user_id(),
8751
                $exercices['iid'],
8752
                $courseInfo['real_id'],
8753
                $sessionId,
8754
                false
8755
            );
8756
8757
            if (!empty($exerciseAttempt)) {
8758
                // Always getting the BEST attempt
8759
                $score = $exerciseAttempt['exe_result'];
8760
                $weighting = $exerciseAttempt['exe_weighting'];
8761
                $exeId = $exerciseAttempt['exe_id'];
8762
8763
                $latestAttemptUrl = $webCodePath.'exercise/result.php?'
8764
                    .http_build_query(
8765
                        [
8766
                            'id' => $exeId,
8767
                            'cidReq' => $courseInfo['code'],
8768
                            'show_headers' => 1,
8769
                            'id_session' => $sessionId,
8770
                        ]
8771
                    );
8772
8773
                $quizData[4] = Display::url(
8774
                    ExerciseLib::show_score($score, $weighting),
8775
                    $latestAttemptUrl
8776
                );
8777
8778
                $myScore = !empty($weighting) && intval($weighting) != 0 ? $score / $weighting : 0;
8779
8780
                // @todo this function slows the page
8781
                if (is_int($userList)) {
8782
                    $userList = [$userList];
8783
                }
8784
8785
                $quizData[5] = ExerciseLib::get_exercise_result_ranking(
8786
                    $myScore,
8787
                    $exeId,
8788
                    $exercices['iid'],
8789
                    $courseInfo['code'],
8790
                    $sessionId,
8791
                    $userList,
8792
                    true,
8793
                    false
8794
                );
8795
                $graph = self::generate_exercise_result_thumbnail_graph($toGraphExerciseResult[$exercices['iid']]);
8796
                $normalGraph = self::generate_exercise_result_graph($toGraphExerciseResult[$exercices['iid']]);
8797
8798
                $quizData[7] = Display::url(
8799
                    Display::img($graph, '', [], false),
8800
                    $normalGraph,
8801
                    ['id' => $exercices['iid'], 'class' => 'expand-image']
8802
                );
8803
            }
8804
8805
            $quizzesTable->addRow($quizData);
8806
        }
8807
8808
        return Display::page_subheader2(get_lang('Exercises'))
8809
            .Display::div(
8810
                $quizzesTable->toHtml(),
8811
                ['class' => 'table-responsive']
8812
            );
8813
    }
8814
8815
    private static function generateLearningPathsTable(
8816
        User $user,
8817
        array $courseInfo,
8818
        int $sessionId = 0,
8819
        bool $isAllowedToEdit = true
8820
    ): string {
8821
        $html = [];
8822
8823
        $columnHeaders = [
8824
            'lp' => get_lang('LearningPath'),
8825
            'time' => get_lang('LatencyTimeSpent'),
8826
            'progress' => get_lang('Progress'),
8827
            'score' => get_lang('Score'),
8828
            'best_score' => get_lang('BestScore'),
8829
            'last_connection' => get_lang('LastConnexion'),
8830
        ];
8831
8832
        $trackingColumns = api_get_configuration_value('tracking_columns');
8833
8834
        if (isset($trackingColumns['my_progress_lp'])) {
8835
            $columnHeaders = array_filter(
8836
                $columnHeaders,
8837
                function ($columHeader, $key) use ($trackingColumns) {
8838
                    if (!isset($trackingColumns['my_progress_lp'][$key])
8839
                        || $trackingColumns['my_progress_lp'][$key] == false
8840
                    ) {
8841
                        return false;
8842
                    }
8843
8844
                    return true;
8845
                },
8846
                ARRAY_FILTER_USE_BOTH
8847
            );
8848
        }
8849
8850
        if (true === api_get_configuration_value('student_follow_page_add_LP_subscription_info')) {
8851
            $columnHeaders['student_follow_page_add_LP_subscription_info'] = get_lang('Unlock');
8852
        }
8853
8854
        if (true === api_get_configuration_value('student_follow_page_add_LP_acquisition_info')) {
8855
            $columnHeaders['student_follow_page_add_LP_acquisition_info'] = get_lang('Acquisition');
8856
        }
8857
8858
        $addLpInvisibleCheckbox = api_get_configuration_value('student_follow_page_add_LP_invisible_checkbox');
8859
        $includeNotsubscribedLp = api_get_configuration_value('student_follow_page_include_not_subscribed_lp_students');
8860
8861
        $columnHeadersKeys = array_keys($columnHeaders);
8862
8863
        $categories = learnpath::getCategories($courseInfo['real_id'], true);
8864
        $countCategories = count($categories);
8865
8866
        $webCodePath = api_get_path(WEB_CODE_PATH);
8867
8868
        /** @var CLpCategory $category */
8869
        foreach ($categories as $category) {
8870
            // LP table results
8871
            $objLearnpathList = new LearnpathList(
8872
                $user->getId(),
8873
                $courseInfo,
8874
                $sessionId,
8875
                'lp.publicatedOn ASC',
8876
                true,
8877
                $category->getId(),
8878
                false,
8879
                false,
8880
                $includeNotsubscribedLp === false
8881
            );
8882
            $lpList = $objLearnpathList->get_flat_list();
8883
8884
            $learningpathsTable = new SortableTableFromArray([], 0, 0, 'learningpaths');
8885
            $learningpathsTable->setHeaders($columnHeaders);
8886
8887
            foreach ($lpList as $lpId => $learnpath) {
8888
                $learningpathData = [];
8889
8890
                if (!$learnpath['lp_visibility']) {
8891
                    continue;
8892
                }
8893
8894
                if ($addLpInvisibleCheckbox) {
8895
                    if (!StudentFollowPage::isViewVisible($lpId, $user->getId(), $courseInfo['real_id'], $sessionId)) {
8896
                        continue;
8897
                    }
8898
                }
8899
8900
                $url = $webCodePath.'lp/lp_controller.php?'
8901
                    .http_build_query(
8902
                        ['cidReq' => $courseInfo['code'], 'id_session' => $sessionId, 'lp_id' => $lpId, 'action' => 'view']
8903
                    );
8904
8905
                if (in_array('lp', $columnHeadersKeys)) {
8906
                    if ($learnpath['lp_visibility'] == 0) {
8907
                        $learningpathData[] = $learnpath['lp_name'];
8908
                    } else {
8909
                        $learningpathData[] = Display::url(
8910
                            $learnpath['lp_name'],
8911
                            $url,
8912
                            ['target' => SESSION_LINK_TARGET]
8913
                        );
8914
                    }
8915
                }
8916
8917
                if (in_array('time', $columnHeadersKeys)) {
8918
                    $time_spent_in_lp = self::get_time_spent_in_lp(
8919
                        $user->getId(),
8920
                        $courseInfo['code'],
8921
                        [$lpId],
8922
                        $sessionId
8923
                    );
8924
8925
                    $learningpathData[] = api_time_to_hms($time_spent_in_lp);
8926
                }
8927
8928
                if (in_array('progress', $columnHeadersKeys)) {
8929
                    $progress = self::get_avg_student_progress(
8930
                        $user->getId(),
8931
                        $courseInfo['code'],
8932
                        [$lpId],
8933
                        $sessionId
8934
                    );
8935
8936
                    if (is_numeric($progress)) {
8937
                        $progress = sprintf(get_lang('XPercent'), $progress);
8938
                    }
8939
8940
                    $learningpathData[] = $progress;
8941
                }
8942
8943
                if (in_array('score', $columnHeadersKeys)) {
8944
                    $percentage_score = self::get_avg_student_score(
8945
                        $user->getId(),
8946
                        $courseInfo['code'],
8947
                        [$lpId],
8948
                        $sessionId
8949
                    );
8950
8951
                    if (is_numeric($percentage_score)) {
8952
                        $percentage_score = sprintf(get_lang('XPercent'), $percentage_score);
8953
                    } else {
8954
                        $percentage_score = sprintf(get_lang('XPercent'), 0);
8955
                    }
8956
8957
                    $learningpathData[] = $percentage_score;
8958
                }
8959
8960
                if (in_array('best_score', $columnHeadersKeys)) {
8961
                    $bestScore = self::get_avg_student_score(
8962
                        $user->getId(),
8963
                        $courseInfo['code'],
8964
                        [$lpId],
8965
                        $sessionId,
8966
                        false,
8967
                        false,
8968
                        true
8969
                    );
8970
8971
                    if (is_numeric($bestScore)) {
8972
                        $bestScore = sprintf(get_lang('XPercent'), $bestScore);
8973
                    } else {
8974
                        $bestScore = '-';
8975
                    }
8976
8977
                    $learningpathData[] = $bestScore;
8978
                }
8979
8980
                if (in_array('last_connection', $columnHeadersKeys)) {
8981
                    $lastConnectionInLp = self::get_last_connection_time_in_lp(
8982
                        $user->getId(),
8983
                        $courseInfo['code'],
8984
                        $lpId,
8985
                        $sessionId
8986
                    );
8987
8988
                    $lastConnection = '-';
8989
8990
                    if (!empty($lastConnectionInLp)) {
8991
                        $lastConnection = api_convert_and_format_date($lastConnectionInLp, DATE_TIME_FORMAT_LONG);
8992
                    }
8993
8994
                    $learningpathData[] = $lastConnection;
8995
                }
8996
8997
                if (in_array('student_follow_page_add_LP_subscription_info', $columnHeadersKeys)) {
8998
                    $learningpathData[] = StudentFollowPage::getLpSubscription(
8999
                        $learnpath,
9000
                        $user->getId(),
9001
                        $courseInfo['real_id'],
9002
                        $sessionId,
9003
                        $isAllowedToEdit
9004
                    );
9005
                }
9006
9007
                if (in_array('student_follow_page_add_LP_acquisition_info', $columnHeadersKeys)) {
9008
                    $learningpathData[] = StudentFollowPage::getLpAcquisition(
9009
                        $learnpath,
9010
                        $user->getId(),
9011
                        $courseInfo['real_id'],
9012
                        $sessionId
9013
                    );
9014
                }
9015
9016
                $learningpathsTable->addRow($learningpathData);
9017
            }
9018
9019
            if ($learningpathsTable->getRowCount() < 2) {
9020
                continue;
9021
            }
9022
9023
            if ($countCategories > 1) {
9024
                $html[] = Display::tag('h5', $category->getName());
9025
            }
9026
9027
            $html[] = Display::div(
9028
                $learningpathsTable->toHtml(),
9029
                ['class' => 'table-responsive']
9030
            );
9031
        }
9032
9033
        return implode(PHP_EOL, $html);
9034
    }
9035
9036
    private static function countSubscribedCoursesPerUser()
9037
    {
9038
    }
9039
}
9040