Passed
Push — 1.11.x ( de4f26...1f4898 )
by Angel Fernando Quiroz
11:12
created

TrackingCourseLog::get_item_resources_data()   F

Complexity

Conditions 42

Size

Total Lines 252
Code Lines 173

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 42
eloc 173
nop 4
dl 0
loc 252
rs 3.3333
c 0
b 0
f 0

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