Tracking   F
last analyzed

Complexity

Total Complexity 1073

Size/Duplication

Total Lines 8972
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
wmc 1073
eloc 5146
dl 0
loc 8972
rs 0.8
c 0
b 0
f 0

74 Methods

Rating   Name   Duplication   Size   Complexity  
B get_group_reporting() 0 84 9
A getLpQuizContentToPdf() 0 59 5
F getLpCertificateTablesToPdf() 0 259 21
F getLpStatsContentToPdf() 0 482 63
A count_course_per_student() 0 21 2
F getLpStats() 0 1297 199
F generate_exercise_result_thumbnail_graph() 0 181 17
C get_time_spent_on_the_platform() 0 86 14
F getCalculateTime() 0 159 40
C compareUserData() 0 47 13
F get_avg_student_exercise_score() 0 136 23
A getTotalTimeSpentOnThePlatform() 0 32 5
B minimumTimeAvailable() 0 24 10
A count_number_of_forums_by_course() 0 45 5
A generate_session_exercise_graph() 0 51 1
F generate_exercise_result_graph() 0 177 17
A get_first_connection_date_on_the_course() 0 38 5
A get_course_list_in_session_from_student() 0 20 2
F get_avg_student_score() 0 372 57
A count_student_visited_links() 0 19 1
A get_exercise_student_progress() 0 37 4
B show_course_detail() 0 40 6
C get_time_spent_in_lp() 0 141 14
B get_ip_from_user_event() 0 32 7
C getCourseLpProgress() 0 67 12
B get_last_connection_time_in_lp() 0 79 10
A get_tools_most_used_by_course() 0 31 4
B count_student_messages() 0 61 6
B get_time_spent_on_the_course() 0 58 10
A getUserTrackExerciseByDates() 0 37 3
A get_links_most_visited_by_course() 0 34 4
A getLastConnectionDateByCourse() 0 23 3
A updateUserLastLogin() 0 36 3
A count_number_of_posts_by_course() 0 46 4
F get_courses_followed_by_coach() 0 90 12
F showUserProgress() 0 764 94
A get_courses_list_from_session() 0 22 2
A getFirstConnectionTimeInLp() 0 46 4
B getTotalTimeSpentInCourses() 0 37 7
A getCourseLpFinalizationDate() 0 37 3
A getCourseQuizLastFinalizationDate() 0 29 3
F get_sessions_coached_by_user() 0 138 19
A chat_last_connection() 0 31 2
A get_exercise_student_average_best_attempt() 0 26 5
F get_exercise_progress() 0 222 26
A getNumberOfCourseAccessDates() 0 14 1
D get_last_connection_date_on_the_course() 0 99 18
C getInactiveStudentsInCourse() 0 96 13
A isAllowToTrack() 0 8 5
F getStats() 0 310 30
A count_student_uploaded_documents() 0 43 4
D generateQuizzesTable() 0 185 15
A is_allowed_to_coach_student() 0 32 3
A setUserSearchForm() 0 35 2
B get_course_connections_count() 0 61 10
A count_student_downloaded_documents() 0 17 1
A get_documents_most_downloaded_by_course() 0 33 4
A getLastStudentPublication() 0 29 1
A chat_connections_during_last_x_days_by_course() 0 33 3
A count_number_of_threads_by_course() 0 55 5
B getToolInformation() 0 82 8
F generateLearningPathsTable() 0 219 26
A getLastConnectionInAnyCourse() 0 22 3
A displayUserSkills() 0 9 3
A count_student_assignments() 0 46 4
F get_avg_student_progress() 0 166 26
A count_student_exercise_attempts() 0 40 3
B get_student_followed_by_coach() 0 88 8
F processUserDataMove() 0 600 82
B getAverageStudentScore() 0 64 8
B get_last_connection_date() 0 39 6
A countSessionsPerStudent() 0 11 1
A get_first_connection_date() 0 20 3
A countSubscribedCoursesPerUser() 0 2 1

How to fix   Complexity   

Complex Class

Complex classes like Tracking often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.

Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.

While breaking up the class, it is a good idea to analyze how other classes use Tracking, and based on these observations, apply Extract Interface, too.

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Entity\Course;
6
use Chamilo\CoreBundle\Entity\Session as SessionEntity;
7
use Chamilo\CourseBundle\Entity\CLpCategory;
8
use Chamilo\UserBundle\Entity\User;
9
use ChamiloSession as Session;
10
use CpChart\Cache as pCache;
11
use CpChart\Data as pData;
12
use CpChart\Image as pImage;
13
use ExtraField as ExtraFieldModel;
14
15
/**
16
 *  Class Tracking.
17
 *
18
 *  @author  Julio Montoya <[email protected]>
19
 */
20
class Tracking
21
{
22
    /**
23
     * Get group reporting.
24
     *
25
     * @param int    $course_id
26
     * @param int    $sessionId
27
     * @param int    $group_id
28
     * @param string $type
29
     * @param int    $start
30
     * @param int    $limit
31
     * @param int    $sidx
32
     * @param string $sord
33
     * @param array  $where_condition
34
     *
35
     * @return array|null
36
     */
37
    public static function get_group_reporting(
38
        $course_id,
39
        $sessionId = 0,
40
        $group_id = 0,
41
        $type = 'all',
42
        $start = 0,
43
        $limit = 1000,
44
        $sidx = 1,
45
        $sord = 'desc',
46
        $where_condition = []
47
    ) {
48
        $course_id = (int) $course_id;
49
        $sessionId = (int) $sessionId;
50
51
        if (empty($course_id)) {
52
            return null;
53
        }
54
        $courseInfo = api_get_course_info_by_id($course_id);
55
        if ('count' == $type) {
56
            return GroupManager::get_group_list(null, $courseInfo, null, $sessionId, true);
57
        }
58
59
        $groupList = GroupManager::get_group_list(null, $courseInfo, null, $sessionId);
60
        $parsedResult = [];
61
        if (!empty($groupList)) {
62
            foreach ($groupList as $group) {
63
                $users = GroupManager::get_users($group['id'], true, null, null, false, $courseInfo['real_id']);
64
                $time = 0;
65
                $avg_student_score = 0;
66
                $avg_student_progress = 0;
67
                $work = 0;
68
                $messages = 0;
69
70
                foreach ($users as $user_data) {
71
                    $time += self::get_time_spent_on_the_course(
72
                        $user_data['user_id'],
73
                        $courseInfo['real_id'],
74
                        $sessionId
75
                    );
76
                    $average = self::get_avg_student_score(
77
                        $user_data['user_id'],
78
                        $courseInfo['code'],
79
                        [],
80
                        $sessionId
81
                    );
82
                    if (is_numeric($average)) {
83
                        $avg_student_score += $average;
84
                    }
85
                    $avg_student_progress += self::get_avg_student_progress(
86
                        $user_data['user_id'],
87
                        $courseInfo['code'],
88
                        [],
89
                        $sessionId
90
                    );
91
                    $work += self::count_student_assignments(
92
                        $user_data['user_id'],
93
                        $courseInfo['code'],
94
                        $sessionId
95
                    );
96
                    $messages += self::count_student_messages(
97
                        $user_data['user_id'],
98
                        $courseInfo['code'],
99
                        $sessionId
100
                    );
101
                }
102
103
                $countUsers = count($users);
104
                $averageProgress = empty($countUsers) ? 0 : round($avg_student_progress / $countUsers, 2);
105
                $averageScore = empty($countUsers) ? 0 : round($avg_student_score / $countUsers, 2);
106
107
                $groupItem = [
108
                    'id' => $group['id'],
109
                    'name' => $group['name'],
110
                    'time' => api_time_to_hms($time),
111
                    'progress' => $averageProgress,
112
                    'score' => $averageScore,
113
                    'works' => $work,
114
                    'messages' => $messages,
115
                ];
116
                $parsedResult[] = $groupItem;
117
            }
118
        }
119
120
        return $parsedResult;
121
    }
122
123
    /**
124
     * Get the lp quizzes result as content for pdf export.
125
     *
126
     * @param $userId
127
     * @param $sessionId
128
     *
129
     * @return string
130
     */
131
    public static function getLpQuizContentToPdf(
132
        $userId,
133
        $sessionId
134
    ) {
135
        $tblLp = Database::get_course_table(TABLE_LP_MAIN);
136
        $tblLpItem = Database::get_course_table(TABLE_LP_ITEM);
137
        $tblLpItemView = Database::get_course_table(TABLE_LP_ITEM_VIEW);
138
        $tblLpView = Database::get_course_table(TABLE_LP_VIEW);
139
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
140
        $tblCourse = Database::get_main_table(TABLE_MAIN_COURSE);
141
142
        $courses = Tracking::get_courses_list_from_session($sessionId);
143
144
        // It creates lp quiz table html
145
        $lpQuizTable = '';
146
        if (!empty($courses)) {
147
            $lpQuizTable .= '<table class="data_table">';
148
            $lpQuizTable .= '<tbody>';
149
            $lpTitle = '';
150
            foreach ($courses as $course) {
151
                $courseId = $course['c_id'];
152
                $sql = 'SELECT DISTINCT c_lp_item.title AS quiz_name,
153
				c_lp.name AS lp_name,
154
				CASE WHEN c_lp_item_view.max_score>0 THEN CONCAT(ROUND((SUM(c_lp_item_view.score)/c_lp_item_view.max_score)*100),\'%\') ELSE 0 END AS score,
155
				SUM(c_lp_view.view_count) AS attempts
156
				FROM '.$tblLpView.' AS c_lp_view
157
				JOIN '.$tblUser.' AS user ON (user.user_id = c_lp_view.user_id)
158
				JOIN '.$tblCourse.' AS course ON (course.id = c_lp_view.c_id)
159
				JOIN '.$tblLp.' AS c_lp ON (c_lp.id = c_lp_view.lp_id AND c_lp.c_id = c_lp_view.c_id)
160
				JOIN '.$tblLpItem.' AS c_lp_item ON (c_lp_item.c_id = c_lp_view.c_id AND c_lp_item.lp_id = c_lp_view.lp_id)
161
				LEFT JOIN '.$tblLpItemView.' AS c_lp_item_view ON (c_lp_item_view.lp_item_id = c_lp_item.id AND c_lp_item_view.lp_view_id = c_lp_view.id AND c_lp_item_view.c_id = c_lp_view.c_id)
162
				WHERE
163
				    user.id = '.$userId.' AND
164
				    course.id = '.$courseId.' AND
165
				    c_lp_item.item_type = \'quiz\' AND
166
				    c_lp_view.session_id = '.$sessionId.'
167
				GROUP BY quiz_name, lp_name
168
				ORDER BY c_lp_item.display_order';
169
                $result = Database::query($sql);
170
                while ($row = Database::fetch_array($result)) {
171
                    if ($lpTitle != $row['lp_name']) {
172
                        $lpTitle = $row['lp_name'];
173
                        $lpQuizTable .= '<tr>
174
                            <th>'.stripslashes($lpTitle).'</th>
175
                            <th>'.get_lang('Score').'</th>
176
                            <th>'.get_lang('Attempts').'</th>
177
                        </tr>';
178
                    }
179
                    $lpQuizTable .= '<tr>
180
                        <td>'.$row['quiz_name'].'</td>
181
                        <td>'.$row['score'].'</td>
182
                        <td>'.$row['attempts'].'</td>
183
                    </tr>';
184
                }
185
            }
186
            $lpQuizTable .= '</tbody></table>';
187
        }
188
189
        return $lpQuizTable;
190
    }
191
192
    /**
193
     * Get the tables html as contents to export lp certificate pdf .
194
     *
195
     * @return array
196
     */
197
    public static function getLpCertificateTablesToPdf(
198
        $userId,
199
        $sessionId
200
    ) {
201
        $tblTrackCourseAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
202
        $tblLp = Database::get_course_table(TABLE_LP_MAIN);
203
        $tblLpItem = Database::get_course_table(TABLE_LP_ITEM);
204
        $tblLpItemView = Database::get_course_table(TABLE_LP_ITEM_VIEW);
205
        $tblLpView = Database::get_course_table(TABLE_LP_VIEW);
206
        $tblUser = Database::get_main_table(TABLE_MAIN_USER);
207
        $tblCourse = Database::get_main_table(TABLE_MAIN_COURSE);
208
209
        $courses = Tracking::get_courses_list_from_session($sessionId);
210
211
        // It creates the progress table html
212
        $progressTable = '';
213
        if (!empty($courses)) {
214
            $timeSpent = 0;
215
            $numberVisits = 0;
216
            $progress = 0;
217
            foreach ($courses as $course) {
218
                $courseId = $course['c_id'];
219
                $timeSpent += Tracking::get_time_spent_on_the_course($userId, $courseId, $sessionId);
220
                $sql = 'SELECT DISTINCT count(course_access_id) as count
221
                    FROM '.$tblTrackCourseAccess.'
222
                    WHERE
223
                        user_id = '.$userId.' AND
224
                        c_id = '.$courseId.' AND
225
                        session_id = '.$sessionId.'
226
                    ORDER BY login_course_date ASC';
227
                $result = Database::query($sql);
228
                $row = Database::fetch_array($result);
229
                $numberVisits += $row['count'];
230
                $progress += Tracking::get_avg_student_progress($userId, $course['code'], [], $sessionId);
231
            }
232
            $average = round($progress / count($courses), 1);
233
            $average = empty($average) ? '0%' : $average.'%';
234
            $first = Tracking::get_first_connection_date($userId);
235
            $last = Tracking::get_last_connection_date($userId);
236
237
            $table = new HTML_Table(['class' => 'data_table']);
238
            $column = 0;
239
            $row = 0;
240
            $headers = [
241
                get_lang('TimeSpent'),
242
                get_lang('NumberOfVisits'),
243
                get_lang('GlobalProgress'),
244
                get_lang('FirstLogin'),
245
                get_lang('LastConnexionDate'),
246
            ];
247
248
            foreach ($headers as $header) {
249
                $table->setHeaderContents($row, $column, $header);
250
                $column++;
251
            }
252
            $table->setCellContents(1, 0, api_time_to_hms($timeSpent));
253
            $table->setCellContents(1, 1, $numberVisits);
254
            $table->setCellContents(1, 2, $average);
255
            $table->setCellContents(1, 3, $first);
256
            $table->setCellContents(1, 4, $last);
257
            $progressTable = $table->toHtml();
258
        }
259
260
        // It creates the course table html
261
        $courseTable = '';
262
        if (!empty($courses)) {
263
            $totalCourseTime = 0;
264
            $totalAttendance = [0, 0];
265
            $totalScore = 0;
266
            $totalProgress = 0;
267
            $gradeBookTotal = [0, 0];
268
            $totalEvaluations = '0/0 (0%)';
269
            $totalCourses = count($courses);
270
            $scoreDisplay = ScoreDisplay::instance();
271
272
            $courseTable .= '<table class="data_table">';
273
            $courseTable .= '<thead>';
274
            $courseTable .= '<tr>
275
                    <th>'.get_lang('ToolLearnpath').'</th>
276
                    <th>'.get_lang('ConnectionTime').'</th>
277
					<th>'.get_lang('NumberOfVisits').'</th>
278
                    <th>'.get_lang('Progress').'</th>
279
                    <th>'.get_lang('FirstLogin').'</th>
280
                    <th>'.get_lang('LastConnexion').'</th>
281
                </tr>';
282
            $courseTable .= '</thead>';
283
            $courseTable .= '<tbody>';
284
            foreach ($courses as $course) {
285
                $courseId = $course['c_id'];
286
                $courseInfoItem = api_get_course_info_by_id($courseId);
287
                $courseId = $courseInfoItem['real_id'];
288
                $courseCodeItem = $courseInfoItem['code'];
289
290
                $isSubscribed = CourseManager::is_user_subscribed_in_course(
291
                    $userId,
292
                    $courseCodeItem,
293
                    true,
294
                    $sessionId
295
                );
296
297
                if ($isSubscribed) {
298
                    $timeInSeconds = Tracking::get_time_spent_on_the_course(
299
                        $userId,
300
                        $courseId,
301
                        $sessionId
302
                    );
303
                    $totalCourseTime += $timeInSeconds;
304
                    $time_spent_on_course = api_time_to_hms($timeInSeconds);
305
                    $progress = Tracking::get_avg_student_progress(
306
                        $userId,
307
                        $courseCodeItem,
308
                        [],
309
                        $sessionId
310
                    );
311
                    $totalProgress += $progress;
312
                    $bestScore = Tracking::get_avg_student_score(
313
                        $userId,
314
                        $courseCodeItem,
315
                        [],
316
                        $sessionId,
317
                        false,
318
                        false,
319
                        true
320
                    );
321
                    if (is_numeric($bestScore)) {
322
                        $totalScore += $bestScore;
323
                    }
324
                    if ($progress > 0) {
325
                        $progress = empty($progress) ? '0%' : $progress.'%';
326
                        $score = empty($bestScore) ? '0%' : $bestScore.'%';
327
                        $sql = 'SELECT
328
                                    DISTINCT count(course_access_id) as count,
329
   							        date_format(min(login_course_date),\'%d/%m/%Y\') as first,
330
							        date_format(max(logout_course_date),\'%d/%m/%Y\') as last
331
                                FROM
332
                                    '.$tblTrackCourseAccess.'
333
                                WHERE
334
                                    user_id = '.$userId.' AND
335
                                    c_id = '.$courseId.' AND
336
                                    session_id = '.$sessionId;
337
                        $result = Database::query($sql);
338
                        $row = Database::fetch_array($result);
339
                        $numberVisitsByCourse = $row['count'];
340
                        $firstByCourse = $row['first'];
341
                        $lastByCourse = $row['last'];
342
                        $courseTable .= '<tr>
343
                        <td ><a href="'.$courseInfoItem['course_public_url'].'?id_session='.$sessionId.'">'.$courseInfoItem['title'].'</a></td>
344
                        <td >'.$time_spent_on_course.'</td>
345
						<td >'.$numberVisitsByCourse.'</td>
346
                        <td >'.$progress.'</td>
347
                        <td >'.$firstByCourse.'</td>
348
                        <td >'.$lastByCourse.'</td>';
349
                        $courseTable .= '</tr>';
350
                    }
351
                }
352
            }
353
            $totalAttendanceFormatted = $scoreDisplay->display_score($totalAttendance);
354
            $totalScoreFormatted = $scoreDisplay->display_score([$totalScore / $totalCourses, 100], SCORE_AVERAGE);
355
            $totalProgressFormatted = $scoreDisplay->display_score(
356
                [$totalProgress / $totalCourses, 100],
357
                SCORE_AVERAGE
358
            );
359
            $totalEvaluations = $scoreDisplay->display_score($gradeBookTotal);
360
            $totalTimeFormatted = api_time_to_hms($totalCourseTime);
361
            $courseTable .= '<tr>
362
                <th>'.get_lang('Total').'</th>
363
                <th>'.$totalTimeFormatted.'</th>
364
				<th>'.$numberVisits.'</th>
365
                <th>'.$totalProgressFormatted.'</th>
366
                <th>'.$first.'</th>
367
                <th>'.$last.'</th>
368
            </tr>';
369
            $courseTable .= '</tbody></table>';
370
        }
371
372
        // It creates the lps table html
373
        $lpTable = '';
374
        if (!empty($courses)) {
375
            $lpTable = '<table class="data_table">';
376
            $lpTable .= '<tbody>';
377
            $lpTitle = '';
378
            foreach ($courses as $course) {
379
                $courseId = $course['c_id'];
380
                $sql = 'SELECT DISTINCT c_lp.name AS lp_name,
381
				c_lp_item.title AS chapter,
382
				SEC_TO_TIME(c_lp_item_view.total_time) AS temps,
383
				c_lp_item_view.status AS etat,
384
				SUM(c_lp_view.view_count) AS tentatives,
385
				c_lp_item_2.title AS chapter2,
386
				SEC_TO_TIME(c_lp_item_view_2.total_time) AS temps2,
387
				c_lp_item_view_2.status AS etat2
388
				FROM '.$tblLpView.' AS c_lp_view
389
				JOIN '.$tblUser.' AS user ON user.user_id = c_lp_view.user_id
390
				JOIN '.$tblCourse.' AS course ON course.id = c_lp_view.c_id
391
				JOIN '.$tblLp.' AS c_lp ON (c_lp.id = c_lp_view.lp_id AND c_lp.c_id = c_lp_view.c_id)
392
				JOIN '.$tblLpItem.' AS c_lp_item ON (c_lp_item.c_id = c_lp_view.c_id AND c_lp_item.lp_id = c_lp_view.lp_id)
393
				LEFT JOIN '.$tblLpItemView.' AS c_lp_item_view ON (c_lp_item_view.lp_item_id = c_lp_item.id AND c_lp_item_view.lp_view_id = c_lp_view.id AND c_lp_item_view.c_id = c_lp_view.c_id)
394
                LEFT JOIN '.$tblLpItem.' AS c_lp_item_2 ON (c_lp_item_2.parent_item_id > 0 AND c_lp_item_2.parent_item_id = c_lp_item.id)
395
				LEFT JOIN '.$tblLpItemView.' AS c_lp_item_view_2 ON (c_lp_item_view_2.lp_item_id = c_lp_item_2.id AND c_lp_item_view_2.lp_view_id = c_lp_view.id AND c_lp_item_view_2.c_id = c_lp_view.c_id)
396
				WHERE
397
				    user.id = '.$userId.' AND
398
				    course.id = '.$courseId.' AND
399
                    c_lp_view.session_id = '.$sessionId.' AND
400
				    c_lp_item.parent_item_id = 0
401
				GROUP BY
402
				    lp_name, chapter, chapter2
403
				ORDER BY
404
				    lp_name, c_lp_item.display_order';
405
                $result = Database::query($sql);
406
                $chapterTitle = false;
407
                while ($row = Database::fetch_array($result)) {
408
                    if ($lpTitle != $row['lp_name']) {
409
                        $lpTitle = $row['lp_name'];
410
                        $lpTable .= '<tr>
411
                        <th>'.stripslashes($lpTitle).'</th>
412
                        <th>'.get_lang('Duration').'</th>
413
                        <th>'.get_lang('Progress').'</th>
414
                    </tr>';
415
                    }
416
                    if ('' == $row['chapter2']) {
417
                        $progression = $row['etat'];
418
                        if (('completed' === $progression) || ('passed' === $progression)) {
419
                            $progression = get_lang('Done');
420
                        } else {
421
                            $progression = get_lang('Incomplete');
422
                        }
423
                        $lpTable .= '<tr>
424
                        <td>'.$row['chapter'].'</td>
425
                        <td>'.$row['temps'].'</td>
426
                        <td>'.$progression.'</td>
427
                    </tr>';
428
                        $chapterTitle = false;
429
                    } else {
430
                        if (false == $chapterTitle) {
431
                            $lpTable .= '<tr>
432
                            <td>'.$row['chapter'].'</td>
433
                            <td></td>
434
                            <td></td>
435
                        </tr>';
436
                            $chapterTitle = true;
437
                        }
438
                        $progression = $row['etat2'];
439
                        if ('completed' == $progression) {
440
                            $progression = get_lang('Done');
441
                        } else {
442
                            $progression = get_lang('Incomplete');
443
                        }
444
                        $lpTable .= '<tr>
445
                        <td>&nbsp;-&nbsp;'.$row['chapter2'].'</td>
446
                        <td>'.$row['temps2'].'</td>
447
                        <td>'.$progression.'</td>
448
                    </tr>';
449
                    }
450
                }
451
            }
452
            $lpTable .= '</tbody></table>';
453
        }
454
455
        return ['progress_table' => $progressTable, 'course_table' => $courseTable, 'lp_table' => $lpTable];
456
    }
457
458
    /**
459
     * It gets table html of Lp stats used to export in pdf.
460
     *
461
     * @return string
462
     */
463
    public static function getLpStatsContentToPdf(
464
        $userId,
465
        $courseInfo,
466
        $sessionId,
467
        $lpId,
468
        $lpName
469
    ) {
470
        $hideTime = api_get_configuration_value('hide_lp_time');
471
        $lpId = (int) $lpId;
472
        $userId = (int) $userId;
473
        $sessionId = (int) $sessionId;
474
        $courseId = $courseInfo['real_id'];
475
        $isAllowedToEdit = api_is_allowed_to_edit(null, true);
476
        $sessionCondition = api_get_session_condition($sessionId);
477
        $counter = 0;
478
        $totalTime = 0;
479
        $h = get_lang('h');
480
        $resultDisabledExtAll = true;
481
        $timeHeader = '<th>'.get_lang('ScormTime').'</th>';
482
        if ($hideTime) {
483
            $timeHeader = '';
484
        }
485
        $output = '<h2 class="clearfix text-center">'.$lpName.'</h2>';
486
        $output .= '<table class="table table-hover table-striped data_table">
487
            <thead>
488
                <tr>
489
                <th>'.get_lang('ScormLessonTitle').'</th>
490
                <th>'.get_lang('ScormStatus').'</th>
491
                <th>'.get_lang('ScormScore').'</th>
492
                '.$timeHeader.'
493
                </tr>
494
            </thead>
495
            <tbody>
496
        ';
497
498
        $tblLpItem = Database::get_course_table(TABLE_LP_ITEM);
499
        $tblLpItemView = Database::get_course_table(TABLE_LP_ITEM_VIEW);
500
        $tblLpView = Database::get_course_table(TABLE_LP_VIEW);
501
        $tblQuizQuestions = Database::get_course_table(TABLE_QUIZ_QUESTION);
502
        $tblQuiz = Database::get_course_table(TABLE_QUIZ_TEST);
503
        $tblStatsExercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
504
        $tblStatsAttempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
505
506
        // it gets the max view
507
        $sql = "SELECT max(view_count) FROM $tblLpView WHERE c_id = $courseId AND lp_id = $lpId AND user_id = $userId $sessionCondition";
508
        $res = Database::query($sql);
509
        $view = 0;
510
        $viewCondition = "";
511
        if (Database::num_rows($res) > 0) {
512
            $view = Database::result($res, 0, 0);
513
            $viewCondition = " AND v.view_count = ".(int) $view;
514
        }
515
516
        $chapterTypes = learnpath::getChapterTypes();
517
        $minimumAvailable = self::minimumTimeAvailable($sessionId, $courseId);
518
        $list = learnpath::get_flat_ordered_items_list($lpId, 0, $courseId);
519
        if (is_array($list) && count($list) > 0) {
520
            foreach ($list as $myItemId) {
521
                $sql = "SELECT
522
                    iv.status as mystatus,
523
                    v.view_count as mycount,
524
                    iv.score as myscore,
525
                    iv.total_time as mytime,
526
                    i.iid as myid,
527
                    i.lp_id as mylpid,
528
                    iv.lp_view_id as mylpviewid,
529
                    i.title as mytitle,
530
                    i.max_score as mymaxscore,
531
                    iv.max_score as myviewmaxscore,
532
                    i.item_type as item_type,
533
                    iv.view_count as iv_view_count,
534
                    iv.id as iv_id,
535
                    path
536
                FROM $tblLpItem as i
537
                INNER JOIN $tblLpItemView as iv
538
                ON (i.iid = iv.lp_item_id AND i.c_id = iv.c_id)
539
                INNER JOIN $tblLpView as v
540
                ON (iv.lp_view_id = v.id AND v.c_id = iv.c_id)
541
                WHERE
542
                    v.c_id = $courseId AND
543
                    i.iid = $myItemId AND
544
                    i.lp_id = $lpId  AND
545
                    v.user_id = $userId AND
546
                    v.session_id = $sessionId
547
                    $viewCondition
548
                ORDER BY iv.view_count";
549
                $result = Database::query($sql);
550
                $num = Database::num_rows($result);
551
                $timeForTotal = 0;
552
553
                if ($num > 0) {
554
                    // Not extended.
555
                    $row = Database::fetch_array($result, 'ASSOC');
556
                    $myId = $row['myid'];
557
                    $myLpId = $row['mylpid'];
558
                    $myLpViewId = $row['mylpviewid'];
559
                    $lpItemPath = (int) $row['path'];
560
                    $resultDisabledExtAll = false;
561
                    if ($row['item_type'] === 'quiz') {
562
                        // Check results_disabled in quiz table.
563
                        $sql = "SELECT results_disabled
564
                                FROM $tblQuiz
565
                                WHERE iid = $lpItemPath";
566
                        $resResultDisabled = Database::query($sql);
567
                        $rowResultDisabled = Database::fetch_row($resResultDisabled);
568
                        if (Database::num_rows($resResultDisabled) > 0 && 1 === (int) $rowResultDisabled[0]) {
569
                            $resultDisabledExtAll = true;
570
                        }
571
                    }
572
573
                    // Check if there are interactions below
574
                    $extendThisAttempt = 0;
575
                    $interNum = learnpath::get_interactions_count_from_db($row['iv_id'], $courseId);
576
                    $objecNum = learnpath::get_objectives_count_from_db($row['iv_id'], $courseId);
577
                    if ($interNum > 0 || $objecNum > 0) {
578
                        $extendThisAttempt = 1;
579
                    }
580
                    $lesson_status = $row['mystatus'];
581
                    $score = $row['myscore'];
582
                    $subtotalTime = $row['mytime'];
583
                    while ($tmp_row = Database::fetch_array($result)) {
584
                        $subtotalTime += $tmp_row['mytime'];
585
                    }
586
587
                    $title = $row['mytitle'];
588
                    // Selecting the exe_id from stats attempts tables in order to look the max score value.
589
                    $sql = 'SELECT * FROM '.$tblStatsExercises.'
590
                            WHERE
591
                                exe_exo_id="'.$row['path'].'" AND
592
                                exe_user_id="'.$userId.'" AND
593
                                orig_lp_id = "'.$lpId.'" AND
594
                                orig_lp_item_id = "'.$row['myid'].'" AND
595
                                c_id = '.$courseId.' AND
596
                                status <> "incomplete" AND
597
                                session_id = '.$sessionId.'
598
                             ORDER BY exe_date DESC
599
                             LIMIT 1';
600
601
                    $resultLastAttempt = Database::query($sql);
602
                    $num = Database::num_rows($resultLastAttempt);
603
                    $idLastAttempt = null;
604
                    if ($num > 0) {
605
                        while ($rowLA = Database::fetch_array($resultLastAttempt)) {
606
                            $idLastAttempt = $rowLA['exe_id'];
607
                        }
608
                    }
609
610
                    switch ($row['item_type']) {
611
                        case 'sco':
612
                            if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
613
                                $maxscore = $row['myviewmaxscore'];
614
                            } elseif ($row['myviewmaxscore'] === '') {
615
                                $maxscore = 0;
616
                            } else {
617
                                $maxscore = $row['mymaxscore'];
618
                            }
619
                            break;
620
                        case 'quiz':
621
                            // Get score and total time from last attempt of an exercise en lp.
622
                            $sql = "SELECT iid, score
623
                                    FROM $tblLpItemView
624
                                    WHERE
625
                                        c_id = $courseId AND
626
                                        lp_item_id = '".(int) $myId."' AND
627
                                        lp_view_id = '".(int) $myLpViewId."'
628
                                    ORDER BY view_count DESC
629
                                    LIMIT 1";
630
                            $resScore = Database::query($sql);
631
                            $rowScore = Database::fetch_array($resScore);
632
633
                            $sql = "SELECT SUM(total_time) as total_time
634
                                    FROM $tblLpItemView
635
                                    WHERE
636
                                        c_id = $courseId AND
637
                                        lp_item_id = '".(int) $myId."' AND
638
                                        lp_view_id = '".(int) $myLpViewId."'";
639
                            $resTime = Database::query($sql);
640
                            $rowTime = Database::fetch_array($resTime);
641
642
                            $score = 0;
643
                            $subtotalTime = 0;
644
                            if (Database::num_rows($resScore) > 0 && Database::num_rows($resTime) > 0) {
645
                                $score = (float) $rowScore['score'];
646
                                $subtotalTime = (int) $rowTime['total_time'];
647
                            }
648
                            // Selecting the max score from an attempt.
649
                            $sql = "SELECT SUM(t.ponderation) as maxscore
650
                                    FROM (
651
                                        SELECT DISTINCT
652
                                            question_id, marks, ponderation
653
                                        FROM $tblStatsAttempts as at
654
                                        INNER JOIN $tblQuizQuestions as q
655
                                        ON q.iid = at.question_id
656
                                        WHERE exe_id ='$idLastAttempt'
657
                                    ) as t";
658
659
                            $result = Database::query($sql);
660
                            $rowMaxScore = Database::fetch_array($result);
661
                            $maxscore = $rowMaxScore['maxscore'];
662
663
                            // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
664
                            $sql = 'SELECT SUM(exe_duration) exe_duration
665
                                    FROM '.$tblStatsExercises.'
666
                                    WHERE
667
                                        exe_exo_id="'.$row['path'].'" AND
668
                                        exe_user_id="'.$userId.'" AND
669
                                        orig_lp_id = "'.$lpId.'" AND
670
                                        orig_lp_item_id = "'.$row['myid'].'" AND
671
                                        c_id = '.$courseId.' AND
672
                                        status <> "incomplete" AND
673
                                        session_id = '.$sessionId.'
674
                                     ORDER BY exe_date DESC ';
675
                            $sumScoreResult = Database::query($sql);
676
                            $durationRow = Database::fetch_array($sumScoreResult, 'ASSOC');
677
                            if (!empty($durationRow['exe_duration'])) {
678
                                $exeDuration = $durationRow['exe_duration'];
679
                                if ($exeDuration != $subtotalTime && !empty($rowScore['iid']) && !empty($exeDuration)) {
680
                                    $subtotalTime = $exeDuration;
681
                                    // Update c_lp_item_view.total_time
682
                                    $sqlUpdate = "UPDATE $tblLpItemView SET total_time = '$exeDuration' WHERE iid = ".$rowScore['iid'];
683
                                    Database::query($sqlUpdate);
684
                                }
685
                            }
686
                            break;
687
                        default:
688
                            $maxscore = $row['mymaxscore'];
689
                            break;
690
                    }
691
692
                    $timeForTotal = $subtotalTime;
693
                    $time = learnpathItem::getScormTimeFromParameter('js', $subtotalTime);
694
                    if (empty($title)) {
695
                        $title = learnpath::rl_get_resource_name(
696
                            $courseInfo['code'],
697
                            $lpId,
698
                            $row['myid']
699
                        );
700
                    }
701
702
                    if (in_array($row['item_type'], $chapterTypes)) {
703
                        $title = Security::remove_XSS($title);
704
                        $output .= '<tr>
705
                                <td colspan="4"><h4>'.$title.'</h4></td>
706
                        </tr>';
707
                    } else {
708
                        if ('quiz' === $row['item_type']) {
709
                            $sql = 'SELECT * FROM '.$tblStatsExercises.'
710
                                     WHERE
711
                                        exe_exo_id="'.$row['path'].'" AND
712
                                        exe_user_id="'.$userId.'" AND
713
                                        orig_lp_id = "'.$lpId.'" AND
714
                                        orig_lp_item_id = "'.$row['myid'].'" AND
715
                                        c_id = '.$courseId.' AND
716
                                        status <> "incomplete" AND
717
                                        session_id = '.$sessionId.'
718
                                     ORDER BY exe_date DESC ';
719
                            $resultLastAttempt = Database::query($sql);
720
                            $num = Database::num_rows($resultLastAttempt);
721
                        }
722
723
                        $title = Security::remove_XSS($title);
724
                        if ($lpId == $myLpId && false) {
725
                            $output .= '<tr>
726
                                    <td>'.$title.'</td>
727
                                    <td>&nbsp;</td>
728
                                    <td>&nbsp;</td>
729
                                    <td>&nbsp;</td>
730
                            </tr>';
731
                        } else {
732
                            $output .= "<tr>";
733
                            $scoreItem = null;
734
                            if ($row['item_type'] === 'quiz') {
735
                                $scoreItem .= ExerciseLib::show_score($score, $maxscore, false);
736
                            } else {
737
                                $scoreItem .= $score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.$maxscore);
738
                            }
739
                            $timeRow = '<td style="width:20%;text-align:center;">'.$time.'</td>';
740
                            if ($hideTime) {
741
                                $timeRow = '';
742
                            }
743
                            $output .= '
744
                                <td style="width:35%;font-weight:bold;">'.$title.'</td>
745
                                <td style="width:25%;text-align:center;"><div class="btn btn-primary boldTitle">'.learnpathitem::humanize_status($lesson_status, false).'</div></td>
746
                                <td style="width:20%;text-align:center;">'.$scoreItem.'</td>
747
                                '.$timeRow.'
748
                             ';
749
                            $output .= '</tr>';
750
                        }
751
                    }
752
753
                    $counter++;
754
                    if ($extendThisAttempt) {
755
                        $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $courseId);
756
                        foreach ($list1 as $id => $interaction) {
757
                            $timeRow = '<td>'.$interaction['time'].'</td>';
758
                            if ($hideTime) {
759
                                $timeRow = '';
760
                            }
761
762
                            $output .= '<tr>
763
                                    <td>'.$interaction['order_id'].'</td>
764
                                    <td>'.$interaction['id'].'</td>
765
                                    <td>'.$interaction['type'].'</td>
766
                                    <td>'.urldecode($interaction['student_response']).'</td>
767
                                    <td>'.$interaction['result'].'</td>
768
                                    <td>'.$interaction['latency'].'</td>
769
                                    '.$timeRow.'
770
                               </tr>';
771
                            $counter++;
772
                        }
773
774
                        $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $courseId);
775
                        foreach ($list2 as $id => $interaction) {
776
                            $output .= '<tr>
777
                                    <td>'.$interaction['order_id'].'</td>
778
                                    <td>'.$interaction['objective_id'].'</td>
779
                                    <td>'.$interaction['status'].'</td>
780
                                    <td>'.$interaction['score_raw'].'</td>
781
                                    <td>'.$interaction['score_max'].'</td>
782
                                    <td>'.$interaction['score_min'].'</td>
783
                               </tr>';
784
                            $counter++;
785
                        }
786
                    }
787
788
                    // Attempts listing by exercise.
789
                    if ($lpId == $myLpId) {
790
                        // Get attempts of a exercise.
791
                        if (!empty($lpId) && 'quiz' === $row['item_type']) {
792
                            $sql = "SELECT path FROM $tblLpItem
793
                                    WHERE
794
                                        c_id = $courseId AND
795
                                        lp_id = '$lpId'";
796
                            $resPath = Database::query($sql);
797
                            if (Database::num_rows($resPath) > 0) {
798
                                while ($rowPath = Database::fetch_array($resPath)) {
799
                                    $sql = 'SELECT * FROM '.$tblStatsExercises.'
800
                                            WHERE
801
                                                exe_exo_id="'.(int) $rowPath['path'].'" AND
802
                                                status <> "incomplete" AND
803
                                                exe_user_id="'.$userId.'" AND
804
                                                orig_lp_id = "'.$lpId.'" AND
805
                                                orig_lp_item_id = "'.$myItemId.'" AND
806
                                                c_id = '.$courseId.'  AND
807
                                                session_id = '.$sessionId.'
808
                                            ORDER BY exe_date';
809
                                    $resAttempts = Database::query($sql);
810
                                    if (Database::num_rows($resAttempts) > 0) {
811
                                        $n = 1;
812
                                        while ($rowAttempts = Database::fetch_array($resAttempts)) {
813
                                            $myScore = $rowAttempts['exe_result'];
814
                                            $myMaxscore = $rowAttempts['exe_weighting'];
815
                                            $mktimeStartDate = api_strtotime($rowAttempts['start_date'], 'UTC');
816
                                            $mktimeExeDate = api_strtotime($rowAttempts['exe_date'], 'UTC');
817
                                            $timeAttempt = ' - ';
818
                                            if ($mktimeStartDate && $mktimeExeDate) {
819
                                                $timeAttempt = api_format_time($rowAttempts['exe_duration'], 'js');
820
                                            }
821
                                            // Show only float when need it
822
                                            if ($myScore == 0) {
823
                                                $viewScore = ExerciseLib::show_score(
824
                                                    0,
825
                                                    $myMaxscore,
826
                                                    false
827
                                                );
828
                                            } else {
829
                                                if ($myMaxscore == 0) {
830
                                                    $viewScore = $myScore;
831
                                                } else {
832
                                                    $viewScore = ExerciseLib::show_score(
833
                                                        $myScore,
834
                                                        $myMaxscore,
835
                                                        false
836
                                                    );
837
                                                }
838
                                            }
839
                                            $myLessonStatus = $rowAttempts['status'];
840
                                            if ($myLessonStatus == '') {
841
                                                $myLessonStatus = learnpathitem::humanize_status('completed', false);
842
                                            } elseif ($myLessonStatus == 'incomplete') {
843
                                                $myLessonStatus = learnpathitem::humanize_status('incomplete', false);
844
                                            }
845
                                            $timeRow = '<td style="text-align:center;">'.$timeAttempt.'</td>';
846
                                            if ($hideTime) {
847
                                                $timeRow = '';
848
                                            }
849
                                            $output .= '<tr>
850
                                            <td>&mdash; <em>'.get_lang('Attempt').' '.$n.'</em></td>
851
                                            <td style="text-align:center;"><div class="btn btn-primary boldTitle">'.$myLessonStatus.'</div></td>
852
                                            <td style="text-align:center;">'.$viewScore.'</td>
853
                                            '.$timeRow;
854
                                            $output .= '</tr>';
855
856
                                            // It displays categories questions by attempts
857
                                            $categoriesQuestions = TestCategory::getQuestionsByCat($rowAttempts['exe_exo_id'], [], [], false, $courseId);
858
                                            $categories = TestCategory::getListOfCategoriesIDForTest($rowAttempts['exe_exo_id'], $courseId);
859
                                            $objExercise = new Exercise($courseId);
860
                                            $objExercise->read($rowAttempts['exe_exo_id']);
861
                                            if (!empty($categoriesQuestions)) {
862
                                                foreach ($categoriesQuestions as $catId => $questions) {
863
                                                    $catScore = 0;
864
                                                    $catWeight = 0;
865
                                                    if (!empty($questions)) {
866
                                                        foreach ($questions as $questionId) {
867
                                                            $questionResult = $objExercise->manage_answer(
868
                                                                $rowAttempts['exe_id'],
869
                                                                $questionId,
870
                                                                '',
871
                                                                'exercise_show',
872
                                                                [],
873
                                                                false,
874
                                                                true,
875
                                                                false,
876
                                                                $objExercise->selectPropagateNeg()
877
                                                            );
878
                                                            $catScore += $questionResult['score'];
879
                                                            $catWeight += $questionResult['weight'];
880
                                                        }
881
                                                    }
882
                                                    $timeCatRow = '';
883
                                                    if (!$hideTime) {
884
                                                        $timeCatRow = '<td></td>';
885
                                                    }
886
                                                    $catTitle = $categories[$catId]['title'];
887
                                                    $catScoreDisplay = ($catWeight > 0) ? round(($catScore * 100) / $catWeight).'% ('.$catScore.'/'.$catWeight.')' : '';
888
                                                    $output .= "<tr>";
889
                                                    $output .= '
890
                                                        <td style="width:35%">&mdash;&mdash; '.$catTitle.'</td>
891
                                                        <td style="width:25%;"></td>
892
                                                        <td style="width:20%;text-align:center;">'.$catScoreDisplay.'</td>
893
                                                        '.$timeCatRow.'
894
                                                     ';
895
                                                    $output .= '</tr>';
896
                                                }
897
                                            }
898
                                            $n++;
899
                                        }
900
                                    }
901
                                }
902
                            }
903
                        }
904
                    }
905
                }
906
                $totalTime += $timeForTotal;
907
            }
908
        }
909
910
        $totalScore = self::get_avg_student_score(
911
            $userId,
912
            $courseInfo['code'],
913
            [$lpId],
914
            $sessionId,
915
            false,
916
            false
917
        );
918
919
        $totalTime = learnpathItem::getScormTimeFromParameter('js', $totalTime);
920
        $totalTime = str_replace('NaN', '00'.$h.'00\'00"', $totalTime);
921
922
        if (!$isAllowedToEdit && $resultDisabledExtAll) {
923
            $finalScore = Display::return_icon('invisible.png', get_lang('ResultsHiddenByExerciseSetting'));
924
        } else {
925
            if (is_numeric($totalScore)) {
926
                $finalScore = $totalScore.'%';
927
            } else {
928
                $finalScore = $totalScore;
929
            }
930
        }
931
        $progress = learnpath::getProgress($lpId, $userId, $courseId, $sessionId);
932
        $timeTotal = '<th style="text-align:center;">'.$totalTime.'</th>';
933
        if ($hideTime) {
934
            $timeTotal = '';
935
        }
936
937
        $output .= '<tr>
938
                <th><i>'.get_lang('AccomplishedStepsTotal').'</i></th>
939
                <th style="text-align:center;">'.$progress.'%</th>
940
                <th style="text-align:center;">'.$finalScore.'</th>
941
                '.$timeTotal.'
942
           </tr></tbody></table>';
943
944
        return $output;
945
    }
946
947
    /**
948
     * @param int    $user_id
949
     * @param array  $courseInfo
950
     * @param int    $session_id
951
     * @param string $origin
952
     * @param bool   $export_csv
953
     * @param int    $lp_id
954
     * @param int    $lp_item_id
955
     * @param int    $extendId
956
     * @param int    $extendAttemptId
957
     * @param string $extendedAttempt
958
     * @param string $extendedAll
959
     * @param string $type            classic or simple
960
     * @param bool   $allowExtend     Optional. Allow or not extend te results
961
     *
962
     * @return string
963
     */
964
    public static function getLpStats(
965
        $user_id,
966
        $courseInfo,
967
        $session_id,
968
        $origin,
969
        $export_csv,
970
        $lp_id,
971
        $lp_item_id = null,
972
        $extendId = null,
973
        $extendAttemptId = null,
974
        $extendedAttempt = null,
975
        $extendedAll = null,
976
        $type = 'classic',
977
        $allowExtend = true
978
    ) {
979
        if (empty($courseInfo) || empty($lp_id)) {
980
            return '';
981
        }
982
983
        $hideTime = api_get_configuration_value('hide_lp_time');
984
        $lp_id = (int) $lp_id;
985
        $lp_item_id = (int) $lp_item_id;
986
        $user_id = (int) $user_id;
987
        $session_id = (int) $session_id;
988
        $origin = Security::remove_XSS($origin);
989
        $list = learnpath::get_flat_ordered_items_list($lp_id, 0, $courseInfo['real_id']);
990
        $is_allowed_to_edit = api_is_allowed_to_edit(null, true);
991
        $course_id = $courseInfo['real_id'];
992
        $courseCode = $courseInfo['code'];
993
        $session_condition = api_get_session_condition($session_id);
994
995
        // Extend all button
996
        $output = '';
997
998
        $extra = '<script>
999
        $(function() {
1000
            $( "#dialog:ui-dialog" ).dialog( "destroy" );
1001
            $( "#dialog-confirm" ).dialog({
1002
                autoOpen: false,
1003
                show: "blind",
1004
                resizable: false,
1005
                height:300,
1006
                modal: true
1007
            });
1008
1009
            $(".export").click(function() {
1010
                var targetUrl = $(this).attr("href");
1011
                $( "#dialog-confirm" ).dialog({
1012
                    width:400,
1013
                    height:300,
1014
                    buttons: {
1015
                        "'.addslashes(get_lang('Download')).'": function() {
1016
                            var option = $("input[name=add_logo]:checked").val();
1017
                            location.href = targetUrl+"&add_logo="+option;
1018
                            $(this).dialog("close");
1019
                        }
1020
                    }
1021
                });
1022
                $("#dialog-confirm").dialog("open");
1023
1024
                return false;
1025
            });
1026
        });
1027
        </script>';
1028
1029
        $extra .= '<div id="dialog-confirm" title="'.get_lang('ConfirmYourChoice').'">';
1030
        $form = new FormValidator('report', 'post', null, null, ['class' => 'form-vertical']);
1031
        $form->addCheckBox('add_logo', '', get_lang('AddRightLogo'), ['id' => 'export_format_csv_label']);
1032
        $extra .= $form->returnForm();
1033
        $extra .= '</div>';
1034
1035
        $output .= $extra;
1036
1037
        $url_suffix = '&lp_id='.$lp_id;
1038
        if ('tracking' === $origin) {
1039
            $url_suffix = '&session_id='.$session_id.'&course='.$courseCode.'&student_id='.$user_id.'&lp_id='.$lp_id.'&origin='.$origin;
1040
        }
1041
1042
        $extend_all = 0;
1043
        if (!empty($extendedAll)) {
1044
            $extend_all_link = Display::url(
1045
                Display::return_icon('view_less_stats.gif', get_lang('HideAllAttempts')),
1046
                api_get_self().'?action=stats'.$url_suffix
1047
            );
1048
            $extend_all = 1;
1049
        } else {
1050
            $extend_all_link = Display::url(
1051
                Display::return_icon('view_more_stats.gif', get_lang('ShowAllAttempts')),
1052
                api_get_self().'?action=stats&extend_all=1'.$url_suffix
1053
            );
1054
        }
1055
1056
        if ($origin != 'tracking') {
1057
            $output .= '<div class="section-status">';
1058
            $output .= Display::page_header(get_lang('ScormMystatus'));
1059
            $output .= '</div>';
1060
        }
1061
1062
        $actionColumn = null;
1063
        if ($type === 'classic') {
1064
            $actionColumn = ' <th>'.get_lang('Actions').'</th>';
1065
        }
1066
1067
        $timeHeader = '<th class="lp_time" colspan="2">'.get_lang('ScormTime').'</th>';
1068
        if ($hideTime) {
1069
            $timeHeader = '';
1070
        }
1071
        $output .= '<div class="table-responsive">';
1072
        $output .= '<table id="lp_tracking" class="table tracking">
1073
            <thead>
1074
            <tr class="table-header">
1075
                <th width="16">'.($allowExtend == true ? $extend_all_link : '&nbsp;').'</th>
1076
                <th colspan="4">
1077
                    '.get_lang('ScormLessonTitle').'
1078
                </th>
1079
                <th colspan="2">
1080
                    '.get_lang('ScormStatus').'
1081
                </th>
1082
                <th colspan="2">
1083
                    '.get_lang('ScormScore').'
1084
                </th>
1085
                '.$timeHeader.'
1086
                '.$actionColumn.'
1087
                </tr>
1088
            </thead>
1089
            <tbody>
1090
        ';
1091
1092
        // Going through the items using the $items[] array instead of the database order ensures
1093
        // we get them in the same order as in the imsmanifest file, which is rather random when using
1094
        // the database table.
1095
        $TBL_LP_ITEM = Database::get_course_table(TABLE_LP_ITEM);
1096
        $TBL_LP_ITEM_VIEW = Database::get_course_table(TABLE_LP_ITEM_VIEW);
1097
        $TBL_LP_VIEW = Database::get_course_table(TABLE_LP_VIEW);
1098
        $tbl_quiz_questions = Database::get_course_table(TABLE_QUIZ_QUESTION);
1099
        $TBL_QUIZ = Database::get_course_table(TABLE_QUIZ_TEST);
1100
        $tbl_stats_exercices = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1101
        $tbl_stats_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1102
1103
        $sql = "SELECT max(view_count)
1104
                FROM $TBL_LP_VIEW
1105
                WHERE
1106
                    c_id = $course_id AND
1107
                    lp_id = $lp_id AND
1108
                    user_id = $user_id
1109
                    $session_condition";
1110
        $res = Database::query($sql);
1111
        $view = 0;
1112
        if (Database::num_rows($res) > 0) {
1113
            $myrow = Database::fetch_array($res);
1114
            $view = (int) $myrow[0];
1115
        }
1116
1117
        $counter = 0;
1118
        $total_time = 0;
1119
        $h = get_lang('h');
1120
1121
        if (!empty($export_csv)) {
1122
            $csvHeaders = [
1123
                get_lang('ScormLessonTitle'),
1124
                get_lang('ScormStatus'),
1125
                get_lang('ScormScore'),
1126
            ];
1127
1128
            if ($hideTime === false) {
1129
                $csvHeaders[] = get_lang('ScormTime');
1130
            }
1131
1132
            $csv_content[] = $csvHeaders;
1133
        }
1134
1135
        $result_disabled_ext_all = true;
1136
        $chapterTypes = learnpath::getChapterTypes();
1137
        $accessToPdfExport = api_is_allowed_to_edit(false, false, true);
1138
1139
        $minimumAvailable = self::minimumTimeAvailable($session_id, $course_id);
1140
        $timeCourse = [];
1141
        if ($minimumAvailable) {
1142
            $timeCourse = self::getCalculateTime($user_id, $course_id, $session_id);
1143
            Session::write('trackTimeCourse', $timeCourse);
1144
        }
1145
1146
        // Show lp items
1147
        if (is_array($list) && count($list) > 0) {
1148
            foreach ($list as $my_item_id) {
1149
                $extend_this = 0;
1150
                $order = 'DESC';
1151
                if ((!empty($extendId) && $extendId == $my_item_id) || $extend_all) {
1152
                    $extend_this = 1;
1153
                    $order = 'ASC';
1154
                }
1155
1156
                // Prepare statement to go through each attempt.
1157
                $viewCondition = null;
1158
                if (!empty($view)) {
1159
                    $viewCondition = " AND v.view_count = $view  ";
1160
                }
1161
1162
                $sql = "SELECT
1163
                    iv.status as mystatus,
1164
                    v.view_count as mycount,
1165
                    iv.score as myscore,
1166
                    iv.total_time as mytime,
1167
                    i.iid as myid,
1168
                    i.lp_id as mylpid,
1169
                    iv.lp_view_id as mylpviewid,
1170
                    i.title as mytitle,
1171
                    i.max_score as mymaxscore,
1172
                    iv.max_score as myviewmaxscore,
1173
                    i.item_type as item_type,
1174
                    iv.view_count as iv_view_count,
1175
                    iv.id as iv_id,
1176
                    path
1177
                FROM $TBL_LP_ITEM as i
1178
                INNER JOIN $TBL_LP_ITEM_VIEW as iv
1179
                ON (i.iid = iv.lp_item_id AND i.c_id = iv.c_id)
1180
                INNER JOIN $TBL_LP_VIEW as v
1181
                ON (iv.lp_view_id = v.id AND v.c_id = iv.c_id)
1182
                WHERE
1183
                    v.c_id = $course_id AND
1184
                    i.iid = $my_item_id AND
1185
                    i.lp_id = $lp_id  AND
1186
                    v.user_id = $user_id AND
1187
                    v.session_id = $session_id
1188
                    $viewCondition
1189
                ORDER BY iv.view_count $order ";
1190
1191
                $result = Database::query($sql);
1192
                $num = Database::num_rows($result);
1193
                $time_for_total = 0;
1194
1195
                // Extend all
1196
                if (($extend_this || $extend_all) && $num > 0) {
1197
                    $row = Database::fetch_array($result);
1198
                    $result_disabled_ext_all = false;
1199
                    if ('quiz' === $row['item_type']) {
1200
                        // Check results_disabled in quiz table.
1201
                        $lpItemPath = (int) $row['path'];
1202
                        $sql = "SELECT results_disabled
1203
                                FROM $TBL_QUIZ
1204
                                WHERE
1205
                                    iid = $lpItemPath";
1206
                        $res_result_disabled = Database::query($sql);
1207
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
1208
1209
                        if (Database::num_rows($res_result_disabled) > 0
1210
                            && 1 === (int) $row_result_disabled[0]
1211
                        ) {
1212
                            $result_disabled_ext_all = true;
1213
                        }
1214
                    }
1215
1216
                    // If there are several attempts, and the link to extend has been clicked, show each attempt...
1217
                    $oddclass = 'row_even';
1218
                    if (($counter % 2) === 0) {
1219
                        $oddclass = 'row_odd';
1220
                    }
1221
                    $extend_link = '';
1222
                    if (!empty($inter_num)) {
1223
                        $extend_link = Display::url(
1224
                            Display::return_icon(
1225
                                'visible.png',
1226
                                get_lang('HideAttemptView')
1227
                            ),
1228
                            api_get_self().'?action=stats&fold_id='.$my_item_id.$url_suffix
1229
                        );
1230
                    }
1231
                    $title = $row['mytitle'];
1232
1233
                    if (empty($title)) {
1234
                        $title = learnpath::rl_get_resource_name($courseInfo['code'], $lp_id, $row['myid']);
1235
                    }
1236
1237
                    if (in_array($row['item_type'], $chapterTypes)) {
1238
                        $title = "<h4> $title </h4>";
1239
                    }
1240
                    $lesson_status = $row['mystatus'];
1241
                    $title = Security::remove_XSS($title);
1242
                    $counter++;
1243
1244
                    $action = null;
1245
                    if ('classic' === $type) {
1246
                        $action = '<td></td>';
1247
                    }
1248
1249
                    if (in_array($row['item_type'], $chapterTypes)) {
1250
                        $output .= '<tr class="'.$oddclass.'">
1251
                                <td>'.$extend_link.'</td>
1252
                                <td colspan="4">
1253
                                   '.$title.'
1254
                                </td>
1255
                                <td colspan="2">'.learnpathItem::humanize_status($lesson_status, true, $type).'</td>
1256
                                <td colspan="2"></td>
1257
                                <td colspan="2"></td>
1258
                                '.$action.'
1259
                            </tr>';
1260
                        continue;
1261
                    } else {
1262
                        $output .= '<tr class="'.$oddclass.'">
1263
                                <td>'.$extend_link.'</td>
1264
                                <td colspan="4">'.$title.'</td>
1265
                                <td colspan="2"></td>
1266
                                <td colspan="2"></td>
1267
                                <td colspan="2"></td>
1268
                                '.$action.'
1269
                            </tr>';
1270
                    }
1271
1272
                    $attemptCount = 1;
1273
                    do {
1274
                        // Check if there are interactions below.
1275
                        $extend_attempt_link = '';
1276
                        $extend_this_attempt = 0;
1277
1278
                        if ($timeCourse) {
1279
                            // $attemptResult = 0;
1280
                            if (isset($timeCourse['learnpath_detailed'])
1281
                                && isset($timeCourse['learnpath_detailed'][$lp_id])
1282
                                && isset($timeCourse['learnpath_detailed'][$lp_id][$my_item_id])
1283
                            ) {
1284
                                $attemptResult = $timeCourse['learnpath_detailed'][$lp_id][$my_item_id][$row['iv_view_count']];
1285
                            }
1286
                        }
1287
                        if ((
1288
                            learnpath::get_interactions_count_from_db($row['iv_id'], $course_id) > 0
1289
                            || learnpath::get_objectives_count_from_db($row['iv_id'], $course_id) > 0
1290
                        )
1291
                        && !$extend_all
1292
                        ) {
1293
                            if ($extendAttemptId == $row['iv_id']) {
1294
                                // The extend button for this attempt has been clicked.
1295
                                $extend_this_attempt = 1;
1296
                                $extend_attempt_link = Display::url(
1297
                                    Display::return_icon('visible.png', get_lang('HideAttemptView')),
1298
                                    api_get_self().'?action=stats&extend_id='.$my_item_id.'&fold_attempt_id='.$row['iv_id'].$url_suffix
1299
                                );
1300
                                if ($accessToPdfExport) {
1301
                                    $extend_attempt_link .= '&nbsp;'.
1302
                                        Display::url(
1303
                                            Display::return_icon('pdf.png', get_lang('ExportToPdf')),
1304
                                            api_get_self().'?action=export_stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix,
1305
                                            ['class' => 'export']
1306
                                        );
1307
                                }
1308
                            } else { // Same case if fold_attempt_id is set, so not implemented explicitly.
1309
                                // The extend button for this attempt has not been clicked.
1310
                                $extend_attempt_link = Display::url(
1311
                                    Display::return_icon('invisible.png', get_lang('ExtendAttemptView')),
1312
                                    api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
1313
                                );
1314
                                if ($accessToPdfExport) {
1315
                                    $extend_attempt_link .= '&nbsp;'.
1316
                                        Display::url(
1317
                                            Display::return_icon('pdf.png', get_lang('ExportToPdf')),
1318
                                            api_get_self().'?action=export_stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix,
1319
                                            ['class' => 'export']
1320
                                        );
1321
                                }
1322
                            }
1323
                        }
1324
1325
                        $oddclass = 'row_even';
1326
                        if (($counter % 2) == 0) {
1327
                            $oddclass = 'row_odd';
1328
                        }
1329
1330
                        $lesson_status = $row['mystatus'];
1331
                        $score = $row['myscore'];
1332
                        $time_for_total += $row['mytime'];
1333
                        $attemptTime = $row['mytime'];
1334
1335
                        if ($minimumAvailable) {
1336
                            /*$lp_time = $timeCourse[TOOL_LEARNPATH];
1337
                            $lpTime = null;
1338
                            if (isset($lp_time[$lp_id])) {
1339
                                $lpTime = (int) $lp_time[$lp_id];
1340
                            }
1341
                            $time_for_total = $lpTime;*/
1342
                            $time_for_total = (int) $attemptResult;
1343
                            $attemptTime = (int) $attemptResult;
1344
                        }
1345
1346
                        $time = learnpathItem::getScormTimeFromParameter('js', $attemptTime);
1347
1348
                        if ($score == 0) {
1349
                            $maxscore = $row['mymaxscore'];
1350
                        } else {
1351
                            if ($row['item_type'] === 'sco') {
1352
                                if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
1353
                                    $maxscore = $row['myviewmaxscore'];
1354
                                } elseif ($row['myviewmaxscore'] === '') {
1355
                                    $maxscore = 0;
1356
                                } else {
1357
                                    $maxscore = $row['mymaxscore'];
1358
                                }
1359
                            } else {
1360
                                $maxscore = $row['mymaxscore'];
1361
                            }
1362
                        }
1363
1364
                        // Remove "NaN" if any (@todo: locate the source of these NaN)
1365
                        $time = str_replace('NaN', '00'.$h.'00\'00"', $time);
1366
                        if ($row['item_type'] !== 'dir') {
1367
                            if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1368
                                $view_score = Display::return_icon(
1369
                                    'invisible.png',
1370
                                    get_lang('ResultsHiddenByExerciseSetting')
1371
                                );
1372
                            } else {
1373
                                switch ($row['item_type']) {
1374
                                    case 'sco':
1375
                                        if ($maxscore == 0) {
1376
                                            $view_score = $score;
1377
                                        } else {
1378
                                            $view_score = ExerciseLib::show_score(
1379
                                                $score,
1380
                                                $maxscore,
1381
                                                false
1382
                                            );
1383
                                        }
1384
                                        break;
1385
                                    case 'document':
1386
                                        $view_score = ($score == 0 ? '/' : ExerciseLib::show_score($score, $maxscore, false));
1387
                                        break;
1388
                                    default:
1389
                                        $view_score = ExerciseLib::show_score(
1390
                                            $score,
1391
                                            $maxscore,
1392
                                            false
1393
                                        );
1394
                                        break;
1395
                                }
1396
                            }
1397
1398
                            $action = null;
1399
                            if ($type === 'classic') {
1400
                                $action = '<td></td>';
1401
                            }
1402
                            $timeRow = '<td class="lp_time" colspan="2">'.$time.'</td>';
1403
                            if ($hideTime) {
1404
                                $timeRow = '';
1405
                            }
1406
                            $output .= '<tr class="'.$oddclass.'">
1407
                                    <td></td>
1408
                                    <td style="width:70px;float:left;">'.$extend_attempt_link.'</td>
1409
                                    <td colspan="3">'.get_lang('Attempt').' '.$attemptCount.'</td>
1410
                                    <td colspan="2">'.learnpathItem::humanize_status($lesson_status, true, $type).'</td>
1411
                                    <td colspan="2">'.$view_score.'</td>
1412
                                    '.$timeRow.'
1413
                                    '.$action.'
1414
                                </tr>';
1415
                            $attemptCount++;
1416
                            if (!empty($export_csv)) {
1417
                                $temp = [];
1418
                                $temp[] = $title = Security::remove_XSS($title);
1419
                                $temp[] = Security::remove_XSS(
1420
                                    learnpathItem::humanize_status($lesson_status, false, $type)
1421
                                );
1422
1423
                                if ($row['item_type'] === 'quiz') {
1424
                                    if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1425
                                        $temp[] = '/';
1426
                                    } else {
1427
                                        $temp[] = ($score == 0 ? '0/'.$maxscore : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
1428
                                    }
1429
                                } else {
1430
                                    $temp[] = ($score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
1431
                                }
1432
1433
                                if ($hideTime === false) {
1434
                                    $temp[] = $time;
1435
                                }
1436
                                $csv_content[] = $temp;
1437
                            }
1438
                        }
1439
1440
                        $counter++;
1441
                        $action = null;
1442
                        if ($type === 'classic') {
1443
                            $action = '<td></td>';
1444
                        }
1445
1446
                        if ($extend_this_attempt || $extend_all) {
1447
                            $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $course_id);
1448
                            foreach ($list1 as $id => $interaction) {
1449
                                $oddclass = 'row_even';
1450
                                if (($counter % 2) == 0) {
1451
                                    $oddclass = 'row_odd';
1452
                                }
1453
                                $timeRow = '<td class="lp_time">'.$interaction['time'].'</td>';
1454
                                if ($hideTime) {
1455
                                    $timeRow = '';
1456
                                }
1457
1458
                                $output .= '<tr class="'.$oddclass.'">
1459
                                        <td></td>
1460
                                        <td></td>
1461
                                        <td></td>
1462
                                        <td>'.$interaction['order_id'].'</td>
1463
                                        <td>'.$interaction['id'].'</td>';
1464
1465
                                $output .= '
1466
                                        <td colspan="2">'.$interaction['type'].'</td>
1467
                                        <td>'.$interaction['student_response_formatted'].'</td>
1468
                                        <td>'.$interaction['result'].'</td>
1469
                                        <td>'.$interaction['latency'].'</td>
1470
                                        '.$timeRow.'
1471
                                        '.$action.'
1472
                                    </tr>';
1473
                                $counter++;
1474
                            }
1475
                            $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $course_id);
1476
                            foreach ($list2 as $id => $interaction) {
1477
                                $oddclass = 'row_even';
1478
                                if (($counter % 2) === 0) {
1479
                                    $oddclass = 'row_odd';
1480
                                }
1481
                                $output .= '<tr class="'.$oddclass.'">
1482
                                        <td></td>
1483
                                        <td></td>
1484
                                        <td></td>
1485
                                        <td>'.$interaction['order_id'].'</td>
1486
                                        <td colspan="2">'.$interaction['objective_id'].'</td>
1487
                                        <td colspan="2">'.$interaction['status'].'</td>
1488
                                        <td>'.$interaction['score_raw'].'</td>
1489
                                        <td>'.$interaction['score_max'].'</td>
1490
                                        <td>'.$interaction['score_min'].'</td>
1491
                                        '.$action.'
1492
                                     </tr>';
1493
                                $counter++;
1494
                            }
1495
                        }
1496
                    } while ($row = Database::fetch_array($result));
1497
                } elseif ($num > 0) {
1498
                    // Not extended.
1499
                    $row = Database::fetch_array($result, 'ASSOC');
1500
                    $my_id = $row['myid'];
1501
                    $my_lp_id = $row['mylpid'];
1502
                    $my_lp_view_id = $row['mylpviewid'];
1503
                    $lpItemPath = (int) $row['path'];
1504
                    $result_disabled_ext_all = false;
1505
                    if ($row['item_type'] === 'quiz') {
1506
                        // Check results_disabled in quiz table.
1507
                        $sql = "SELECT results_disabled
1508
                                FROM $TBL_QUIZ
1509
                                WHERE iid = $lpItemPath";
1510
                        $res_result_disabled = Database::query($sql);
1511
                        $row_result_disabled = Database::fetch_row($res_result_disabled);
1512
1513
                        if (Database::num_rows($res_result_disabled) > 0
1514
                            && (int) $row_result_disabled[0] === 1
1515
                        ) {
1516
                            $result_disabled_ext_all = true;
1517
                        }
1518
                    }
1519
1520
                    // Check if there are interactions below
1521
                    $extend_this_attempt = 0;
1522
                    $inter_num = learnpath::get_interactions_count_from_db($row['iv_id'], $course_id);
1523
                    $objec_num = learnpath::get_objectives_count_from_db($row['iv_id'], $course_id);
1524
                    $extend_attempt_link = '';
1525
                    if ($inter_num > 0 || $objec_num > 0) {
1526
                        if (!empty($extendAttemptId) && $extendAttemptId == $row['iv_id']) {
1527
                            // The extend button for this attempt has been clicked.
1528
                            $extend_this_attempt = 1;
1529
                            $extend_attempt_link = Display::url(
1530
                                Display::return_icon('visible.png', get_lang('HideAttemptView')),
1531
                                api_get_self().'?action=stats&extend_id='.$my_item_id.'&fold_attempt_id='.$row['iv_id'].$url_suffix
1532
                            );
1533
                        } else {
1534
                            // Same case if fold_attempt_id is set, so not implemented explicitly.
1535
                            // The extend button for this attempt has not been clicked.
1536
                            $extend_attempt_link = Display::url(
1537
                                Display::return_icon('invisible.png', get_lang('ExtendAttemptView')),
1538
                                api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
1539
                            );
1540
                        }
1541
                    }
1542
1543
                    $oddclass = 'row_even';
1544
                    if (($counter % 2) == 0) {
1545
                        $oddclass = 'row_odd';
1546
                    }
1547
1548
                    $extend_link = '';
1549
                    if ($inter_num > 1) {
1550
                        $extend_link = Display::url(
1551
                            Display::return_icon('invisible.png', get_lang('ExtendAttemptView')),
1552
                            api_get_self().'?action=stats&extend_id='.$my_item_id.'&extend_attempt_id='.$row['iv_id'].$url_suffix
1553
                        );
1554
                    }
1555
                    $lesson_status = $row['mystatus'];
1556
                    $score = $row['myscore'];
1557
                    $subtotal_time = $row['mytime'];
1558
1559
                    // This is necessary for fix the total time
1560
                    $h5pImportEnable = api_get_plugin_setting('h5pimport', 'tool_enable');
1561
                    if ($row['item_type'] === 'h5p' && 'true' === $h5pImportEnable) {
1562
                        $subtotal_time = H5pImportPlugin::fixTotalTimeInLpItemView(
1563
                            $row['iv_id'],
1564
                            $user_id
1565
                        );
1566
                    }
1567
                    while ($tmp_row = Database::fetch_array($result)) {
1568
                        $subtotal_time += $tmp_row['mytime'];
1569
                    }
1570
1571
                    $title = $row['mytitle'];
1572
                    // Selecting the exe_id from stats attempts tables in order to look the max score value.
1573
                    $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
1574
                            WHERE
1575
                                exe_exo_id="'.$row['path'].'" AND
1576
                                exe_user_id="'.$user_id.'" AND
1577
                                orig_lp_id = "'.$lp_id.'" AND
1578
                                orig_lp_item_id = "'.$row['myid'].'" AND
1579
                                c_id = '.$course_id.' AND
1580
                                status <> "incomplete" AND
1581
                                session_id = '.$session_id.'
1582
                             ORDER BY exe_date DESC
1583
                             LIMIT 1';
1584
1585
                    $resultLastAttempt = Database::query($sql);
1586
                    $num = Database::num_rows($resultLastAttempt);
1587
                    $id_last_attempt = null;
1588
                    if ($num > 0) {
1589
                        while ($rowLA = Database::fetch_array($resultLastAttempt)) {
1590
                            $id_last_attempt = $rowLA['exe_id'];
1591
                        }
1592
                    }
1593
1594
                    switch ($row['item_type']) {
1595
                        case 'sco':
1596
                            if (!empty($row['myviewmaxscore']) && $row['myviewmaxscore'] > 0) {
1597
                                $maxscore = $row['myviewmaxscore'];
1598
                            } elseif ($row['myviewmaxscore'] === '') {
1599
                                $maxscore = 0;
1600
                            } else {
1601
                                $maxscore = $row['mymaxscore'];
1602
                            }
1603
                            break;
1604
                        case 'quiz':
1605
                            // Get score and total time from last attempt of a exercise en lp.
1606
                            $sql = "SELECT iid, score
1607
                                FROM $TBL_LP_ITEM_VIEW
1608
                                WHERE
1609
                                    c_id = ".(int) $course_id." AND
1610
                                    lp_item_id = ".(int) $my_id." AND
1611
                                    lp_view_id = ".(int) $my_lp_view_id."
1612
                                ORDER BY view_count DESC
1613
                                LIMIT 1";
1614
1615
                            $res_score = Database::query($sql);
1616
                            $row_score = Database::fetch_array($res_score);
1617
1618
                            $sql = "SELECT SUM(total_time) AS total_time
1619
                                    FROM $TBL_LP_ITEM_VIEW
1620
                                    WHERE
1621
                                        c_id = ".(int) $course_id." AND
1622
                                        lp_item_id = ".(int) $my_id." AND
1623
                                        lp_view_id = ".(int) $my_lp_view_id;
1624
                            $res_time = Database::query($sql);
1625
                            $row_time = Database::fetch_array($res_time);
1626
1627
                            $score = 0;
1628
                            $subtotal_time = 0;
1629
                            if (Database::num_rows($res_score) > 0
1630
                                && Database::num_rows($res_time) > 0
1631
                            ) {
1632
                                $score = (float) $row_score['score'];
1633
                                $subtotal_time = (int) $row_time['total_time'];
1634
                            }
1635
                            // Selecting the max score from an attempt.
1636
                            $sql = "SELECT SUM(t.ponderation) as maxscore
1637
                                    FROM (
1638
                                        SELECT DISTINCT
1639
                                            question_id, marks, ponderation
1640
                                        FROM $tbl_stats_attempts as at
1641
                                        INNER JOIN $tbl_quiz_questions as q
1642
                                        ON q.iid = at.question_id
1643
                                        WHERE exe_id ='$id_last_attempt'
1644
                                    ) as t";
1645
1646
                            $result = Database::query($sql);
1647
                            $row_max_score = Database::fetch_array($result);
1648
                            $maxscore = $row_max_score['maxscore'];
1649
1650
                            // Get duration time from track_e_exercises.exe_duration instead of lp_view_item.total_time
1651
                            $sql = 'SELECT SUM(exe_duration) exe_duration
1652
                                    FROM '.$tbl_stats_exercices.'
1653
                                    WHERE
1654
                                        exe_exo_id="'.$row['path'].'" AND
1655
                                        exe_user_id="'.$user_id.'" AND
1656
                                        orig_lp_id = "'.$lp_id.'" AND
1657
                                        orig_lp_item_id = "'.$row['myid'].'" AND
1658
                                        c_id = '.$course_id.' AND
1659
                                        status <> "incomplete" AND
1660
                                        session_id = '.$session_id.'
1661
                                     ORDER BY exe_date DESC ';
1662
                            $sumScoreResult = Database::query($sql);
1663
                            $durationRow = Database::fetch_array($sumScoreResult, 'ASSOC');
1664
                            if (!empty($durationRow['exe_duration'])) {
1665
                                $exeDuration = $durationRow['exe_duration'];
1666
                                if ($exeDuration != $subtotal_time
1667
                                    && !empty($row_score['iid'])
1668
                                    && !empty($exeDuration)
1669
                                ) {
1670
                                    $subtotal_time = $exeDuration;
1671
                                    // Update c_lp_item_view.total_time
1672
                                    $sqlUpdate = "UPDATE $TBL_LP_ITEM_VIEW SET total_time = '$exeDuration'
1673
                                                  WHERE iid = ".$row_score['iid'];
1674
                                    Database::query($sqlUpdate);
1675
                                }
1676
                            }
1677
                            break;
1678
                        case 'h5p':
1679
                            if ($row['item_type'] === 'h5p' && 'true' === $h5pImportEnable) {
1680
                                $sql = "SELECT iid, score
1681
                                        FROM $TBL_LP_ITEM_VIEW
1682
                                        WHERE
1683
                                            c_id = ".(int) $course_id." AND
1684
                                            lp_item_id = ".(int) $my_id." AND
1685
                                            lp_view_id = ".(int) $my_lp_view_id."
1686
                                        ORDER BY view_count DESC
1687
                                        LIMIT 1";
1688
                                $res_score = Database::query($sql);
1689
                                $row_score = Database::fetch_array($res_score);
1690
1691
                                H5pImportPlugin::fixTotalTimeInLpItemView($row_score['iid'], $user_id);
1692
                            }
1693
                            break;
1694
                        default:
1695
                            $maxscore = $row['mymaxscore'];
1696
                            break;
1697
                    }
1698
1699
                    $time_for_total = $subtotal_time;
1700
                    $time = learnpathItem::getScormTimeFromParameter('js', $subtotal_time);
1701
                    if (empty($title)) {
1702
                        $title = learnpath::rl_get_resource_name(
1703
                            $courseInfo['code'],
1704
                            $lp_id,
1705
                            $row['myid']
1706
                        );
1707
                    }
1708
1709
                    $action = null;
1710
                    if ($type === 'classic') {
1711
                        $action = '<td></td>';
1712
                    }
1713
1714
                    if (in_array($row['item_type'], $chapterTypes)) {
1715
                        $title = Security::remove_XSS($title);
1716
                        $output .= '<tr class="'.$oddclass.'">
1717
                                <td>'.$extend_link.'</td>
1718
                                <td colspan="10">
1719
                                <h4>'.$title.'</h4>
1720
                                </td>
1721
                                '.$action.'
1722
                            </tr>';
1723
                    } else {
1724
                        $correct_test_link = '-';
1725
                        $showRowspan = false;
1726
                        $my_url_suffix = '&course='.$courseCode.'&student_id='.$user_id.'&lp_id='.intval($row['mylpid']).'&origin='.$origin;
1727
                        if ($row['item_type'] === 'quiz') {
1728
                            $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
1729
                                     WHERE
1730
                                        exe_exo_id="'.$row['path'].'" AND
1731
                                        exe_user_id="'.$user_id.'" AND
1732
                                        orig_lp_id = "'.$lp_id.'" AND
1733
                                        orig_lp_item_id = "'.$row['myid'].'" AND
1734
                                        c_id = '.$course_id.' AND
1735
                                        status <> "incomplete" AND
1736
                                        session_id = '.$session_id.'
1737
                                     ORDER BY exe_date DESC ';
1738
1739
                            $resultLastAttempt = Database::query($sql);
1740
                            $num = Database::num_rows($resultLastAttempt);
1741
                            $showRowspan = false;
1742
                            if ($num > 0) {
1743
                                $linkId = 'link_'.$my_id;
1744
                                if ($extendedAttempt == 1
1745
                                    && $lp_id == $my_lp_id
1746
                                    && $lp_item_id == $my_id
1747
                                ) {
1748
                                    $showRowspan = true;
1749
                                    $correct_test_link = Display::url(
1750
                                        Display::return_icon(
1751
                                            'view_less_stats.gif',
1752
                                            get_lang('HideAllAttempts')
1753
                                        ),
1754
                                        api_get_self().'?action=stats'.$my_url_suffix.'&session_id='.$session_id.'&lp_item_id='.$my_id.'#'.$linkId,
1755
                                        ['id' => $linkId]
1756
                                    );
1757
                                } else {
1758
                                    $correct_test_link = Display::url(
1759
                                        Display::return_icon(
1760
                                            'view_more_stats.gif',
1761
                                            get_lang(
1762
                                                'ShowAllAttemptsByExercise'
1763
                                            )
1764
                                        ),
1765
                                        api_get_self().'?action=stats&extend_attempt=1'.$my_url_suffix.'&session_id='.$session_id.'&lp_item_id='.$my_id.'#'.$linkId,
1766
                                        ['id' => $linkId]
1767
                                    );
1768
                                }
1769
                            }
1770
                        }
1771
                        if ($row['item_type'] === 'h5p' && 'true' === $h5pImportEnable) {
1772
                            $em = Database::getManager();
1773
                            $cLpItemViewRepo = $em->getRepository('ChamiloPluginBundle:H5pImport\H5pImportResults');
1774
                            $count = $cLpItemViewRepo->count(['user' => $user_id, 'cLpItemView' => $row['iv_id']]);
1775
1776
                            $showRowspan = false;
1777
                            if ($count > 0) {
1778
                                $linkId = 'link_'.$my_id;
1779
                                if ($extendedAttempt == 1
1780
                                    && $lp_id == $my_lp_id
1781
                                    && $lp_item_id == $my_id
1782
                                ) {
1783
                                    $showRowspan = true;
1784
                                    $correct_test_link = Display::url(
1785
                                        Display::return_icon(
1786
                                            'view_less_stats.gif',
1787
                                            get_lang('HideAllAttempts')
1788
                                        ),
1789
                                        api_get_self().'?action=stats'.$my_url_suffix.'&session_id='.$session_id.'&lp_item_id='.$my_id.'#'.$linkId,
1790
                                        ['id' => $linkId]
1791
                                    );
1792
                                } else {
1793
                                    $correct_test_link = Display::url(
1794
                                        Display::return_icon(
1795
                                            'view_more_stats.gif',
1796
                                            get_lang(
1797
                                                'ShowAllAttemptsByExercise'
1798
                                            )
1799
                                        ),
1800
                                        api_get_self().'?action=stats&extend_attempt=1'.$my_url_suffix.'&session_id='.$session_id.'&lp_item_id='.$my_id.'#'.$linkId,
1801
                                        ['id' => $linkId]
1802
                                    );
1803
                                }
1804
                            }
1805
                        }
1806
                        $title = Security::remove_XSS($title);
1807
                        $action = null;
1808
                        if ($type === 'classic') {
1809
                            $action = '<td '.($showRowspan ? 'rowspan="2"' : '').'>'.$correct_test_link.'</td>';
1810
                        }
1811
1812
                        if ($lp_id == $my_lp_id && false) {
1813
                            $output .= '<tr class ='.$oddclass.'>
1814
                                    <td>'.$extend_link.'</td>
1815
                                    <td colspan="4">'.$title.'</td>
1816
                                    <td colspan="2">&nbsp;</td>
1817
                                    <td colspan="2">&nbsp;</td>
1818
                                    <td colspan="2">&nbsp;</td>
1819
                                    '.$action.'
1820
                                </tr>';
1821
                            $output .= '</tr>';
1822
                        } else {
1823
                            if ($lp_id == $my_lp_id && $lp_item_id == $my_id) {
1824
                                $output .= "<tr class='$oddclass'>";
1825
                            } else {
1826
                                $output .= "<tr class='$oddclass'>";
1827
                            }
1828
1829
                            $scoreItem = null;
1830
                            if ($row['item_type'] === 'quiz') {
1831
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1832
                                    $scoreItem .= Display::return_icon(
1833
                                        'invisible.gif',
1834
                                        get_lang('ResultsHiddenByExerciseSetting')
1835
                                    );
1836
                                } else {
1837
                                    $scoreItem .= ExerciseLib::show_score($score, $maxscore, false);
1838
                                }
1839
                            } else {
1840
                                $scoreItem .= $score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.$maxscore);
1841
                            }
1842
1843
                            $timeRow = '<td class="lp_time" colspan="2">'.$time.'</td>';
1844
                            if ($hideTime) {
1845
                                $timeRow = '';
1846
                            }
1847
1848
                            $output .= '
1849
                                <td>'.$extend_link.'</td>
1850
                                <td colspan="4">'.$title.'</td>
1851
                                <td colspan="2">'.learnpathitem::humanize_status($lesson_status).'</td>
1852
                                <td colspan="2">'.$scoreItem.'</td>
1853
                                '.$timeRow.'
1854
                                '.$action.'
1855
                             ';
1856
                            $output .= '</tr>';
1857
                        }
1858
1859
                        if (!empty($export_csv)) {
1860
                            $temp = [];
1861
                            $temp[] = api_html_entity_decode($title, ENT_QUOTES);
1862
                            $temp[] = api_html_entity_decode($lesson_status, ENT_QUOTES);
1863
                            if ($row['item_type'] === 'quiz') {
1864
                                if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1865
                                    $temp[] = '/';
1866
                                } else {
1867
                                    $temp[] = ($score == 0 ? '0/'.$maxscore : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
1868
                                }
1869
                            } else {
1870
                                $temp[] = ($score == 0 ? '/' : ($maxscore == 0 ? $score : $score.'/'.float_format($maxscore, 1)));
1871
                            }
1872
1873
                            if ($hideTime === false) {
1874
                                $temp[] = $time;
1875
                            }
1876
                            $csv_content[] = $temp;
1877
                        }
1878
                    }
1879
1880
                    $counter++;
1881
                    $action = null;
1882
                    if ($type === 'classic') {
1883
                        $action = '<td></td>';
1884
                    }
1885
1886
                    if ($extend_this_attempt || $extend_all) {
1887
                        $list1 = learnpath::get_iv_interactions_array($row['iv_id'], $course_id);
1888
                        foreach ($list1 as $id => $interaction) {
1889
                            $oddclass = 'row_even';
1890
                            if (($counter % 2) == 0) {
1891
                                $oddclass = 'row_odd';
1892
                            }
1893
                            $timeRow = '<td class="lp_time">'.$interaction['time'].'</td>';
1894
                            if ($hideTime) {
1895
                                $timeRow = '';
1896
                            }
1897
1898
                            $output .= '<tr class="'.$oddclass.'">
1899
                                    <td></td>
1900
                                    <td></td>
1901
                                    <td></td>
1902
                                    <td>'.$interaction['order_id'].'</td>
1903
                                    <td>'.$interaction['id'].'</td>
1904
                                    <td colspan="2">'.$interaction['type'].'</td>
1905
                                    <td>'.urldecode($interaction['student_response']).'</td>
1906
                                    <td>'.$interaction['result'].'</td>
1907
                                    <td>'.$interaction['latency'].'</td>
1908
                                    '.$timeRow.'
1909
                                    '.$action.'
1910
                               </tr>';
1911
                            $counter++;
1912
                        }
1913
1914
                        $list2 = learnpath::get_iv_objectives_array($row['iv_id'], $course_id);
1915
                        foreach ($list2 as $id => $interaction) {
1916
                            $oddclass = 'row_even';
1917
                            if (($counter % 2) == 0) {
1918
                                $oddclass = 'row_odd';
1919
                            }
1920
                            $output .= '<tr class="'.$oddclass.'">
1921
                                    <td></td>
1922
                                    <td></td>
1923
                                    <td></td>
1924
                                    <td>'.$interaction['order_id'].'</td>
1925
                                    <td colspan="2">'.$interaction['objective_id'].'</td>
1926
                                    <td colspan="2">'.$interaction['status'].'</td>
1927
                                    <td>'.$interaction['score_raw'].'</td>
1928
                                    <td>'.$interaction['score_max'].'</td>
1929
                                    <td>'.$interaction['score_min'].'</td>
1930
                                    '.$action.'
1931
                               </tr>';
1932
                            $counter++;
1933
                        }
1934
                    }
1935
1936
                    // Attempts listing by exercise.
1937
                    if ($lp_id == $my_lp_id && $lp_item_id == $my_id && $extendedAttempt) {
1938
                        // Get attempts of an exercise.
1939
                        if (!empty($lp_id)
1940
                            && !empty($lp_item_id)
1941
                        ) {
1942
                            $sql = "SELECT path FROM $TBL_LP_ITEM
1943
                                    WHERE
1944
                                        c_id = $course_id AND
1945
                                        iid = '$lp_item_id' AND
1946
                                        lp_id = '$lp_id'";
1947
                            $res_path = Database::query($sql);
1948
                            $row_path = Database::fetch_array($res_path);
1949
1950
                            if (Database::num_rows($res_path) > 0) {
1951
                                if ('quiz' === $row['item_type']) {
1952
                                    $sql = 'SELECT * FROM '.$tbl_stats_exercices.'
1953
                                    WHERE
1954
                                        exe_exo_id = '.(int) $row_path['path'].' AND
1955
                                        status <> "incomplete" AND
1956
                                        exe_user_id='.$user_id.' AND
1957
                                        orig_lp_id = '.(int) $lp_id.' AND
1958
                                        orig_lp_item_id = '.(int) $lp_item_id.' AND
1959
                                        c_id = '.$course_id.' AND
1960
                                        session_id = '.$session_id.'
1961
                                    ORDER BY exe_date';
1962
                                    $res_attempts = Database::query($sql);
1963
                                    $num_attempts = Database::num_rows($res_attempts);
1964
                                    if ($num_attempts > 0) {
1965
                                        $n = 1;
1966
                                        while ($row_attempts = Database::fetch_array($res_attempts)) {
1967
                                            $my_score = $row_attempts['exe_result'];
1968
                                            $my_maxscore = $row_attempts['exe_weighting'];
1969
                                            $my_exe_id = $row_attempts['exe_id'];
1970
                                            $mktime_start_date = api_strtotime($row_attempts['start_date'], 'UTC');
1971
                                            $mktime_exe_date = api_strtotime($row_attempts['exe_date'], 'UTC');
1972
                                            $time_attemp = ' - ';
1973
                                            if ($mktime_start_date && $mktime_exe_date) {
1974
                                                $time_attemp = api_format_time($row_attempts['exe_duration'], 'js');
1975
                                            }
1976
                                            if (!$is_allowed_to_edit && $result_disabled_ext_all) {
1977
                                                $view_score = Display::return_icon(
1978
                                                    'invisible.png',
1979
                                                    get_lang(
1980
                                                        'ResultsHiddenByExerciseSetting'
1981
                                                    )
1982
                                                );
1983
                                            } else {
1984
                                                // Show only float when need it
1985
                                                if ($my_score == 0) {
1986
                                                    $view_score = ExerciseLib::show_score(
1987
                                                        0,
1988
                                                        $my_maxscore,
1989
                                                        false
1990
                                                    );
1991
                                                } else {
1992
                                                    if ($my_maxscore == 0) {
1993
                                                        $view_score = $my_score;
1994
                                                    } else {
1995
                                                        $view_score = ExerciseLib::show_score(
1996
                                                            $my_score,
1997
                                                            $my_maxscore,
1998
                                                            false
1999
                                                        );
2000
                                                    }
2001
                                                }
2002
                                            }
2003
                                            $my_lesson_status = $row_attempts['status'];
2004
                                            if ($my_lesson_status == '') {
2005
                                                $my_lesson_status = learnpathitem::humanize_status('completed');
2006
                                            } elseif ($my_lesson_status == 'incomplete') {
2007
                                                $my_lesson_status = learnpathitem::humanize_status('incomplete');
2008
                                            }
2009
                                            $timeRow = '<td class="lp_time" colspan="2">'.$time_attemp.'</td>';
2010
                                            if ($hideTime) {
2011
                                                $timeRow = '';
2012
                                            }
2013
2014
                                            $output .= '<tr class="'.$oddclass.'" >
2015
                                    <td></td>
2016
                                    <td>'.$extend_attempt_link.'</td>
2017
                                    <td colspan="3">'.get_lang('Attempt').' '.$n.'</td>
2018
                                    <td colspan="2">'.$my_lesson_status.'</td>
2019
                                    <td colspan="2">'.$view_score.'</td>
2020
                                    '.$timeRow;
2021
2022
                                            if ($action == 'classic') {
2023
                                                if ($origin != 'tracking') {
2024
                                                    if (!$is_allowed_to_edit && $result_disabled_ext_all) {
2025
                                                        $output .= '<td>
2026
                                                        <img src="'.Display::returnIconPath('quiz_na.gif').'" alt="'.get_lang('ShowAttempt').'" title="'.get_lang('ShowAttempt').'">
2027
                                                        </td>';
2028
                                                    } else {
2029
                                                        $output .= '<td>
2030
                                                        <a href="../exercise/exercise_show.php?origin='.$origin.'&id='.$my_exe_id.'&cidReq='.$courseCode.'" target="_parent">
2031
                                                        <img src="'.Display::returnIconPath('quiz.png').'" alt="'.get_lang('ShowAttempt').'" title="'.get_lang('ShowAttempt').'">
2032
                                                        </a></td>';
2033
                                                    }
2034
                                                } else {
2035
                                                    if (!$is_allowed_to_edit && $result_disabled_ext_all) {
2036
                                                        $output .= '<td>
2037
                                                            <img src="'.Display::returnIconPath('quiz_na.gif').'" alt="'.get_lang('ShowAndQualifyAttempt').'" title="'.get_lang('ShowAndQualifyAttempt').'"></td>';
2038
                                                    } else {
2039
                                                        $output .= '<td>
2040
                                                                <a href="../exercise/exercise_show.php?cidReq='.$courseCode.'&origin=correct_exercise_in_lp&id='.$my_exe_id.'" target="_parent">
2041
                                                                <img src="'.Display::returnIconPath('quiz.gif').'" alt="'.get_lang('ShowAndQualifyAttempt').'" title="'.get_lang('ShowAndQualifyAttempt').'"></a></td>';
2042
                                                    }
2043
                                                }
2044
                                            }
2045
                                            $output .= '</tr>';
2046
                                            $n++;
2047
                                        }
2048
                                    }
2049
                                } elseif (
2050
                                    'h5p' === $row['item_type']
2051
                                    && 'true' === $h5pImportEnable
2052
                                ) {
2053
                                    $em = Database::getManager();
2054
                                    $h5pImportResultsRepo = $em
2055
                                        ->getRepository('ChamiloPluginBundle:H5pImport\H5pImportResults');
2056
                                    $h5pResults = $h5pImportResultsRepo
2057
                                        ->findBy(
2058
                                            [
2059
                                                'user' => $user_id, 'cLpItemView' => $row['iv_id'],
2060
                                            ]
2061
                                        );
2062
2063
                                    if (count($h5pResults) > 0) {
2064
                                        foreach ($h5pResults as $result) {
2065
                                            $timeAttempt = ' - ';
2066
                                            if ($result->getTotalTime()) {
2067
                                                $timeAttempt = gmdate('H:i:s', $result->getTotalTime());
2068
                                            }
2069
                                            if (!$is_allowed_to_edit && $result_disabled_ext_all) {
2070
                                                $view_score = Display::return_icon(
2071
                                                    'invisible.png',
2072
                                                    get_lang(
2073
                                                        'ResultsHiddenByExerciseSetting'
2074
                                                    )
2075
                                                );
2076
                                            } else {
2077
                                                // Show only float when need it
2078
                                                if ($result->getScore() == 0) {
2079
                                                    $view_score = ExerciseLib::show_score(
2080
                                                        0,
2081
                                                        $result->getMaxScore(),
2082
                                                        false
2083
                                                    );
2084
                                                } else {
2085
                                                    if ($result->getMaxScore() == 0) {
2086
                                                        $view_score = $result->getScore();
2087
                                                    } else {
2088
                                                        $view_score = ExerciseLib::show_score(
2089
                                                            $result->getScore(),
2090
                                                            $result->getMaxScore(),
2091
                                                            false
2092
                                                        );
2093
                                                    }
2094
                                                }
2095
                                            }
2096
                                            $my_lesson_status = learnpathitem::humanize_status($row['status']);
2097
                                            $timeRow = '<td class="lp_time" colspan="2">'.$timeAttempt.'</td>';
2098
                                            if ($hideTime) {
2099
                                                $timeRow = '';
2100
                                            }
2101
2102
                                            $output .= '<tr class="'.$oddclass.'" >
2103
                                                <td></td>
2104
                                                <td>'.$extend_attempt_link.'</td>
2105
                                                <td colspan="3">'.get_lang('Attempt').' '.$n.'</td>
2106
                                                <td colspan="2">'.$my_lesson_status.'</td>
2107
                                                <td colspan="2">'.$view_score.'</td>'.$timeRow;
2108
2109
                                            if ($action == 'classic') {
2110
                                                if ($origin != 'tracking') {
2111
                                                    if (!$is_allowed_to_edit && $result_disabled_ext_all) {
2112
                                                        $output .= '<td>
2113
                                                        <img src="'.Display::returnIconPath('quiz_na.gif').'" alt="'.get_lang('ShowAttempt').'" title="'.get_lang('ShowAttempt').'">
2114
                                                        </td>';
2115
                                                    } else {
2116
                                                        $output .= '<td>
2117
                                                        <a href="../exercise/exercise_show.php?origin='.$origin.'&id='.$result->getIid().'&cidReq='.$courseCode.'" target="_parent">
2118
                                                        <img src="'.Display::returnIconPath('quiz.png').'" alt="'.get_lang('ShowAttempt').'" title="'.get_lang('ShowAttempt').'">
2119
                                                        </a></td>';
2120
                                                    }
2121
                                                } else {
2122
                                                    if (!$is_allowed_to_edit && $result_disabled_ext_all) {
2123
                                                        $output .= '<td>
2124
                                                            <img src="'.Display::returnIconPath('quiz_na.gif').'" alt="'.get_lang('ShowAndQualifyAttempt').'" title="'.get_lang('ShowAndQualifyAttempt').'"></td>';
2125
                                                    } else {
2126
                                                        $output .= '<td>
2127
                                                                <a href="../exercise/exercise_show.php?cidReq='.$courseCode.'&origin=correct_exercise_in_lp&id='.$result->getIid().'" target="_parent">
2128
                                                                <img src="'.Display::returnIconPath('quiz.gif').'" alt="'.get_lang('ShowAndQualifyAttempt').'" title="'.get_lang('ShowAndQualifyAttempt').'"></a></td>';
2129
                                                    }
2130
                                                }
2131
                                            }
2132
                                            $output .= '</tr>';
2133
                                            $n++;
2134
                                        }
2135
                                    }
2136
                                } else {
2137
                                    $output .= '<tr><td colspan="12">&nbsp;</td></tr>';
2138
                                }
2139
                            }
2140
                        }
2141
                    }
2142
                }
2143
2144
                $total_time += $time_for_total;
2145
                // QUIZZ IN LP
2146
                $a_my_id = [];
2147
                if (!empty($my_lp_id)) {
2148
                    $a_my_id[] = $my_lp_id;
2149
                }
2150
            }
2151
        }
2152
2153
        // NOT Extend all "left green cross"
2154
        if (!empty($a_my_id)) {
2155
            if ($extendedAttempt) {
2156
                // "Right green cross" extended
2157
                $total_score = self::get_avg_student_score(
2158
                    $user_id,
2159
                    $course_id,
2160
                    $a_my_id,
2161
                    $session_id,
2162
                    false,
2163
                    false
2164
                );
2165
            } else {
2166
                // "Left green cross" extended
2167
                $total_score = self::get_avg_student_score(
2168
                    $user_id,
2169
                    $course_id,
2170
                    $a_my_id,
2171
                    $session_id,
2172
                    false,
2173
                    true
2174
                );
2175
            }
2176
        } else {
2177
            // Extend all "left green cross"
2178
            $total_score = self::get_avg_student_score(
2179
                $user_id,
2180
                $course_id,
2181
                [$lp_id],
2182
                $session_id,
2183
                false,
2184
                false
2185
            );
2186
        }
2187
2188
        $total_time = learnpathItem::getScormTimeFromParameter('js', $total_time);
2189
        $total_time = str_replace('NaN', '00'.$h.'00\'00"', $total_time);
2190
2191
        if (!$is_allowed_to_edit && $result_disabled_ext_all) {
2192
            $final_score = Display::return_icon('invisible.png', get_lang('ResultsHiddenByExerciseSetting'));
2193
            $finalScoreToCsv = get_lang('ResultsHiddenByExerciseSetting');
2194
        } else {
2195
            if (is_numeric($total_score)) {
2196
                $final_score = $total_score.'%';
2197
            } else {
2198
                $final_score = $total_score;
2199
            }
2200
            $finalScoreToCsv = $final_score;
2201
        }
2202
        $progress = learnpath::getProgress($lp_id, $user_id, $course_id, $session_id);
2203
2204
        $oddclass = 'row_even';
2205
        if (($counter % 2) == 0) {
2206
            $oddclass = 'row_odd';
2207
        }
2208
2209
        $action = null;
2210
        if ('classic' === $type) {
2211
            $action = '<td></td>';
2212
        }
2213
2214
        $timeTotal = '<td class="lp_time" colspan="2">'.$total_time.'</div>';
2215
        if ($hideTime) {
2216
            $timeTotal = '';
2217
        }
2218
2219
        $output .= '<tr class="'.$oddclass.'">
2220
                <td></td>
2221
                <td colspan="4">
2222
                    <i>'.get_lang('AccomplishedStepsTotal').'</i>
2223
                </td>
2224
                <td colspan="2">'.$progress.'%</td>
2225
                <td colspan="2">'.$final_score.'</td>
2226
                '.$timeTotal.'
2227
                '.$action.'
2228
           </tr>';
2229
2230
        $output .= '
2231
                    </tbody>
2232
                </table>
2233
            </div>
2234
        ';
2235
2236
        if (!empty($export_csv)) {
2237
            $temp = [
2238
                '',
2239
                '',
2240
                '',
2241
                '',
2242
            ];
2243
            $csv_content[] = $temp;
2244
            $temp = [
2245
                get_lang('AccomplishedStepsTotal'),
2246
                '',
2247
                $finalScoreToCsv,
2248
            ];
2249
2250
            if ($hideTime === false) {
2251
                $temp[] = $total_time;
2252
            }
2253
2254
            $csv_content[] = $temp;
2255
            ob_end_clean();
2256
            Export::arrayToCsv($csv_content, 'reporting_learning_path_details');
2257
            exit;
0 ignored issues
show
Best Practice introduced by
Using exit here is not recommended.

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

Loading history...
2258
        }
2259
2260
        return $output;
2261
    }
2262
2263
    /**
2264
     * @param int  $userId
2265
     * @param bool $getCount
2266
     *
2267
     * @return array
2268
     */
2269
    public static function getStats($userId, $getCount = false)
2270
    {
2271
        $courses = [];
2272
        $students = [];
2273
        $assignedCourses = [];
2274
        $drhCount = 0;
2275
        $teachersCount = 0;
2276
        $studentBossCount = 0;
2277
        $courseCount = 0;
2278
        $assignedCourseCount = 0;
2279
        $studentCount = 0;
2280
        $checkSessionVisibility = api_get_configuration_value('show_users_in_active_sessions_in_tracking');
2281
        $allowDhrAccessToAllStudents = api_get_configuration_value('drh_allow_access_to_all_students');
2282
2283
        if (api_is_drh() && api_drh_can_access_all_session_content()) {
2284
            $studentBossesList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
2285
                'drh_all',
2286
                $userId,
2287
                $getCount,
2288
                null,
2289
                null,
2290
                null,
2291
                null,
2292
                null,
2293
                null,
2294
                null,
2295
                [],
2296
                [],
2297
                STUDENT_BOSS
2298
            );
2299
2300
            if ($getCount) {
2301
                $studentBossCount = $studentBossesList;
2302
            } else {
2303
                $studentBosses = [];
2304
                if (is_array($studentBossesList)) {
2305
                    foreach ($studentBossesList as $studentBossData) {
2306
                        $studentBosses[] = $studentBossData['user_id'];
2307
                    }
2308
                }
2309
            }
2310
2311
            $teacherList = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
2312
                'drh_all',
2313
                $userId,
2314
                $getCount,
2315
                null,
2316
                null,
2317
                null,
2318
                null,
2319
                null,
2320
                null,
2321
                null,
2322
                [],
2323
                [],
2324
                COURSEMANAGER
2325
            );
2326
2327
            if ($getCount) {
2328
                $teachersCount = $teacherList;
2329
            } else {
2330
                $teachers = [];
2331
                foreach ($teacherList as $teacherData) {
2332
                    $teachers[] = $teacherData['user_id'];
2333
                }
2334
            }
2335
2336
            $humanResources = SessionManager::getAllUsersFromCoursesFromAllSessionFromStatus(
2337
                'drh_all',
2338
                $userId,
2339
                $getCount,
2340
                null,
2341
                null,
2342
                null,
2343
                null,
2344
                null,
2345
                null,
2346
                null,
2347
                [],
2348
                [],
2349
                DRH
2350
            );
2351
2352
            if ($getCount) {
2353
                $drhCount = $humanResources;
2354
            } else {
2355
                $humanResourcesList = [];
2356
                if (is_array($humanResources)) {
2357
                    foreach ($humanResources as $item) {
2358
                        $humanResourcesList[] = $item['user_id'];
2359
                    }
2360
                }
2361
            }
2362
2363
            $platformCourses = SessionManager::getAllCoursesFollowedByUser(
2364
                $userId,
2365
                null,
2366
                null,
2367
                null,
2368
                null,
2369
                null,
2370
                $getCount
2371
            );
2372
2373
            if ($getCount) {
2374
                $courseCount = $platformCourses;
2375
            } else {
2376
                foreach ($platformCourses as $course) {
2377
                    $courses[$course['code']] = $course['code'];
2378
                }
2379
            }
2380
2381
            $sessions = SessionManager::get_sessions_followed_by_drh(
2382
                $userId,
2383
                null,
2384
                null,
2385
                false
2386
            );
2387
        } else {
2388
            if (api_is_drh() && $allowDhrAccessToAllStudents) {
2389
                $studentList = UserManager::get_user_list(
2390
                    ['status' => STUDENT],
2391
                    [],
2392
                    false,
2393
                    false,
2394
                    null,
2395
                    null,
2396
                    null,
2397
                    $getCount
2398
                );
2399
            } else {
2400
                $studentList = UserManager::getUsersFollowedByUser(
2401
                    $userId,
2402
                    STUDENT,
2403
                    false,
2404
                    false,
2405
                    $getCount,
2406
                    null,
2407
                    null,
2408
                    null,
2409
                    null,
2410
                    null,
2411
                    null,
2412
                    COURSEMANAGER,
2413
                    null,
2414
                    $checkSessionVisibility
2415
                );
2416
            }
2417
2418
            if ($getCount) {
2419
                $studentCount = (int) $studentList;
2420
            } else {
2421
                $students = [];
2422
                if (is_array($studentList)) {
2423
                    foreach ($studentList as $studentData) {
2424
                        $students[] = $studentData['user_id'];
2425
                    }
2426
                }
2427
            }
2428
2429
            $studentBossesList = UserManager::getUsersFollowedByUser(
2430
                $userId,
2431
                STUDENT_BOSS,
2432
                false,
2433
                false,
2434
                $getCount,
2435
                null,
2436
                null,
2437
                null,
2438
                null,
2439
                null,
2440
                null,
2441
                COURSEMANAGER,
2442
                null,
2443
                $checkSessionVisibility
2444
            );
2445
2446
            if ($getCount) {
2447
                $studentBossCount = $studentBossesList;
2448
            } else {
2449
                $studentBosses = [];
2450
                if (is_array($studentBossesList)) {
2451
                    foreach ($studentBossesList as $studentBossData) {
2452
                        $studentBosses[] = $studentBossData['user_id'];
2453
                    }
2454
                }
2455
            }
2456
2457
            $teacherList = UserManager::getUsersFollowedByUser(
2458
                $userId,
2459
                COURSEMANAGER,
2460
                false,
2461
                false,
2462
                $getCount,
2463
                null,
2464
                null,
2465
                null,
2466
                null,
2467
                null,
2468
                null,
2469
                COURSEMANAGER,
2470
                null,
2471
                $checkSessionVisibility
2472
            );
2473
2474
            if ($getCount) {
2475
                $teachersCount = $teacherList;
2476
            } else {
2477
                $teachers = [];
2478
                foreach ($teacherList as $teacherData) {
2479
                    $teachers[] = $teacherData['user_id'];
2480
                }
2481
            }
2482
2483
            $humanResources = UserManager::getUsersFollowedByUser(
2484
                $userId,
2485
                DRH,
2486
                false,
2487
                false,
2488
                $getCount,
2489
                null,
2490
                null,
2491
                null,
2492
                null,
2493
                null,
2494
                null,
2495
                COURSEMANAGER,
2496
                null,
2497
                $checkSessionVisibility
2498
            );
2499
2500
            if ($getCount) {
2501
                $drhCount = $humanResources;
2502
            } else {
2503
                $humanResourcesList = [];
2504
                foreach ($humanResources as $item) {
2505
                    $humanResourcesList[] = $item['user_id'];
2506
                }
2507
            }
2508
2509
            $platformCourses = CourseManager::getCoursesFollowedByUser(
2510
                $userId,
2511
                COURSEMANAGER,
2512
                null,
2513
                null,
2514
                null,
2515
                null,
2516
                $getCount,
2517
                null,
2518
                null,
2519
                true
2520
            );
2521
2522
            if ($getCount) {
2523
                $assignedCourseCount = $platformCourses;
2524
            } else {
2525
                foreach ($platformCourses as $course) {
2526
                    $assignedCourses[$course['code']] = $course['code'];
2527
                }
2528
            }
2529
2530
            $platformCourses = CourseManager::getCoursesFollowedByUser(
2531
                $userId,
2532
                COURSEMANAGER,
2533
                null,
2534
                null,
2535
                null,
2536
                null,
2537
                $getCount
2538
            );
2539
2540
            if ($getCount) {
2541
                $courseCount = $platformCourses;
2542
            } else {
2543
                foreach ($platformCourses as $course) {
2544
                    $courses[$course['code']] = $course['code'];
2545
                }
2546
            }
2547
2548
            $sessions = SessionManager::getSessionsFollowedByUser(
2549
                $userId,
2550
                COURSEMANAGER,
2551
                null,
2552
                null,
2553
                false
2554
            );
2555
        }
2556
2557
        if ($getCount) {
2558
            return [
2559
                'drh' => $drhCount,
2560
                'teachers' => $teachersCount,
2561
                'student_count' => $studentCount,
2562
                'student_list' => $students,
2563
                'student_bosses' => $studentBossCount,
2564
                'courses' => $courseCount,
2565
                'session_count' => count($sessions),
2566
                'session_list' => $sessions,
2567
                'assigned_courses' => $assignedCourseCount,
2568
            ];
2569
        }
2570
2571
        return [
2572
            'drh' => $humanResourcesList,
2573
            'teachers' => $teachers,
2574
            'student_list' => $students,
2575
            'student_bosses' => $studentBosses,
2576
            'courses' => $courses,
2577
            'sessions' => $sessions,
2578
            'assigned_courses' => $assignedCourses,
2579
        ];
2580
    }
2581
2582
    /**
2583
     * Calculates the time spent on the platform by a user.
2584
     *
2585
     * @param int|array $userId
2586
     * @param string    $timeFilter       type of time filter: 'last_week' or 'custom'
2587
     * @param string    $start_date       start date date('Y-m-d H:i:s')
2588
     * @param string    $end_date         end date date('Y-m-d H:i:s')
2589
     * @param bool      $returnAllRecords
2590
     *
2591
     * @return int
2592
     */
2593
    public static function get_time_spent_on_the_platform(
2594
        $userId,
2595
        $timeFilter = 'last_7_days',
2596
        $start_date = null,
2597
        $end_date = null,
2598
        $returnAllRecords = false
2599
    ) {
2600
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2601
        $condition_time = '';
2602
2603
        if (is_array($userId)) {
2604
            $userList = array_map('intval', $userId);
2605
            $userCondition = " login_user_id IN ('".implode("','", $userList)."')";
2606
        } else {
2607
            $userId = (int) $userId;
2608
            $userCondition = " login_user_id = $userId ";
2609
        }
2610
2611
        $url_condition = null;
2612
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2613
        $url_table = null;
2614
        if (api_is_multiple_url_enabled()) {
2615
            $access_url_id = api_get_current_access_url_id();
2616
            $url_table = ", $tbl_url_rel_user as url_users";
2617
            $url_condition = " AND u.login_user_id = url_users.user_id AND access_url_id='$access_url_id'";
2618
        }
2619
2620
        if (empty($timeFilter)) {
2621
            $timeFilter = 'last_week';
2622
        }
2623
2624
        $today = new DateTime('now', new DateTimeZone('UTC'));
2625
2626
        switch ($timeFilter) {
2627
            case 'last_7_days':
2628
                $newDate = new DateTime('-7 day', new DateTimeZone('UTC'));
2629
                $condition_time = " AND (login_date >= '{$newDate->format('Y-m-d H:i:s')}'";
2630
                $condition_time .= " AND logout_date <= '{$today->format('Y-m-d H:i:s')}') ";
2631
                break;
2632
            case 'last_30_days':
2633
                $newDate = new DateTime('-30 days', new DateTimeZone('UTC'));
2634
                $condition_time = " AND (login_date >= '{$newDate->format('Y-m-d H:i:s')}'";
2635
                $condition_time .= "AND logout_date <= '{$today->format('Y-m-d H:i:s')}') ";
2636
                break;
2637
            case 'wide':
2638
                if (!empty($start_date) && !empty($end_date)) {
2639
                    $start_date = Database::escape_string($start_date);
2640
                    $end_date = Database::escape_string($end_date);
2641
                    $condition_time = ' AND (
2642
                        (login_date >= "'.$start_date.'" AND login_date <= "'.$end_date.'") OR
2643
                        (logout_date >= "'.$start_date.'" AND logout_date <= "'.$end_date.'") OR
2644
                        (login_date <= "'.$start_date.'" AND logout_date >= "'.$end_date.'")
2645
                    ) ';
2646
                }
2647
                break;
2648
            case 'custom':
2649
                if (!empty($start_date) && !empty($end_date)) {
2650
                    $start_date = Database::escape_string($start_date);
2651
                    $end_date = Database::escape_string($end_date);
2652
                    $condition_time = ' AND (login_date >= "'.$start_date.'" AND logout_date <= "'.$end_date.'" ) ';
2653
                }
2654
                break;
2655
        }
2656
2657
        if ($returnAllRecords) {
2658
            $sql = "SELECT login_date, logout_date, TIMESTAMPDIFF(SECOND, login_date, logout_date) diff
2659
                    FROM $tbl_track_login u $url_table
2660
                    WHERE $userCondition $condition_time $url_condition
2661
                    ORDER BY login_date";
2662
            $rs = Database::query($sql);
2663
2664
            return Database::store_result($rs, 'ASSOC');
2665
        }
2666
2667
        $sql = "SELECT SUM(TIMESTAMPDIFF(SECOND, login_date, logout_date)) diff
2668
    	        FROM $tbl_track_login u $url_table
2669
                WHERE $userCondition $condition_time $url_condition";
2670
        $rs = Database::query($sql);
2671
        $row = Database::fetch_array($rs, 'ASSOC');
2672
        $diff = $row['diff'];
2673
2674
        if ($diff >= 0) {
2675
            return $diff;
2676
        }
2677
2678
        return -1;
2679
    }
2680
2681
    /**
2682
     * @param string $startDate
2683
     * @param string $endDate
2684
     *
2685
     * @return int
2686
     */
2687
    public static function getTotalTimeSpentOnThePlatform(
2688
        $startDate = '',
2689
        $endDate = ''
2690
    ) {
2691
        $tbl_track_login = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2692
        $tbl_url_rel_user = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2693
2694
        $url_table = null;
2695
        $url_condition = null;
2696
        if (api_is_multiple_url_enabled()) {
2697
            $access_url_id = api_get_current_access_url_id();
2698
            $url_table = ", ".$tbl_url_rel_user." as url_users";
2699
            $url_condition = " AND u.login_user_id = url_users.user_id AND access_url_id='$access_url_id'";
2700
        }
2701
2702
        if (!empty($startDate) && !empty($endDate)) {
2703
            $startDate = Database::escape_string($startDate);
2704
            $endDate = Database::escape_string($endDate);
2705
            $condition_time = ' (login_date >= "'.$startDate.'" AND logout_date <= "'.$endDate.'" ) ';
2706
        }
2707
        $sql = "SELECT SUM(TIMESTAMPDIFF(SECOND, login_date, logout_date)) diff
2708
    	        FROM $tbl_track_login u $url_table
2709
                WHERE $condition_time $url_condition";
2710
        $rs = Database::query($sql);
2711
        $row = Database::fetch_array($rs, 'ASSOC');
2712
        $diff = $row['diff'];
2713
2714
        if ($diff >= 0) {
2715
            return $diff;
2716
        }
2717
2718
        return -1;
2719
    }
2720
2721
    /**
2722
     * Return the total time spent in courses (no the total in platform).
2723
     *
2724
     * @return int
2725
     */
2726
    public static function getTotalTimeSpentInCourses(
2727
        string $dateFrom = '',
2728
        string $dateUntil = ''
2729
    ) {
2730
        $tableTrackLogin = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2731
        $tableUrlRelUser = Database::get_main_table(TABLE_MAIN_ACCESS_URL_REL_USER);
2732
2733
        $tableUrl = null;
2734
        $urlCondition = null;
2735
        $conditionTime = null;
2736
        if (api_is_multiple_url_enabled()) {
2737
            $accessUrlId = api_get_current_access_url_id();
2738
            $tableUrl = ", ".$tableUrlRelUser." as url_users";
2739
            $urlCondition = " AND teca.user_id = url_users.user_id AND url_users.access_url_id = $accessUrlId";
2740
        }
2741
2742
        if (!empty($dateFrom) && !empty($dateUntil)) {
2743
            $dateFrom = Database::escape_string($dateFrom);
2744
            $dateUntil = Database::escape_string($dateUntil);
2745
            $conditionTime = " (teca.login_course_date >= '$dateFrom' AND teca.logout_course_date <= '$dateUntil' ) ";
2746
        }
2747
        $sql = "SELECT SUM(TIMESTAMPDIFF(HOUR, teca.login_course_date, teca.logout_course_date)) diff
2748
    	        FROM $tableTrackLogin teca $tableUrl
2749
                WHERE $conditionTime $urlCondition";
2750
2751
        $rs = Database::query($sql);
2752
        if (Database::num_rows($rs) < 1) {
2753
            return -1;
2754
        }
2755
        $row = Database::fetch_array($rs, 'ASSOC');
2756
        $diff = $row['diff'];
2757
2758
        if (isset($diff) && $diff >= 0) {
2759
            return $diff;
2760
        }
2761
2762
        return -1;
2763
    }
2764
2765
    /**
2766
     * Checks if the "lp_minimum_time" feature is available for the course.
2767
     *
2768
     * @param int $sessionId
2769
     * @param int $courseId
2770
     *
2771
     * @return bool
2772
     */
2773
    public static function minimumTimeAvailable($sessionId, $courseId)
2774
    {
2775
        if (!api_get_configuration_value('lp_minimum_time')) {
2776
            return false;
2777
        }
2778
2779
        if (!empty($sessionId)) {
2780
            $extraFieldValue = new ExtraFieldValue('session');
2781
            $value = $extraFieldValue->get_values_by_handler_and_field_variable($sessionId, 'new_tracking_system');
2782
2783
            if ($value && isset($value['value']) && 1 == $value['value']) {
2784
                return true;
2785
            }
2786
        } else {
2787
            if ($courseId) {
2788
                $extraFieldValue = new ExtraFieldValue('course');
2789
                $value = $extraFieldValue->get_values_by_handler_and_field_variable($courseId, 'new_tracking_system');
2790
                if ($value && isset($value['value']) && 1 == $value['value']) {
2791
                    return true;
2792
                }
2793
            }
2794
        }
2795
2796
        return false;
2797
    }
2798
2799
    /**
2800
     * Calculates the time spent on the course.
2801
     *
2802
     * @param int|array<int, int> $user_id
2803
     * @param int                 $courseId
2804
     * @param int                 $session_id
2805
     * @param string              $startDate  date string
2806
     * @param string              $endDate    date string
2807
     *
2808
     * @return int Time in seconds
2809
     */
2810
    public static function get_time_spent_on_the_course(
2811
        $user_id,
2812
        $courseId,
2813
        $session_id = 0,
2814
        $startDate = null,
2815
        $endDate = null
2816
    ) {
2817
        $courseId = (int) $courseId;
2818
2819
        if (empty($courseId) || empty($user_id)) {
2820
            return 0;
2821
        }
2822
2823
        if (self::minimumTimeAvailable($session_id, $courseId)) {
2824
            $courseTime = self::getCalculateTime($user_id, $courseId, $session_id);
2825
2826
            return isset($courseTime['total_time']) ? $courseTime['total_time'] : 0;
2827
        }
2828
2829
        $conditionUser = '';
2830
        $session_id = (int) $session_id;
2831
        if (is_array($user_id)) {
2832
            $user_id = array_map('intval', $user_id);
2833
            $conditionUser = " AND user_id IN (".implode(',', $user_id).") ";
2834
        } else {
2835
            if (!empty($user_id)) {
2836
                $user_id = (int) $user_id;
2837
                $conditionUser = " AND user_id = $user_id ";
2838
            }
2839
        }
2840
2841
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2842
        $sql = "SELECT
2843
                SUM(UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) as nb_seconds
2844
                FROM $table
2845
                WHERE
2846
                    UNIX_TIMESTAMP(logout_course_date) > UNIX_TIMESTAMP(login_course_date) AND
2847
                    c_id = '$courseId' ";
2848
2849
        if (-1 != $session_id) {
2850
            $sql .= "AND session_id = '$session_id' ";
2851
        }
2852
2853
        if (!empty($startDate)) {
2854
            $startDate = api_get_utc_datetime($startDate, false, true);
2855
            $sql .= " AND login_course_date >= '".$startDate->format('Y-m-d 00:00:00')."' ";
2856
        }
2857
        if (!empty($endDate)) {
2858
            $endDate = api_get_utc_datetime($endDate, false, true);
2859
            $sql .= " AND login_course_date <= '".$endDate->format('Y-m-d 23:59:59')."' ";
2860
        }
2861
2862
        $sql .= $conditionUser;
2863
2864
        $rs = Database::query($sql);
2865
        $row = Database::fetch_array($rs);
2866
2867
        return $row['nb_seconds'];
2868
    }
2869
2870
    /**
2871
     * Get first connection date for a student.
2872
     *
2873
     * @param int $student_id
2874
     *
2875
     * @return string|bool Date format long without day or false if there are no connections
2876
     */
2877
    public static function get_first_connection_date($student_id, $dateFormat = DATE_FORMAT_SHORT)
2878
    {
2879
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2880
        $sql = 'SELECT login_date
2881
                FROM '.$table.'
2882
                WHERE login_user_id = '.intval($student_id).'
2883
                ORDER BY login_date ASC
2884
                LIMIT 0,1';
2885
2886
        $rs = Database::query($sql);
2887
        if (Database::num_rows($rs) > 0) {
2888
            if ($first_login_date = Database::result($rs, 0, 0)) {
2889
                return api_convert_and_format_date(
2890
                    $first_login_date,
2891
                    $dateFormat
2892
                );
2893
            }
2894
        }
2895
2896
        return false;
2897
    }
2898
2899
    /**
2900
     * Get las connection date for a student.
2901
     *
2902
     * @param int  $student_id
2903
     * @param bool $warning_message  Show a warning message (optional)
2904
     * @param bool $return_timestamp True for returning results in timestamp (optional)
2905
     *
2906
     * @return string|int|bool Date format long without day, false if there are no connections or
2907
     *                         timestamp if parameter $return_timestamp is true
2908
     */
2909
    public static function get_last_connection_date(
2910
        $student_id,
2911
        $warning_message = false,
2912
        $return_timestamp = false,
2913
        $dateFormat = DATE_FORMAT_SHORT
2914
    ) {
2915
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
2916
        $sql = 'SELECT login_date
2917
                FROM '.$table.'
2918
                WHERE login_user_id = '.intval($student_id).'
2919
                ORDER BY login_date
2920
                DESC LIMIT 0,1';
2921
2922
        $rs = Database::query($sql);
2923
        if (Database::num_rows($rs) > 0) {
2924
            if ($last_login_date = Database::result($rs, 0, 0)) {
2925
                $last_login_date = api_get_local_time($last_login_date);
2926
                if ($return_timestamp) {
2927
                    return api_strtotime($last_login_date, 'UTC');
2928
                } else {
2929
                    if (!$warning_message) {
2930
                        return api_format_date($last_login_date, $dateFormat);
2931
                    } else {
2932
                        $timestamp = api_strtotime($last_login_date, 'UTC');
2933
                        $currentTimestamp = time();
2934
2935
                        // If the last connection is > than 7 days, the text is red
2936
                        // 604800 = 7 days in seconds
2937
                        if ($currentTimestamp - $timestamp > 604800) {
2938
                            return '<span style="color: #F00;">'.api_format_date($last_login_date, $dateFormat).'</span>';
2939
                        } else {
2940
                            return api_format_date($last_login_date, $dateFormat);
2941
                        }
2942
                    }
2943
                }
2944
            }
2945
        }
2946
2947
        return false;
2948
    }
2949
2950
    /**
2951
     * Get first user's connection date on the course.
2952
     *
2953
     * @param int User id
2954
     * @param int $courseId
2955
     * @param int Session id (optional, default=0)
2956
     * @param bool $convert_date
2957
     *
2958
     * @return string|bool Date with format long without day or false if there is no date
2959
     */
2960
    public static function get_first_connection_date_on_the_course(
2961
        $student_id,
2962
        $courseId,
2963
        $session_id = 0,
2964
        $convert_date = true
2965
    ) {
2966
        $student_id = (int) $student_id;
2967
        $courseId = (int) $courseId;
2968
        $session_id = (int) $session_id;
2969
2970
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2971
        $sql = 'SELECT login_course_date
2972
                FROM '.$table.'
2973
                WHERE
2974
                    user_id = '.$student_id.' AND
2975
                    c_id = '.$courseId.' AND
2976
                    session_id = '.$session_id.'
2977
                ORDER BY login_course_date ASC
2978
                LIMIT 0,1';
2979
        $rs = Database::query($sql);
2980
        if (Database::num_rows($rs) > 0) {
2981
            if ($first_login_date = Database::result($rs, 0, 0)) {
2982
                if (empty($first_login_date)) {
2983
                    return false;
2984
                }
2985
2986
                if ($convert_date) {
2987
                    return api_convert_and_format_date(
2988
                        $first_login_date,
2989
                        DATE_FORMAT_SHORT
2990
                    );
2991
                }
2992
2993
                return $first_login_date;
2994
            }
2995
        }
2996
2997
        return false;
2998
    }
2999
3000
    /**
3001
     * Get last user's connection date on the course.
3002
     *
3003
     * @param int         User id
3004
     * @param array $courseInfo real_id and code are used
3005
     * @param int            Session id (optional, default=0)
3006
     * @param bool $convert_date
3007
     *
3008
     * @return string|bool Date with format long without day or false if there is no date
3009
     */
3010
    public static function get_last_connection_date_on_the_course(
3011
        $student_id,
3012
        $courseInfo,
3013
        $session_id = 0,
3014
        $convert_date = true
3015
    ) {
3016
        // protect data
3017
        $student_id = (int) $student_id;
3018
        $session_id = (int) $session_id;
3019
3020
        if (empty($courseInfo) || empty($student_id)) {
3021
            return false;
3022
        }
3023
3024
        $courseId = $courseInfo['real_id'];
3025
3026
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
3027
3028
        if (self::minimumTimeAvailable($session_id, $courseId)) {
3029
            // Show the last date on which the user acceed the session when it was active
3030
            $where_condition = '';
3031
            $userInfo = api_get_user_info($student_id);
3032
            if (STUDENT == $userInfo['status'] && !empty($session_id)) {
3033
                // fin de acceso a la sesión
3034
                $sessionInfo = SessionManager::fetch($session_id);
3035
                $last_access = $sessionInfo['access_end_date'];
3036
                if (!empty($last_access)) {
3037
                    $where_condition = ' AND logout_course_date < "'.$last_access.'" ';
3038
                }
3039
            }
3040
            $sql = "SELECT logout_course_date
3041
                    FROM $table
3042
                    WHERE   user_id = $student_id AND
3043
                            c_id = $courseId AND
3044
                            session_id = $session_id $where_condition
3045
                    ORDER BY logout_course_date DESC
3046
                    LIMIT 0,1";
3047
3048
            $rs = Database::query($sql);
3049
            if (Database::num_rows($rs) > 0) {
3050
                if ($last_login_date = Database::result($rs, 0, 0)) {
3051
                    if (empty($last_login_date)) {
3052
                        return false;
3053
                    }
3054
                    if ($convert_date) {
3055
                        return api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
3056
                    }
3057
3058
                    return $last_login_date;
3059
                }
3060
            }
3061
        } else {
3062
            $sql = "SELECT logout_course_date
3063
                    FROM $table
3064
                    WHERE   user_id = $student_id AND
3065
                            c_id = $courseId AND
3066
                            session_id = $session_id
3067
                    ORDER BY logout_course_date DESC
3068
                    LIMIT 0,1";
3069
3070
            $rs = Database::query($sql);
3071
            if (Database::num_rows($rs) > 0) {
3072
                if ($last_login_date = Database::result($rs, 0, 0)) {
3073
                    if (empty($last_login_date)) {
3074
                        return false;
3075
                    }
3076
                    // see #5736
3077
                    $last_login_date_timestamp = api_strtotime($last_login_date);
3078
                    $now = time();
3079
                    // If the last connection is > than 7 days, the text is red
3080
                    // 604800 = 7 days in seconds
3081
                    if ($now - $last_login_date_timestamp > 604800) {
3082
                        if ($convert_date) {
3083
                            $last_login_date = api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
3084
                            $icon = null;
3085
                            if (api_is_allowed_to_edit()) {
3086
                                $url = api_get_path(WEB_CODE_PATH).
3087
                                    'announcements/announcements.php?action=add&remind_inactive='.$student_id.'&cidReq='.$courseInfo['code'];
3088
                                $icon = '<a href="'.$url.'" title="'.get_lang('RemindInactiveUser').'">
3089
                                  '.Display::return_icon('messagebox_warning.gif').'
3090
                                 </a>';
3091
                            }
3092
3093
                            return $icon.Display::label($last_login_date, 'warning');
3094
                        }
3095
3096
                        return $last_login_date;
3097
                    } else {
3098
                        if ($convert_date) {
3099
                            return api_convert_and_format_date($last_login_date, DATE_FORMAT_SHORT);
3100
                        }
3101
3102
                        return $last_login_date;
3103
                    }
3104
                }
3105
            }
3106
        }
3107
3108
        return false;
3109
    }
3110
3111
    public static function getLastConnectionInAnyCourse($studentId)
3112
    {
3113
        $studentId = (int) $studentId;
3114
3115
        if (empty($studentId)) {
3116
            return false;
3117
        }
3118
3119
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
3120
        $sql = "SELECT logout_course_date
3121
                FROM $table
3122
                WHERE user_id = $studentId
3123
                ORDER BY logout_course_date DESC
3124
                LIMIT 1";
3125
        $result = Database::query($sql);
3126
        if (Database::num_rows($result)) {
3127
            $row = Database::fetch_array($result);
3128
3129
            return $row['logout_course_date'];
3130
        }
3131
3132
        return false;
3133
    }
3134
3135
    /**
3136
     * Get last course access by course/session.
3137
     */
3138
    public static function getLastConnectionDateByCourse($courseId, $sessionId = 0)
3139
    {
3140
        $courseId = (int) $courseId;
3141
        $sessionId = (int) $sessionId;
3142
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
3143
3144
        $sql = "SELECT logout_course_date
3145
                FROM $table
3146
                WHERE
3147
                        c_id = $courseId AND
3148
                        session_id = $sessionId
3149
                ORDER BY logout_course_date DESC
3150
                LIMIT 0,1";
3151
3152
        $result = Database::query($sql);
3153
        if (Database::num_rows($result)) {
3154
            $row = Database::fetch_array($result);
3155
            if ($row) {
3156
                return $row['logout_course_date'];
3157
            }
3158
        }
3159
3160
        return '';
3161
    }
3162
3163
    /**
3164
     * Get count of the connections to the course during a specified period.
3165
     *
3166
     * @param int $courseId
3167
     * @param   int     Session id (optional)
3168
     * @param   int     Datetime from which to collect data (defaults to 0)
3169
     * @param   int     Datetime to which to collect data (defaults to now)
3170
     *
3171
     * @return int count connections
3172
     */
3173
    public static function get_course_connections_count(
3174
        $courseId,
3175
        $session_id = 0,
3176
        $start = 0,
3177
        $stop = null
3178
    ) {
3179
        if ($start < 0) {
3180
            $start = 0;
3181
        }
3182
        if (!isset($stop) || $stop < 0) {
3183
            $stop = api_get_utc_datetime();
3184
        }
3185
3186
        // Given we're storing in cache, round the start and end times
3187
        // to the lower minute
3188
        $roundedStart = substr($start, 0, -2).'00';
3189
        $roundedStop = substr($stop, 0, -2).'00';
3190
        $roundedStart = Database::escape_string($roundedStart);
3191
        $roundedStop = Database::escape_string($roundedStop);
3192
        $month_filter = " AND login_course_date > '$roundedStart' AND login_course_date < '$roundedStop' ";
3193
        $courseId = (int) $courseId;
3194
        $session_id = (int) $session_id;
3195
        $count = 0;
3196
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
3197
        $sql = "SELECT count(*) as count_connections
3198
                FROM $table
3199
                WHERE
3200
                    c_id = $courseId AND
3201
                    session_id = $session_id
3202
                    $month_filter";
3203
3204
        // This query can be very slow (several seconds on an indexed table
3205
        // with 14M rows). As such, we'll try to use APCu if it is
3206
        // available to store the resulting value for a few seconds
3207
        $cacheAvailable = api_get_configuration_value('apc');
3208
        if ($cacheAvailable === true) {
3209
            $apc = apcu_cache_info(true);
3210
            $apc_end = $apc['start_time'] + $apc['ttl'];
3211
            $apc_var = api_get_configuration_value('apc_prefix').'course_access_'.$courseId.'_'.$session_id.'_'.strtotime($roundedStart).'_'.strtotime($roundedStop);
3212
            if (apcu_exists($apc_var) && (time() < $apc_end)
3213
                && apcu_fetch($apc_var) > 0
3214
            ) {
3215
                $count = apcu_fetch($apc_var);
3216
            } else {
3217
                $rs = Database::query($sql);
3218
                if (Database::num_rows($rs) > 0) {
3219
                    $row = Database::fetch_object($rs);
3220
                    $count = $row->count_connections;
3221
                }
3222
                apcu_clear_cache();
3223
                apcu_store($apc_var, $count, 60);
3224
            }
3225
        } else {
3226
            $rs = Database::query($sql);
3227
            if (Database::num_rows($rs) > 0) {
3228
                $row = Database::fetch_object($rs);
3229
                $count = $row->count_connections;
3230
            }
3231
        }
3232
3233
        return $count;
3234
    }
3235
3236
    /**
3237
     * Get count courses per student.
3238
     *
3239
     * @param int  $user_id          Student id
3240
     * @param bool $include_sessions Include sessions (optional)
3241
     *
3242
     * @return int count courses
3243
     */
3244
    public static function count_course_per_student($user_id, $include_sessions = true)
3245
    {
3246
        $user_id = (int) $user_id;
3247
        $tbl_course_rel_user = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3248
        $tbl_session_course_rel_user = Database::get_main_table(TABLE_MAIN_SESSION_COURSE_USER);
3249
3250
        $sql = 'SELECT DISTINCT c_id
3251
                FROM '.$tbl_course_rel_user.'
3252
                WHERE user_id = '.$user_id.' AND relation_type<>'.COURSE_RELATION_TYPE_RRHH;
3253
        $rs = Database::query($sql);
3254
        $nb_courses = Database::num_rows($rs);
3255
3256
        if ($include_sessions) {
3257
            $sql = 'SELECT DISTINCT c_id
3258
                    FROM '.$tbl_session_course_rel_user.'
3259
                    WHERE user_id = '.$user_id;
3260
            $rs = Database::query($sql);
3261
            $nb_courses += Database::num_rows($rs);
3262
        }
3263
3264
        return $nb_courses;
3265
    }
3266
3267
    public static function countSessionsPerStudent(int $userId): int
3268
    {
3269
        $tblSessionUser = Database::get_main_table(TABLE_MAIN_SESSION_USER);
3270
3271
        $sql = 'SELECT DISTINCT id
3272
            FROM '.$tblSessionUser.'
3273
            WHERE relation_type = '.SessionEntity::STUDENT.' AND user_id = '.$userId;
3274
3275
        $rs = Database::query($sql);
3276
3277
        return Database::num_rows($rs);
3278
    }
3279
3280
    /**
3281
     * Gets the score average from all tests in a course by student.
3282
     *
3283
     * @param $student_id
3284
     * @param $course_code
3285
     * @param int  $exercise_id
3286
     * @param null $session_id
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $session_id is correct as it would always require null to be passed?
Loading history...
3287
     * @param int  $active_filter 2 for consider all tests
3288
     *                            1 for active <> -1
3289
     *                            0 for active <> 0
3290
     * @param int  $into_lp       1 for all exercises
3291
     *                            0 for without LP
3292
     * @param mixed id
3293
     * @param string code
3294
     * @param int id (optional), filtered by exercise
3295
     * @param int id (optional), if param $session_id is null
3296
     *                                                it'll return results including sessions, 0 = session is not
3297
     *                                                filtered
3298
     *
3299
     * @return string value (number %) Which represents a round integer about the score average
3300
     */
3301
    public static function get_avg_student_exercise_score(
3302
        $student_id,
3303
        $course_code,
3304
        $exercise_id = 0,
3305
        $session_id = null,
3306
        $active_filter = 1,
3307
        $into_lp = 0
3308
    ) {
3309
        $course_code = Database::escape_string($course_code);
3310
        $course_info = api_get_course_info($course_code);
3311
        if (!empty($course_info)) {
3312
            // table definition
3313
            $tbl_course_quiz = Database::get_course_table(TABLE_QUIZ_TEST);
3314
            $tbl_stats_exercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3315
3316
            // Compose a filter based on optional exercise given
3317
            $condition_quiz = "";
3318
            if (!empty($exercise_id)) {
3319
                $exercise_id = intval($exercise_id);
3320
                $condition_quiz = " AND iid = $exercise_id ";
3321
            }
3322
3323
            // Compose a filter based on optional session id given
3324
            $condition_session = '';
3325
            if (isset($session_id)) {
3326
                $session_id = intval($session_id);
3327
                $condition_session = " AND session_id = $session_id ";
3328
            }
3329
            if ($active_filter == 1) {
3330
                $condition_active = 'AND active <> -1';
3331
            } elseif ($active_filter == 0) {
3332
                $condition_active = 'AND active <> 0';
3333
            } else {
3334
                $condition_active = '';
3335
            }
3336
            $condition_into_lp = '';
3337
            $select_lp_id = '';
3338
            if ($into_lp == 0) {
3339
                $condition_into_lp = 'AND orig_lp_id = 0 AND orig_lp_item_id = 0';
3340
            } else {
3341
                $select_lp_id = ', orig_lp_id as lp_id ';
3342
            }
3343
3344
            $sql = "SELECT count(iid)
3345
    		        FROM $tbl_course_quiz
3346
    				WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz ";
3347
            $count_quiz = 0;
3348
            $countQuizResult = Database::query($sql);
3349
            if (!empty($countQuizResult)) {
3350
                $count_quiz = Database::fetch_row($countQuizResult);
3351
            }
3352
3353
            if (!empty($count_quiz[0]) && !empty($student_id)) {
3354
                if (is_array($student_id)) {
3355
                    $student_id = array_map('intval', $student_id);
3356
                    $condition_user = " AND exe_user_id IN (".implode(',', $student_id).") ";
3357
                } else {
3358
                    $student_id = intval($student_id);
3359
                    $condition_user = " AND exe_user_id = '$student_id' ";
3360
                }
3361
3362
                if (empty($exercise_id)) {
3363
                    $sql = "SELECT iid FROM $tbl_course_quiz
3364
                            WHERE c_id = {$course_info['real_id']} $condition_active $condition_quiz";
3365
                    $result = Database::query($sql);
3366
                    $exercise_list = [];
3367
                    $exercise_id = null;
3368
                    if (!empty($result) && Database::num_rows($result)) {
3369
                        while ($row = Database::fetch_array($result)) {
3370
                            $exercise_list[] = $row['iid'];
3371
                        }
3372
                    }
3373
                    if (!empty($exercise_list)) {
3374
                        $exercise_id = implode("','", $exercise_list);
3375
                    }
3376
                }
3377
3378
                $count_quiz = Database::fetch_row(Database::query($sql));
3379
                $sql = "SELECT
3380
                        SUM(exe_result/exe_weighting*100) as avg_score,
3381
                        COUNT(*) as num_attempts
3382
                        $select_lp_id
3383
                        FROM $tbl_stats_exercise
3384
                        WHERE
3385
                            exe_exo_id IN ('".$exercise_id."')
3386
                            $condition_user AND
3387
                            status = '' AND
3388
                            c_id = {$course_info['real_id']}
3389
                            $condition_session
3390
                            $condition_into_lp
3391
                        ORDER BY exe_date DESC";
3392
3393
                $res = Database::query($sql);
3394
                $row = Database::fetch_array($res);
3395
                $quiz_avg_score = null;
3396
3397
                if (!empty($row['avg_score'])) {
3398
                    $quiz_avg_score = round($row['avg_score'], 2);
3399
                }
3400
3401
                if (!empty($row['num_attempts'])) {
3402
                    $quiz_avg_score = round($quiz_avg_score / $row['num_attempts'], 2);
3403
                }
3404
                if (is_array($student_id)) {
3405
                    $quiz_avg_score = round($quiz_avg_score / count($student_id), 2);
3406
                }
3407
                if ($into_lp == 0) {
3408
                    return $quiz_avg_score;
3409
                } else {
3410
                    if (!empty($row['lp_id'])) {
3411
                        $tbl_lp = Database::get_course_table(TABLE_LP_MAIN);
3412
                        $tbl_course = Database::get_main_table(TABLE_MAIN_COURSE);
3413
                        $sql = "SELECT lp.name
3414
                                FROM $tbl_lp as lp, $tbl_course as c
3415
                                WHERE
3416
                                    c.code = '$course_code' AND
3417
                                    lp.id = ".$row['lp_id']." AND
3418
                                    lp.c_id = c.id
3419
                                LIMIT 1;
3420
                        ";
3421
                        $result = Database::query($sql);
3422
                        $row_lp = Database::fetch_row($result);
3423
                        $lp_name = null;
3424
                        if ($row_lp && isset($row_lp[0])) {
3425
                            $lp_name = $row_lp[0];
3426
                        }
3427
3428
                        return [$quiz_avg_score, $lp_name];
3429
                    } else {
3430
                        return [$quiz_avg_score, null];
3431
                    }
3432
                }
3433
            }
3434
        }
3435
3436
        return null;
3437
    }
3438
3439
    /**
3440
     * Get count student's exercise COMPLETED attempts.
3441
     *
3442
     * @param int $student_id
3443
     * @param int $courseId
3444
     * @param int $exercise_id
3445
     * @param int $lp_id
3446
     * @param int $lp_item_id
3447
     * @param int $session_id
3448
     * @param int $find_all_lp 0 = just LP specified
3449
     *                         1 = LP specified or whitout LP,
3450
     *                         2 = all rows
3451
     *
3452
     * @internal param \Student $int id
3453
     * @internal param \Course $string code
3454
     * @internal param \Exercise $int id
3455
     * @internal param \Learning $int path id (optional),
3456
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required
3457
     * @internal param \Learning $int path item id (optional),
3458
     * for showing attempts inside a learning path $lp_id and $lp_item_id params are required
3459
     *
3460
     * @return int count of attempts
3461
     */
3462
    public static function count_student_exercise_attempts(
3463
        $student_id,
3464
        $courseId,
3465
        $exercise_id,
3466
        $lp_id = 0,
3467
        $lp_item_id = 0,
3468
        $session_id = 0,
3469
        $find_all_lp = 0
3470
    ) {
3471
        $courseId = intval($courseId);
3472
        $student_id = intval($student_id);
3473
        $exercise_id = intval($exercise_id);
3474
        $session_id = intval($session_id);
3475
3476
        $lp_id = intval($lp_id);
3477
        $lp_item_id = intval($lp_item_id);
3478
        $tbl_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3479
3480
        $sql = "SELECT COUNT(ex.exe_id) as essais
3481
                FROM $tbl_stats_exercises AS ex
3482
                WHERE
3483
                    ex.c_id = $courseId AND
3484
                    ex.exe_exo_id = $exercise_id AND
3485
                    status = '' AND
3486
                    exe_user_id= $student_id AND
3487
                    session_id = $session_id ";
3488
3489
        if ($find_all_lp == 1) {
3490
            $sql .= "AND (orig_lp_id = $lp_id OR orig_lp_id = 0)
3491
                AND (orig_lp_item_id = $lp_item_id OR orig_lp_item_id = 0)";
3492
        } elseif ($find_all_lp == 0) {
3493
            $sql .= "AND orig_lp_id = $lp_id
3494
                AND orig_lp_item_id = $lp_item_id";
3495
        }
3496
3497
        $rs = Database::query($sql);
3498
        $row = Database::fetch_row($rs);
3499
        $count_attempts = $row[0];
3500
3501
        return $count_attempts;
3502
    }
3503
3504
    /**
3505
     * It gets the last finalization date of learnpaths in a course.
3506
     *
3507
     * @return string finalization date formatted or false if it is empty.
3508
     */
3509
    public static function getCourseLpFinalizationDate(
3510
        int $userId,
3511
        int $courseId,
3512
        int $sessionId,
3513
        bool $convertDate = true
3514
    ) {
3515
        $tblLpView = Database::get_course_table(TABLE_LP_VIEW);
3516
        $tblLpItem = Database::get_course_table(TABLE_LP_ITEM);
3517
        $tblLpItemView = Database::get_course_table(TABLE_LP_ITEM_VIEW);
3518
3519
        $sql = "SELECT FROM_UNIXTIME(liv.start_time) as start_date
3520
                FROM $tblLpItemView liv
3521
                INNER JOIN
3522
                    $tblLpView lv ON lv.iid = liv.lp_view_id
3523
                INNER JOIN
3524
                    $tblLpItem li ON li.iid = liv.lp_item_id
3525
                WHERE
3526
                    lv.user_id = $userId AND
3527
                    lv.c_id = $courseId AND
3528
                    lv.session_id = $sessionId AND
3529
                    li.item_type = '".TOOL_LP_FINAL_ITEM."' AND
3530
                    liv.status = 'completed'
3531
                ORDER BY start_date DESC
3532
                LIMIT 1";
3533
3534
        $rs = Database::query($sql);
3535
        $lpFinalDate = Database::result($rs, 0, 0);
3536
3537
        if (empty($lpFinalDate)) {
3538
            return false;
3539
        }
3540
3541
        if ($convertDate) {
3542
            return api_convert_and_format_date($lpFinalDate, DATE_FORMAT_SHORT);
3543
        }
3544
3545
        return $lpFinalDate;
3546
    }
3547
3548
    /**
3549
     * It gets the last finalization date of exercises in a course.
3550
     *
3551
     * @return string finalization date formatted or false if it is empty.
3552
     */
3553
    public static function getCourseQuizLastFinalizationDate(
3554
        int $userId,
3555
        int $courseId,
3556
        int $sessionId,
3557
        bool $convertDate = true
3558
    ) {
3559
        $tblTrackExercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3560
3561
        $sql = "SELECT ex.exe_date
3562
                FROM $tblTrackExercise AS ex
3563
                WHERE
3564
                    ex.c_id = $courseId AND
3565
                    ex.session_id  = $sessionId AND
3566
                    ex.exe_user_id = $userId AND
3567
                    ex.status = ''
3568
                ORDER BY ex.exe_date DESC
3569
                LIMIT 1";
3570
        $rs = Database::query($sql);
3571
        $exeDate = Database::result($rs, 0, 0);
3572
3573
        if (empty($exeDate)) {
3574
            return false;
3575
        }
3576
3577
        if ($convertDate) {
3578
            return api_convert_and_format_date($exeDate, DATE_FORMAT_SHORT);
3579
        }
3580
3581
        return $exeDate;
3582
    }
3583
3584
    /**
3585
     * Get count student's exercise progress.
3586
     *
3587
     * @param array $exercise_list
3588
     * @param int   $user_id
3589
     * @param int   $courseId
3590
     * @param int   $session_id
3591
     *
3592
     * @return string
3593
     */
3594
    public static function get_exercise_student_progress(
3595
        $exercise_list,
3596
        $user_id,
3597
        $courseId,
3598
        $session_id
3599
    ) {
3600
        $courseId = (int) $courseId;
3601
        $user_id = (int) $user_id;
3602
        $session_id = (int) $session_id;
3603
3604
        if (empty($exercise_list)) {
3605
            return '0%';
3606
        }
3607
        $tbl_stats_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3608
        $exercise_list = array_keys($exercise_list);
3609
        $exercise_list = array_map('intval', $exercise_list);
3610
3611
        $exercise_list_imploded = implode("' ,'", $exercise_list);
3612
3613
        $sql = "SELECT COUNT(DISTINCT ex.exe_exo_id)
3614
                FROM $tbl_stats_exercises AS ex
3615
                WHERE
3616
                    ex.c_id = $courseId AND
3617
                    ex.session_id  = $session_id AND
3618
                    ex.exe_user_id = $user_id AND
3619
                    ex.status = '' AND
3620
                    ex.exe_exo_id IN ('$exercise_list_imploded') ";
3621
3622
        $rs = Database::query($sql);
3623
        $count = 0;
3624
        if ($rs) {
3625
            $row = Database::fetch_row($rs);
3626
            $count = $row[0];
3627
        }
3628
        $count = ($count != 0) ? 100 * round(intval($count) / count($exercise_list), 2).'%' : '0%';
3629
3630
        return $count;
3631
    }
3632
3633
    /**
3634
     * @param array $exercise_list
3635
     * @param int   $user_id
3636
     * @param int   $courseId
3637
     * @param int   $session_id
3638
     *
3639
     * @return string
3640
     */
3641
    public static function get_exercise_student_average_best_attempt(
3642
        $exercise_list,
3643
        $user_id,
3644
        $courseId,
3645
        $session_id
3646
    ) {
3647
        $result = 0;
3648
        if (!empty($exercise_list)) {
3649
            foreach ($exercise_list as $exercise_data) {
3650
                $exercise_id = $exercise_data['iid'];
3651
                $best_attempt = Event::get_best_attempt_exercise_results_per_user(
3652
                    $user_id,
3653
                    $exercise_id,
3654
                    $courseId,
3655
                    $session_id
3656
                );
3657
3658
                if (!empty($best_attempt) && !empty($best_attempt['exe_weighting'])) {
3659
                    $result += $best_attempt['exe_result'] / $best_attempt['exe_weighting'];
3660
                }
3661
            }
3662
            $result = $result / count($exercise_list);
3663
            $result = round($result, 2) * 100;
3664
        }
3665
3666
        return $result.'%';
3667
    }
3668
3669
    /**
3670
     * Returns the average student progress in the learning paths of the given
3671
     * course, it will take into account the progress that were not started.
3672
     *
3673
     * @param int|array $studentId
3674
     * @param string    $courseCode
3675
     * @param array     $lpIdList        Limit average to listed lp ids
3676
     * @param int       $sessionId       Session id (optional),
3677
     *                                   if parameter $session_id is null(default) it'll return results including
3678
     *                                   sessions, 0 = session is not filtered
3679
     * @param bool      $returnArray     Will return an array of the type:
3680
     *                                   [sum_of_progresses, number] if it is set to true
3681
     * @param bool      $onlySeriousGame Optional. Limit average to lp on seriousgame mode
3682
     * @param bool      $maxInsteadAvg   Optional. It will return the max progress instead the average
3683
     *
3684
     * @return float Average or max progress of the user in this course from 0 to 100
3685
     */
3686
    public static function get_avg_student_progress(
3687
        $studentId,
3688
        $courseCode = null,
3689
        $lpIdList = [],
3690
        $sessionId = null,
3691
        $returnArray = false,
3692
        $onlySeriousGame = false,
3693
        $maxInsteadAvg = false,
3694
        $startDate = null,
3695
        $endDate = null
3696
    ) {
3697
        // If there is at least one learning path and one student.
3698
        if (empty($studentId)) {
3699
            return false;
3700
        }
3701
3702
        $sessionId = (int) $sessionId;
3703
        $courseInfo = api_get_course_info($courseCode);
3704
3705
        if (empty($courseInfo)) {
3706
            return false;
3707
        }
3708
3709
        $lPTable = Database::get_course_table(TABLE_LP_MAIN);
3710
        $lpViewTable = Database::get_course_table(TABLE_LP_VIEW);
3711
        $lpConditions = [];
3712
        $lpConditions['c_id = ? '] = $courseInfo['real_id'];
3713
3714
        if ($sessionId > 0) {
3715
            $lpConditions['AND (session_id = ? OR session_id = 0 OR session_id IS NULL)'] = $sessionId;
3716
        } else {
3717
            $lpConditions['AND session_id = ?'] = $sessionId;
3718
        }
3719
3720
        if (is_array($lpIdList) && count($lpIdList) > 0) {
3721
            $placeHolders = [];
3722
            for ($i = 0; $i < count($lpIdList); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

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