Passed
Pull Request — 1.11.x (#4625)
by Angel Fernando Quiroz
08:19
created

Tracking::getCourseLpProgress()   C

Complexity

Conditions 12
Paths 2

Size

Total Lines 67
Code Lines 44

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 12
eloc 44
c 0
b 0
f 0
nop 2
dl 0
loc 67
rs 6.9666
nc 2

How to fix   Long Method    Complexity   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

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