Passed
Push — master ( 447a4c...2f567c )
by Julito
09:40
created

Event::get_count_exercises_attempted_by_course()   A

Complexity

Conditions 2
Paths 2

Size

Total Lines 24
Code Lines 12

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 2
eloc 12
nc 2
nop 2
dl 0
loc 24
rs 9.8666
c 0
b 0
f 0
1
<?php
2
/* See license terms in /license.txt */
3
4
//use Chamilo\UserBundle\Entity\User;
5
use Chamilo\CoreBundle\Component\Utils\ChamiloApi;
6
use ChamiloSession as Session;
7
8
/**
9
 * Class Event
10
 * Functions of this library are used to record informations when some kind
11
 * of event occur. Each event has his own types of informations then each event
12
 * use its own function.
13
 */
14
class Event
15
{
16
    /**
17
     * @author Sebastien Piraux <[email protected]> old code
18
     * @author Julio Montoya
19
     *
20
     * @param int $userId
21
     *
22
     * @return bool
23
     * @desc Record information for login event when an user identifies himself with username & password
24
     */
25
    public static function eventLogin($userId)
26
    {
27
        $userInfo = api_get_user_info($userId);
28
        $userId = (int) $userId;
29
30
        if (empty($userInfo)) {
31
            return false;
32
        }
33
34
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LOGIN);
35
        $reallyNow = api_get_utc_datetime();
36
        $userIp = Database::escape_string(api_get_real_ip());
37
38
        $sql = "INSERT INTO $table (login_user_id, user_ip, login_date, logout_date) VALUES
39
                    ($userId,
40
                    '$userIp',
41
                    '$reallyNow',
42
                    '$reallyNow'
43
                )";
44
        Database::query($sql);
45
46
        // Auto subscribe
47
        $user_status = $userInfo['status'] == SESSIONADMIN ? 'sessionadmin' : $userInfo['status'] == COURSEMANAGER ? 'teacher' : $userInfo['status'] == DRH ? 'DRH' : 'student';
48
        $autoSubscribe = api_get_setting($user_status.'_autosubscribe');
49
        if ($autoSubscribe) {
50
            $autoSubscribe = explode('|', $autoSubscribe);
51
            foreach ($autoSubscribe as $code) {
52
                if (CourseManager::course_exists($code)) {
53
                    CourseManager::subscribeUser($userId, $code);
54
                }
55
            }
56
        }
57
58
        return true;
59
    }
60
61
    /**
62
     * @author Sebastien Piraux <[email protected]>
63
     * @desc Record information for access event for courses
64
     */
65
    public static function accessCourse()
66
    {
67
        if (Session::read('login_as')) {
68
            return false;
69
        }
70
71
        $TABLETRACK_ACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
72
        //for "what's new" notification
73
        $TABLETRACK_LASTACCESS = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
74
75
        $id_session = api_get_session_id();
76
        $now = api_get_utc_datetime();
77
        $courseId = api_get_course_int_id();
78
        $userId = api_get_user_id();
79
        $ip = Database::escape_string(api_get_real_ip());
80
81
        if ($userId) {
82
            $userId = $userId;
83
        } else {
84
            $userId = "0"; // no one
85
        }
86
        $sql = "INSERT INTO $TABLETRACK_ACCESS  (user_ip, access_user_id, c_id, access_date, access_session_id) 
87
                VALUES ('$ip', $userId, $courseId, '$now', $id_session)";
88
89
        Database::query($sql);
90
91
        // added for "what's new" notification
92
        $sql = "UPDATE $TABLETRACK_LASTACCESS  SET access_date = '$now'
93
                WHERE 
94
                  access_user_id = $userId AND
95
                  c_id = $courseId AND 
96
                  access_tool IS NULL AND 
97
                  access_session_id = $id_session";
98
        $result = Database::query($sql);
99
100
        if (Database::affected_rows($result) == 0) {
101
            $sql = "INSERT INTO $TABLETRACK_LASTACCESS (access_user_id, c_id, access_date, access_session_id)
102
                    VALUES ($userId, $courseId, '$now', $id_session)";
103
            Database::query($sql);
104
        }
105
106
        return 1;
107
    }
108
109
    /**
110
     * @param string $tool name of the tool
111
     *
112
     * @author Sebastien Piraux <[email protected]>
113
     * @desc Record information for access event for tools
114
     *
115
     *  $tool can take this values :
116
     *  Links, Calendar, Document, Announcements,
117
     *  Group, Video, Works, Users, Exercises, Course Desc
118
     *  ...
119
     *  Values can be added if new modules are created (15char max)
120
     *  I encourage to use $nameTool as $tool when calling this function
121
     *
122
     * Functionality for "what's new" notification is added by Toon Van Hoecke
123
     *
124
     * @return bool
125
     */
126
    public static function event_access_tool($tool)
127
    {
128
        if (Session::read('login_as')) {
129
            return false;
130
        }
131
132
        $tool = Database::escape_string($tool);
133
134
        if (empty($tool)) {
135
            return false;
136
        }
137
138
        $courseInfo = api_get_course_info();
139
        $sessionId = api_get_session_id();
140
        $reallyNow = api_get_utc_datetime();
141
        $userId = api_get_user_id();
142
143
        if (empty($courseInfo)) {
144
            return false;
145
        }
146
        $courseId = $courseInfo['real_id'];
147
148
        $tableAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ACCESS);
149
        //for "what's new" notification
150
        $tableLastAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LASTACCESS);
151
152
        // record information
153
        // only if user comes from the course $_cid
154
        //if( eregi($_configuration['root_web'].$_cid,$_SERVER['HTTP_REFERER'] ) )
155
        //$pos = strpos($_SERVER['HTTP_REFERER'],$_configuration['root_web'].$_cid);
156
        $coursePath = isset($courseInfo['path']) ? $courseInfo['path'] : null;
157
158
        $pos = isset($_SERVER['HTTP_REFERER']) ? strpos(strtolower($_SERVER['HTTP_REFERER']), strtolower(api_get_path(WEB_COURSE_PATH).$coursePath)) : false;
159
        // added for "what's new" notification
160
        $pos2 = isset($_SERVER['HTTP_REFERER']) ? strpos(strtolower($_SERVER['HTTP_REFERER']), strtolower(api_get_path(WEB_PATH)."index")) : false;
161
162
        // end "what's new" notification
163
        if ($pos !== false || $pos2 !== false) {
164
            $params = [
165
                'access_user_id' => $userId,
166
                'c_id' => $courseId,
167
                'access_tool' => $tool,
168
                'access_date' => $reallyNow,
169
                'access_session_id' => $sessionId,
170
                'user_ip' => Database::escape_string(api_get_real_ip()),
171
            ];
172
            Database::insert($tableAccess, $params);
173
        }
174
175
        // "what's new" notification
176
        $sql = "UPDATE $tableLastAccess
177
                SET access_date = '$reallyNow'
178
                WHERE 
179
                    access_user_id = $userId AND 
180
                    c_id = $courseId AND 
181
                    access_tool = '$tool' AND 
182
                    access_session_id = $sessionId";
183
        $result = Database::query($sql);
184
185
        if (Database::affected_rows($result) == 0) {
186
            $params = [
187
                'access_user_id' => $userId,
188
                'c_id' => $courseId,
189
                'access_tool' => $tool,
190
                'access_date' => $reallyNow,
191
                'access_session_id' => $sessionId,
192
            ];
193
            Database::insert($tableLastAccess, $params);
194
        }
195
196
        return true;
197
    }
198
199
    /**
200
     * Record information for download event (when an user click to d/l a
201
     * document) it will be used in a redirection page.
202
     *
203
     * @param string $documentUrl
204
     *
205
     * @return int
206
     *
207
     * @author Sebastien Piraux <[email protected]>
208
     * @author Evie Embrechts (bug fixed: The user id is put in single quotes)
209
     */
210
    public static function event_download($documentUrl)
211
    {
212
        if (Session::read('login_as')) {
213
            return false;
214
        }
215
216
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DOWNLOADS);
217
        $documentUrl = Database::escape_string($documentUrl);
218
219
        $reallyNow = api_get_utc_datetime();
220
        $userId = api_get_user_id();
221
        $courseId = api_get_course_int_id();
222
        $sessionId = api_get_session_id();
223
224
        $sql = "INSERT INTO $table (
225
                 down_user_id,
226
                 c_id,
227
                 down_doc_path,
228
                 down_date,
229
                 down_session_id
230
                )
231
                VALUES (
232
                 $userId,
233
                 $courseId,
234
                 '$documentUrl',
235
                 '$reallyNow',
236
                 $sessionId
237
                )";
238
        Database::query($sql);
239
240
        return 1;
241
    }
242
243
    /**
244
     * @param int $documentId of document (id in mainDb.document table)
245
     *
246
     * @author Sebastien Piraux <[email protected]>
247
     * @desc Record information for upload event
248
     * used in the works tool to record informations when
249
     * an user upload 1 work
250
     *
251
     * @return int
252
     */
253
    public static function event_upload($documentId)
254
    {
255
        if (Session::read('login_as')) {
256
            return false;
257
        }
258
259
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_UPLOADS);
260
        $courseId = api_get_course_int_id();
261
        $reallyNow = api_get_utc_datetime();
262
        $userId = api_get_user_id();
263
        $documentId = (int) $documentId;
264
        $sessionId = api_get_session_id();
265
266
        $sql = "INSERT INTO $table
267
                ( upload_user_id,
268
                  c_id,
269
                  upload_work_id,
270
                  upload_date,
271
                  upload_session_id
272
                )
273
                VALUES (
274
                 $userId,
275
                 $courseId,
276
                 $documentId,
277
                 '$reallyNow',
278
                 $sessionId
279
                )";
280
        Database::query($sql);
281
282
        return 1;
283
    }
284
285
    /**
286
     * Record information for link event (when an user click on an added link)
287
     * it will be used in a redirection page.
288
     *
289
     * @param int $linkId (id in c_link table)
290
     *
291
     * @return int
292
     *
293
     * @author Sebastien Piraux <[email protected]>
294
     */
295
    public static function event_link($linkId)
296
    {
297
        if (Session::read('login_as')) {
298
            return false;
299
        }
300
301
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_LINKS);
302
        $reallyNow = api_get_utc_datetime();
303
        $userId = api_get_user_id();
304
        $courseId = api_get_course_int_id();
305
        $linkId = (int) $linkId;
306
        $sessionId = api_get_session_id();
307
        $sql = "INSERT INTO ".$table."
308
                    ( links_user_id,
309
                     c_id,
310
                     links_link_id,
311
                     links_date,
312
                     links_session_id
313
                    ) VALUES (
314
                     $userId,
315
                     $courseId,
316
                     $linkId,
317
                     '$reallyNow',
318
                     $sessionId
319
                    )";
320
        Database::query($sql);
321
322
        return 1;
323
    }
324
325
    /**
326
     * Update the TRACK_E_EXERCICES exercises.
327
     *
328
     * @param   int     exeid id of the attempt
329
     * @param   int     exo_id    exercise id
330
     * @param   mixed   result    score
331
     * @param   int     weighting ( higher score )
332
     * @param   int     duration ( duration of the attempt in seconds )
333
     * @param   int     session_id
334
     * @param   int     learnpath_id (id of the learnpath)
335
     * @param   int     learnpath_item_id (id of the learnpath_item)
336
     *
337
     * @return bool
338
     *
339
     * @author Sebastien Piraux <[email protected]>
340
     * @author Julio Montoya Armas <[email protected]> Reworked 2010
341
     * @desc Record result of user when an exercise was done
342
     */
343
    public static function updateEventExercise(
344
        $exeId,
345
        $exoId,
346
        $score,
347
        $weighting,
348
        $sessionId,
349
        $learnpathId = 0,
350
        $learnpathItemId = 0,
351
        $learnpathItemViewId = 0,
352
        $duration = 0,
353
        $questionsList = [],
354
        $status = '',
355
        $remindList = [],
356
        $endDate = null
357
    ) {
358
        if (empty($exeId)) {
359
            return false;
360
        }
361
362
        /*
363
         * Code commented due BT#8423 do not change the score to 0.
364
         *
365
         * Validation in case of fraud with actived control time
366
        if (!ExerciseLib::exercise_time_control_is_valid($exo_id, $learnpath_id, $learnpath_item_id)) {
367
            $score = 0;
368
        }
369
        */
370
        if (!isset($status) || empty($status)) {
371
            $status = '';
372
        } else {
373
            $status = Database::escape_string($status);
374
        }
375
376
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
377
378
        if (!empty($questionsList)) {
379
            $questionsList = array_map('intval', $questionsList);
380
        }
381
382
        if (!empty($remindList)) {
383
            $remindList = array_map('intval', $remindList);
384
            $remindList = array_filter($remindList);
385
            $remindList = implode(",", $remindList);
386
        } else {
387
            $remindList = '';
388
        }
389
390
        if (empty($endDate)) {
391
            $endDate = api_get_utc_datetime();
392
        }
393
        $exoId = (int) $exoId;
394
        $sessionId = (int) $sessionId;
395
        $learnpathId = (int) $learnpathId;
396
        $learnpathItemId = (int) $learnpathItemId;
397
        $learnpathItemViewId = (int) $learnpathItemViewId;
398
        $duration = (int) $duration;
399
        $exeId = (int) $exeId;
400
        $score = Database::escape_string($score);
401
        $weighting = Database::escape_string($weighting);
402
        $questions = implode(',', $questionsList);
403
        $userIp = Database::escape_string(api_get_real_ip());
404
405
        $sql = "UPDATE $table SET
406
               exe_exo_id = $exoId,
407
               score = '$score',
408
               max_score = '$weighting',
409
               session_id = $sessionId,
410
               orig_lp_id = $learnpathId,
411
               orig_lp_item_id = $learnpathItemId,
412
               orig_lp_item_view_id = $learnpathItemViewId,
413
               exe_duration = $duration,
414
               exe_date = '$endDate',
415
               status = '$status',
416
               questions_to_check = '$remindList',
417
               data_tracking = '$questions',
418
               user_ip = '$userIp'
419
             WHERE exe_id = $exeId";
420
        Database::query($sql);
421
422
        //Deleting control time session track
423
        //ExerciseLib::exercise_time_control_delete($exo_id);
424
        return true;
425
    }
426
427
    /**
428
     * Record an event for this attempt at answering an exercise.
429
     *
430
     * @param    float    Score achieved
431
     * @param    string    Answer given
432
     * @param    int    Question ID
433
     * @param    int Exercise attempt ID a.k.a exe_id (from track_e_exercise)
434
     * @param    int    Position
435
     * @param    int Exercise ID (from c_quiz)
436
     * @param    bool update results?
437
     * @param $fileName string  Filename (for audio answers - using nanogong)
438
     * @param    int User ID The user who's going to get this score. Default value of null means "get from context".
439
     * @param    int Course ID (from the "id" column of course table). Default value of null means "get from context".
440
     * @param    int Session ID (from the session table). Default value of null means "get from context".
441
     * @param    int Learnpath ID (from c_lp table). Default value of null means "get from context".
442
     * @param    int Learnpath item ID (from the c_lp_item table). Default value of null means "get from context".
443
     *
444
     * @return bool Result of the insert query
445
     */
446
    public static function saveQuestionAttempt(
447
        $score,
448
        $answer,
449
        $question_id,
450
        $exe_id,
451
        $position,
452
        $exercise_id = 0,
453
        $updateResults = false,
454
        $fileName = null,
455
        $user_id = null,
456
        $course_id = null,
457
        $session_id = null,
458
        $learnpath_id = null,
459
        $learnpath_item_id = null
460
    ) {
461
        global $debug;
462
        $question_id = Database::escape_string($question_id);
463
        $exe_id = Database::escape_string($exe_id);
464
        $position = Database::escape_string($position);
465
        $now = api_get_utc_datetime();
466
        $course_id = (int) $course_id;
467
468
        // check user_id or get from context
469
        if (empty($user_id)) {
470
            $user_id = api_get_user_id();
471
            // anonymous
472
            if (empty($user_id)) {
473
                $user_id = api_get_anonymous_id();
474
            }
475
        }
476
        // check course_id or get from context
477
        if (empty($course_id)) {
478
            $course_id = api_get_course_int_id();
479
        }
480
        // check session_id or get from context
481
        $session_id = (int) $session_id;
482
        if (empty($session_id)) {
483
            $session_id = api_get_session_id();
484
        }
485
        // check learnpath_id or get from context
486
        if (empty($learnpath_id)) {
487
            global $learnpath_id;
488
        }
489
        // check learnpath_item_id or get from context
490
        if (empty($learnpath_item_id)) {
491
            global $learnpath_item_id;
492
        }
493
494
        $TBL_TRACK_ATTEMPT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
495
496
        if ($debug) {
497
            error_log("----- entering saveQuestionAttempt() function ------");
498
            error_log("answer: $answer");
499
            error_log("score: $score");
500
            error_log("question_id : $question_id");
501
            error_log("position: $position");
502
        }
503
504
        //Validation in case of fraud with active control time
505
        if (!ExerciseLib::exercise_time_control_is_valid($exercise_id, $learnpath_id, $learnpath_item_id)) {
506
            if ($debug) {
507
                error_log("exercise_time_control_is_valid is false");
508
            }
509
            $score = 0;
510
            $answer = 0;
511
        }
512
513
        if (!empty($question_id) && !empty($exe_id) && !empty($user_id)) {
514
            if (is_null($answer)) {
515
                $answer = '';
516
            }
517
518
            if (is_null($score)) {
519
                $score = 0;
520
            }
521
522
            $attempt = [
523
                'user_id' => $user_id,
524
                'question_id' => $question_id,
525
                'answer' => $answer,
526
                'marks' => $score,
527
                'c_id' => $course_id,
528
                'session_id' => $session_id,
529
                'position' => $position,
530
                'tms' => $now,
531
                'filename' => !empty($fileName) ? basename($fileName) : $fileName,
532
                'teacher_comment' => '',
533
            ];
534
535
            // Check if attempt exists.
536
            $sql = "SELECT exe_id FROM $TBL_TRACK_ATTEMPT
537
                    WHERE
538
                        c_id = $course_id AND
539
                        session_id = $session_id AND
540
                        exe_id = $exe_id AND
541
                        user_id = $user_id AND
542
                        question_id = $question_id AND
543
                        position = $position";
544
            $result = Database::query($sql);
545
            if (Database::num_rows($result)) {
546
                if ($debug) {
547
                    error_log("Attempt already exist: exe_id: $exe_id - user_id:$user_id - question_id:$question_id");
548
                }
549
                if ($updateResults == false) {
550
                    //The attempt already exist do not update use  update_event_exercise() instead
551
                    return false;
552
                }
553
            } else {
554
                $attempt['exe_id'] = $exe_id;
555
            }
556
557
            if ($debug) {
558
                error_log("updateResults : $updateResults");
559
                error_log("Saving question attempt: ");
560
                error_log($sql);
561
            }
562
563
            $recording_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
564
565
            if ($updateResults == false) {
566
                $attempt_id = Database::insert($TBL_TRACK_ATTEMPT, $attempt);
567
568
                if ($debug) {
569
                    error_log("Insert attempt with id #$attempt_id");
570
                }
571
572
                if (defined('ENABLED_LIVE_EXERCISE_TRACKING')) {
573
                    if ($debug) {
574
                        error_log("Saving e attempt recording ");
575
                    }
576
                    $attempt_recording = [
577
                        'exe_id' => $attempt_id,
578
                        'question_id' => $question_id,
579
                        'marks' => $score,
580
                        'insert_date' => $now,
581
                        'author' => '',
582
                        'session_id' => $session_id,
583
                    ];
584
                    Database::insert($recording_table, $attempt_recording);
585
                }
586
            } else {
587
                Database::update(
588
                    $TBL_TRACK_ATTEMPT,
589
                    $attempt,
590
                    [
591
                        'exe_id = ? AND question_id = ? AND user_id = ? ' => [
592
                            $exe_id,
593
                            $question_id,
594
                            $user_id,
595
                        ],
596
                    ]
597
                );
598
599
                if (defined('ENABLED_LIVE_EXERCISE_TRACKING')) {
600
                    $attempt_recording = [
601
                        'exe_id' => $exe_id,
602
                        'question_id' => $question_id,
603
                        'marks' => $score,
604
                        'insert_date' => $now,
605
                        'author' => '',
606
                        'session_id' => $session_id,
607
                    ];
608
609
                    Database::update(
610
                        $recording_table,
611
                        $attempt_recording,
612
                        [
613
                            'exe_id = ? AND question_id = ? AND session_id = ? ' => [
614
                                $exe_id,
615
                                $question_id,
616
                                $session_id,
617
                            ],
618
                        ]
619
                    );
620
                }
621
                $attempt_id = $exe_id;
622
            }
623
624
            return $attempt_id;
625
        } else {
626
            return false;
627
        }
628
    }
629
630
    /**
631
     * Record an hotspot spot for this attempt at answering an hotspot question.
632
     *
633
     * @param int    $exeId
634
     * @param int    $questionId    Question ID
635
     * @param int    $answerId      Answer ID
636
     * @param int    $correct
637
     * @param string $coords        Coordinates of this point (e.g. 123;324)
638
     * @param bool   $updateResults
639
     * @param int    $exerciseId
640
     *
641
     * @return bool Result of the insert query
642
     *
643
     * @uses \Course code and user_id from global scope $_cid and $_user
644
     */
645
    public static function saveExerciseAttemptHotspot(
646
        $exeId,
647
        $questionId,
648
        $answerId,
649
        $correct,
650
        $coords,
651
        $updateResults = false,
652
        $exerciseId = 0
653
    ) {
654
        $debug = false;
655
        global $safe_lp_id, $safe_lp_item_id;
656
657
        if ($updateResults == false) {
658
            // Validation in case of fraud with activated control time
659
            if (!ExerciseLib::exercise_time_control_is_valid($exerciseId, $safe_lp_id, $safe_lp_item_id)) {
660
                if ($debug) {
661
                    error_log('Attempt is fraud');
662
                }
663
                $correct = 0;
664
            }
665
        }
666
667
        if (empty($exeId)) {
668
            if ($debug) {
669
                error_log('exe id is empty');
670
            }
671
672
            return false;
673
        }
674
675
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTSPOT);
676
        if ($updateResults) {
677
            if ($debug) {
678
                error_log("Insert hotspot results: exeId: $exeId correct: $correct");
679
            }
680
            $params = [
681
                'hotspot_correct' => $correct,
682
                'hotspot_coordinate' => $coords,
683
            ];
684
            Database::update(
685
                $table,
686
                $params,
687
                [
688
                    'hotspot_user_id = ? AND hotspot_exe_id = ? AND hotspot_question_id = ? AND hotspot_answer_id = ? ' => [
689
                        api_get_user_id(),
690
                        $exeId,
691
                        $questionId,
692
                        $answerId,
693
                    ],
694
                ]
695
            );
696
        } else {
697
            if ($debug) {
698
                error_log("Insert hotspot results: exeId: $exeId correct: $correct");
699
            }
700
701
            return Database::insert(
702
                $table,
703
                [
704
                    'hotspot_user_id' => api_get_user_id(),
705
                    'c_id' => api_get_course_int_id(),
706
                    'hotspot_exe_id' => $exeId,
707
                    'hotspot_question_id' => $questionId,
708
                    'hotspot_answer_id' => $answerId,
709
                    'hotspot_correct' => $correct,
710
                    'hotspot_coordinate' => $coords,
711
                ]
712
            );
713
        }
714
    }
715
716
    /**
717
     * Records information for common (or admin) events (in the track_e_default table).
718
     *
719
     * @author Yannick Warnier <[email protected]>
720
     *
721
     * @param string $event_type       Type of event
722
     * @param string $event_value_type Type of value
723
     * @param mixed  $event_value      Value (string, or array in the case of user info)
724
     * @param string $datetime         Datetime (UTC) (defaults to null)
725
     * @param int    $user_id          User ID (defaults to null)
726
     * @param int    $course_id        Course ID (defaults to null)
727
     * @param int    $sessionId        Session ID
728
     *
729
     * @return bool
730
     * @assert ('','','') === false
731
     */
732
    public static function addEvent(
733
        $event_type,
734
        $event_value_type,
735
        $event_value,
736
        $datetime = null,
737
        $user_id = null,
738
        $course_id = null,
739
        $sessionId = 0
740
    ) {
741
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_DEFAULT);
742
743
        if (empty($event_type)) {
744
            return false;
745
        }
746
        $event_type = Database::escape_string($event_type);
747
        $event_value_type = Database::escape_string($event_value_type);
748
        if (!empty($course_id)) {
749
            $course_id = (int) $course_id;
750
        } else {
751
            $course_id = api_get_course_int_id();
752
        }
753
        if (!empty($sessionId)) {
754
            $sessionId = (int) $sessionId;
755
        } else {
756
            $sessionId = api_get_session_id();
757
        }
758
759
        //Clean the user_info
760
        if ($event_value_type == LOG_USER_OBJECT) {
761
            if (is_array($event_value)) {
762
                unset($event_value['complete_name']);
763
                unset($event_value['complete_name_with_username']);
764
                unset($event_value['firstName']);
765
                unset($event_value['lastName']);
766
                unset($event_value['avatar_small']);
767
                unset($event_value['avatar']);
768
                unset($event_value['mail']);
769
                unset($event_value['password']);
770
                unset($event_value['last_login']);
771
                unset($event_value['picture_uri']);
772
                $event_value = serialize($event_value);
773
            }
774
        }
775
        // If event is an array then the $event_value_type should finish with
776
        // the suffix _array for example LOG_WORK_DATA = work_data_array
777
        if (is_array($event_value)) {
778
            $event_value = serialize($event_value);
779
        }
780
781
        $event_value = Database::escape_string($event_value);
782
        $sessionId = empty($sessionId) ? api_get_session_id() : (int) $sessionId;
783
784
        if (!isset($datetime)) {
785
            $datetime = api_get_utc_datetime();
786
        }
787
788
        $datetime = Database::escape_string($datetime);
789
790
        if (!isset($user_id)) {
791
            $user_id = api_get_user_id();
792
        }
793
794
        $params = [
795
            'default_user_id' => $user_id,
796
            'c_id' => $course_id,
797
            'default_date' => $datetime,
798
            'default_event_type' => $event_type,
799
            'default_value_type' => $event_value_type,
800
            'default_value' => $event_value,
801
            'session_id' => $sessionId,
802
        ];
803
        Database::insert($table, $params);
804
805
        return true;
806
    }
807
808
    /**
809
     * Gets the last attempt of an exercise based in the exe_id.
810
     *
811
     * @param int $exeId
812
     *
813
     * @return mixed
814
     */
815
    public static function getLastAttemptDateOfExercise($exeId)
816
    {
817
        $exeId = (int) $exeId;
818
        $track_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
819
        $sql = "SELECT max(tms) as last_attempt_date
820
                FROM $track_attempts
821
                WHERE exe_id = $exeId";
822
        $rs_last_attempt = Database::query($sql);
823
        $row_last_attempt = Database::fetch_array($rs_last_attempt);
824
        $date = $row_last_attempt['last_attempt_date']; //Get the date of last attempt
825
826
        return $date;
827
    }
828
829
    /**
830
     * Gets the last attempt of an exercise based in the exe_id.
831
     *
832
     * @param int $exeId
833
     *
834
     * @return mixed
835
     */
836
    public static function getLatestQuestionIdFromAttempt($exeId)
837
    {
838
        $exeId = (int) $exeId;
839
        $track_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
840
        $sql = "SELECT question_id FROM $track_attempts
841
                WHERE exe_id = $exeId
842
                ORDER BY tms DESC
843
                LIMIT 1";
844
        $result = Database::query($sql);
845
        if (Database::num_rows($result)) {
846
            $row = Database::fetch_array($result);
847
848
            return $row['question_id'];
849
        } else {
850
            return false;
851
        }
852
    }
853
854
    /**
855
     * Gets how many attempts exists by user, exercise, learning path.
856
     *
857
     * @param   int user id
858
     * @param   int exercise id
859
     * @param   int lp id
860
     * @param   int lp item id
861
     * @param   int lp item view id
862
     *
863
     * @return int
864
     */
865
    public static function get_attempt_count(
866
        $user_id,
867
        $exerciseId,
868
        $lp_id,
869
        $lp_item_id,
870
        $lp_item_view_id
871
    ) {
872
        $stat_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
873
        $user_id = (int) $user_id;
874
        $exerciseId = (int) $exerciseId;
875
        $lp_id = (int) $lp_id;
876
        $lp_item_id = (int) $lp_item_id;
877
        $lp_item_view_id = (int) $lp_item_view_id;
878
        $courseId = api_get_course_int_id();
879
        $sessionId = api_get_session_id();
880
881
        $sql = "SELECT count(*) as count
882
                FROM $stat_table
883
                WHERE
884
                    exe_exo_id = $exerciseId AND
885
                    exe_user_id = $user_id AND
886
                    status != 'incomplete' AND
887
                    orig_lp_id = $lp_id AND
888
                    orig_lp_item_id = $lp_item_id AND
889
                    orig_lp_item_view_id = $lp_item_view_id AND
890
                    c_id = $courseId AND
891
                    session_id = $sessionId";
892
893
        $query = Database::query($sql);
894
        if (Database::num_rows($query) > 0) {
895
            $attempt = Database::fetch_array($query, 'ASSOC');
896
897
            return (int) $attempt['count'];
898
        }
899
900
        return 0;
901
    }
902
903
    /**
904
     * @param $user_id
905
     * @param $exerciseId
906
     * @param $lp_id
907
     * @param $lp_item_id
908
     *
909
     * @return int
910
     */
911
    public static function get_attempt_count_not_finished(
912
        $user_id,
913
        $exerciseId,
914
        $lp_id,
915
        $lp_item_id
916
    ) {
917
        $stat_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
918
        $user_id = (int) $user_id;
919
        $exerciseId = (int) $exerciseId;
920
        $lp_id = (int) $lp_id;
921
        $lp_item_id = (int) $lp_item_id;
922
        //$lp_item_view_id = (int) $lp_item_view_id;
923
        $courseId = api_get_course_int_id();
924
        $sessionId = api_get_session_id();
925
926
        $sql = "SELECT count(*) as count
927
                FROM $stat_table
928
                WHERE
929
                    exe_exo_id 			= $exerciseId AND
930
                    exe_user_id 		= $user_id AND
931
                    status 				!= 'incomplete' AND
932
                    orig_lp_id 			= $lp_id AND
933
                    orig_lp_item_id 	= $lp_item_id AND
934
                    c_id = $courseId AND
935
                    session_id = $sessionId";
936
937
        $query = Database::query($sql);
938
        if (Database::num_rows($query) > 0) {
939
            $attempt = Database::fetch_array($query, 'ASSOC');
940
941
            return $attempt['count'];
942
        } else {
943
            return 0;
944
        }
945
    }
946
947
    /**
948
     * @param int   $user_id
949
     * @param int   $lp_id
950
     * @param array $course
951
     * @param int   $session_id
952
     *
953
     * @return bool
954
     */
955
    public static function delete_student_lp_events(
956
        $user_id,
957
        $lp_id,
958
        $course,
959
        $session_id
960
    ) {
961
        $lp_view_table = Database::get_course_table(TABLE_LP_VIEW);
962
        $lp_item_view_table = Database::get_course_table(TABLE_LP_ITEM_VIEW);
963
        $lpInteraction = Database::get_course_table(TABLE_LP_IV_INTERACTION);
964
        $lpObjective = Database::get_course_table(TABLE_LP_IV_OBJECTIVE);
965
966
        if (empty($course)) {
967
            return false;
968
        }
969
970
        $course_id = $course['real_id'];
971
        $user_id = (int) $user_id;
972
        $lp_id = (int) $lp_id;
973
        $session_id = (int) $session_id;
974
975
        if (empty($course_id)) {
976
            $course_id = api_get_course_int_id();
977
        }
978
979
        $track_e_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
980
        $track_attempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
981
        $recording_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
982
983
        // Make sure we have the exact lp_view_id
984
        $sql = "SELECT id FROM $lp_view_table
985
                WHERE
986
                    c_id = $course_id AND
987
                    user_id = $user_id AND
988
                    lp_id = $lp_id AND
989
                    session_id = $session_id";
990
        $result = Database::query($sql);
991
992
        if (Database::num_rows($result)) {
993
            $view = Database::fetch_array($result, 'ASSOC');
994
            $lp_view_id = $view['id'];
995
996
            $sql = "DELETE FROM $lp_item_view_table
997
                    WHERE c_id = $course_id AND lp_view_id = $lp_view_id";
998
            Database::query($sql);
999
1000
            $sql = "DELETE FROM $lpInteraction
1001
                    WHERE c_id = $course_id AND lp_iv_id = $lp_view_id";
1002
            Database::query($sql);
1003
1004
            $sql = "DELETE FROM $lpObjective
1005
                    WHERE c_id = $course_id AND lp_iv_id = $lp_view_id";
1006
            Database::query($sql);
1007
        }
1008
1009
        if (api_get_configuration_value('lp_minimum_time')) {
1010
            $sql = "DELETE FROM track_e_access_complete
1011
                    WHERE 
1012
                        tool = 'learnpath' AND 
1013
                        c_id = $course_id AND 
1014
                        tool_id = $lp_id AND
1015
                        user_id = $user_id AND
1016
                        session_id = $session_id
1017
                    ";
1018
            Database::query($sql);
1019
        }
1020
1021
        $sql = "DELETE FROM $lp_view_table
1022
                WHERE
1023
                    c_id = $course_id AND
1024
                    user_id = $user_id AND
1025
                    lp_id= $lp_id AND
1026
                    session_id = $session_id
1027
            ";
1028
        Database::query($sql);
1029
1030
        $sql = "SELECT exe_id FROM $track_e_exercises
1031
                WHERE   
1032
                    exe_user_id = $user_id AND
1033
                    session_id = $session_id AND
1034
                    c_id = $course_id AND
1035
                    orig_lp_id = $lp_id";
1036
        $result = Database::query($sql);
1037
        $exe_list = [];
1038
        while ($row = Database::fetch_array($result, 'ASSOC')) {
1039
            $exe_list[] = $row['exe_id'];
1040
        }
1041
1042
        if (!empty($exe_list) && is_array($exe_list) && count($exe_list) > 0) {
1043
            $exeListString = implode(',', $exe_list);
1044
            $sql = "DELETE FROM $track_e_exercises
1045
                    WHERE exe_id IN ($exeListString)";
1046
            Database::query($sql);
1047
1048
            $sql = "DELETE FROM $track_attempts
1049
                    WHERE exe_id IN ($exeListString)";
1050
            Database::query($sql);
1051
1052
            $sql = "DELETE FROM $recording_table
1053
                    WHERE exe_id IN ($exeListString)";
1054
            Database::query($sql);
1055
        }
1056
1057
        self::addEvent(
1058
            LOG_LP_ATTEMPT_DELETE,
1059
            LOG_LP_ID,
1060
            $lp_id,
1061
            null,
1062
            null,
1063
            $course_id,
1064
            $session_id
1065
        );
1066
1067
        return true;
1068
    }
1069
1070
    /**
1071
     * Delete all exercise attempts (included in LP or not).
1072
     *
1073
     * @param int user id
1074
     * @param int exercise id
1075
     * @param int $course_id
1076
     * @param int session id
1077
     */
1078
    public static function delete_all_incomplete_attempts(
1079
        $user_id,
1080
        $exercise_id,
1081
        $course_id,
1082
        $session_id = 0
1083
    ) {
1084
        $user_id = (int) $user_id;
1085
        $exercise_id = (int) $exercise_id;
1086
        $course_id = (int) $course_id;
1087
        $session_id = (int) $session_id;
1088
1089
        if (!empty($user_id) && !empty($exercise_id) && !empty($course_id)) {
1090
            $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1091
            $sql = "DELETE FROM $table
1092
                    WHERE
1093
                        exe_user_id = $user_id AND
1094
                        exe_exo_id = $exercise_id AND
1095
                        c_id = $course_id AND
1096
                        session_id = $session_id AND
1097
                        status = 'incomplete' ";
1098
            Database::query($sql);
1099
            self::addEvent(
1100
                LOG_EXERCISE_RESULT_DELETE,
1101
                LOG_EXERCISE_AND_USER_ID,
1102
                $exercise_id.'-'.$user_id,
1103
                null,
1104
                null,
1105
                $course_id,
1106
                $session_id
1107
            );
1108
        }
1109
    }
1110
1111
    /**
1112
     * Gets all exercise results (NO Exercises in LPs ) from a given exercise id, course, session.
1113
     *
1114
     * @param int $exercise_id
1115
     * @param int $courseId
1116
     * @param int $session_id
1117
     *
1118
     * @return array with the results
1119
     */
1120
    public static function get_all_exercise_results(
1121
        $exercise_id,
1122
        $courseId,
1123
        $session_id = 0,
1124
        $load_question_list = true,
1125
        $user_id = null
1126
    ) {
1127
        $TABLETRACK_EXERCICES = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1128
        $TBL_TRACK_ATTEMPT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1129
        $courseId = (int) $courseId;
1130
        $exercise_id = (int) $exercise_id;
1131
        $session_id = (int) $session_id;
1132
1133
        $user_condition = null;
1134
        if (!empty($user_id)) {
1135
            $user_id = (int) $user_id;
1136
            $user_condition = "AND exe_user_id = $user_id ";
1137
        }
1138
        $sql = "SELECT * FROM $TABLETRACK_EXERCICES
1139
                WHERE
1140
                    status = ''  AND
1141
                    c_id = $courseId AND
1142
                    exe_exo_id = $exercise_id AND
1143
                    session_id = $session_id  AND
1144
                    orig_lp_id =0 AND
1145
                    orig_lp_item_id = 0
1146
                    $user_condition
1147
                ORDER BY exe_id";
1148
        $res = Database::query($sql);
1149
        $list = [];
1150
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1151
            $list[$row['exe_id']] = $row;
1152
            if ($load_question_list) {
1153
                $sql = "SELECT * FROM $TBL_TRACK_ATTEMPT
1154
                        WHERE exe_id = {$row['exe_id']}";
1155
                $res_question = Database::query($sql);
1156
                while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
1157
                    $list[$row['exe_id']]['question_list'][$row_q['question_id']] = $row_q;
1158
                }
1159
            }
1160
        }
1161
1162
        return $list;
1163
    }
1164
1165
    /**
1166
     * Gets all exercise results (NO Exercises in LPs ) from a given exercise id, course, session.
1167
     *
1168
     * @param int  $courseId
1169
     * @param int  $session_id
1170
     * @param bool $get_count
1171
     *
1172
     * @return array with the results
1173
     */
1174
    public static function get_all_exercise_results_by_course(
1175
        $courseId,
1176
        $session_id = 0,
1177
        $get_count = true
1178
    ) {
1179
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1180
        $courseId = (int) $courseId;
1181
        $session_id = (int) $session_id;
1182
1183
        $select = '*';
1184
        if ($get_count) {
1185
            $select = 'count(*) as count';
1186
        }
1187
        $sql = "SELECT $select FROM $table_track_exercises
1188
                WHERE   status = ''  AND
1189
                        c_id = $courseId AND
1190
                        session_id = $session_id  AND
1191
                        orig_lp_id = 0 AND
1192
                        orig_lp_item_id = 0
1193
                ORDER BY exe_id";
1194
        $res = Database::query($sql);
1195
        if ($get_count) {
1196
            $row = Database::fetch_array($res, 'ASSOC');
1197
1198
            return $row['count'];
1199
        } else {
1200
            $list = [];
1201
            while ($row = Database::fetch_array($res, 'ASSOC')) {
1202
                $list[$row['exe_id']] = $row;
1203
            }
1204
1205
            return $list;
1206
        }
1207
    }
1208
1209
    /**
1210
     * Gets all exercise results (NO Exercises in LPs) from a given exercise id, course, session.
1211
     *
1212
     * @param int $user_id
1213
     * @param int $courseId
1214
     * @param int $session_id
1215
     *
1216
     * @return array with the results
1217
     */
1218
    public static function get_all_exercise_results_by_user(
1219
        $user_id,
1220
        $courseId,
1221
        $session_id = 0
1222
    ) {
1223
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1224
        $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1225
        $courseId = (int) $courseId;
1226
        $session_id = (int) $session_id;
1227
        $user_id = (int) $user_id;
1228
1229
        $sql = "SELECT * FROM $table_track_exercises
1230
                WHERE
1231
                    status = '' AND
1232
                    exe_user_id = $user_id AND
1233
                    c_id = $courseId AND
1234
                    session_id = $session_id AND
1235
                    orig_lp_id = 0 AND
1236
                    orig_lp_item_id = 0
1237
                ORDER by exe_id";
1238
1239
        $res = Database::query($sql);
1240
        $list = [];
1241
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1242
            $list[$row['exe_id']] = $row;
1243
            $sql = "SELECT * FROM $table_track_attempt 
1244
                    WHERE exe_id = {$row['exe_id']}";
1245
            $res_question = Database::query($sql);
1246
            while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
1247
                $list[$row['exe_id']]['question_list'][$row_q['question_id']] = $row_q;
1248
            }
1249
        }
1250
1251
        return $list;
1252
    }
1253
1254
    /**
1255
     * Gets exercise results (NO Exercises in LPs) from a given exercise id, course, session.
1256
     *
1257
     * @param int    $exe_id attempt id
1258
     * @param string $status
1259
     *
1260
     * @return array with the results
1261
     */
1262
    public static function get_exercise_results_by_attempt($exe_id, $status = null)
1263
    {
1264
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1265
        $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1266
        $table_track_attempt_recording = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
1267
        $exe_id = (int) $exe_id;
1268
1269
        $status = Database::escape_string($status);
1270
1271
        $sql = "SELECT * FROM $table_track_exercises
1272
                WHERE status = '$status' AND exe_id = $exe_id";
1273
1274
        $res = Database::query($sql);
1275
        $list = [];
1276
        if (Database::num_rows($res)) {
1277
            $row = Database::fetch_array($res, 'ASSOC');
1278
1279
            //Checking if this attempt was revised by a teacher
1280
            $sql_revised = "SELECT exe_id FROM $table_track_attempt_recording
1281
                            WHERE author != '' AND exe_id = $exe_id 
1282
                            LIMIT 1";
1283
            $res_revised = Database::query($sql_revised);
1284
            $row['attempt_revised'] = 0;
1285
            if (Database::num_rows($res_revised) > 0) {
1286
                $row['attempt_revised'] = 1;
1287
            }
1288
            $list[$exe_id] = $row;
1289
            $sql = "SELECT * FROM $table_track_attempt
1290
                    WHERE exe_id = $exe_id 
1291
                    ORDER BY tms ASC";
1292
            $res_question = Database::query($sql);
1293
            while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
1294
                $list[$exe_id]['question_list'][$row_q['question_id']] = $row_q;
1295
            }
1296
        }
1297
1298
        return $list;
1299
    }
1300
1301
    /**
1302
     * Gets exercise results (NO Exercises in LPs) from a given user, exercise id, course, session, lp_id, lp_item_id.
1303
     *
1304
     * @param   int     user id
1305
     * @param   int     exercise id
1306
     * @param   string  course code
1307
     * @param   int     session id
1308
     * @param   int     lp id
1309
     * @param   int     lp item id
1310
     * @param   string order asc or desc
1311
     *
1312
     * @return array with the results
1313
     */
1314
    public static function getExerciseResultsByUser(
1315
        $user_id,
1316
        $exercise_id,
1317
        $courseId,
1318
        $session_id = 0,
1319
        $lp_id = 0,
1320
        $lp_item_id = 0,
1321
        $order = null
1322
    ) {
1323
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1324
        $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1325
        $table_track_attempt_recording = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
1326
        $courseId = (int) $courseId;
1327
        $exercise_id = (int) $exercise_id;
1328
        $session_id = (int) $session_id;
1329
        $user_id = (int) $user_id;
1330
        $lp_id = (int) $lp_id;
1331
        $lp_item_id = (int) $lp_item_id;
1332
1333
        if (!in_array(strtolower($order), ['asc', 'desc'])) {
1334
            $order = 'asc';
1335
        }
1336
1337
        $sql = "SELECT * FROM $table_track_exercises
1338
                WHERE
1339
                    status 			= '' AND
1340
                    exe_user_id 	= $user_id AND
1341
                    c_id 	        = $courseId AND
1342
                    exe_exo_id 		= $exercise_id AND
1343
                    session_id 		= $session_id AND
1344
                    orig_lp_id 		= $lp_id AND
1345
                    orig_lp_item_id = $lp_item_id
1346
                ORDER by exe_id $order ";
1347
1348
        $res = Database::query($sql);
1349
        $list = [];
1350
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1351
            // Checking if this attempt was revised by a teacher
1352
            $exeId = $row['exe_id'];
1353
            $sql = "SELECT exe_id FROM $table_track_attempt_recording
1354
                    WHERE author != '' AND exe_id = $exeId
1355
                    LIMIT 1";
1356
            $res_revised = Database::query($sql);
1357
            $row['attempt_revised'] = 0;
1358
            if (Database::num_rows($res_revised) > 0) {
1359
                $row['attempt_revised'] = 1;
1360
            }
1361
            $list[$row['exe_id']] = $row;
1362
            $sql = "SELECT * FROM $table_track_attempt
1363
                    WHERE exe_id = $exeId";
1364
            $res_question = Database::query($sql);
1365
            while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
1366
                $list[$row['exe_id']]['question_list'][$row_q['question_id']][] = $row_q;
1367
            }
1368
        }
1369
1370
        return $list;
1371
    }
1372
1373
    /**
1374
     * Count exercise attempts (NO Exercises in LPs ) from a given exercise id, course, session.
1375
     *
1376
     * @param int $user_id
1377
     * @param int $exercise_id
1378
     * @param int $courseId
1379
     * @param int $session_id
1380
     *
1381
     * @return array with the results
1382
     */
1383
    public static function count_exercise_attempts_by_user(
1384
        $user_id,
1385
        $exercise_id,
1386
        $courseId,
1387
        $session_id = 0
1388
    ) {
1389
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1390
        $courseId = (int) $courseId;
1391
        $exercise_id = (int) $exercise_id;
1392
        $session_id = (int) $session_id;
1393
        $user_id = (int) $user_id;
1394
1395
        $sql = "SELECT count(*) as count 
1396
                FROM $table
1397
                WHERE status = ''  AND
1398
                    exe_user_id = $user_id AND
1399
                    c_id = $courseId AND
1400
                    exe_exo_id = $exercise_id AND
1401
                    session_id = $session_id AND
1402
                    orig_lp_id =0 AND
1403
                    orig_lp_item_id = 0
1404
                ORDER BY exe_id";
1405
        $res = Database::query($sql);
1406
        $result = 0;
1407
        if (Database::num_rows($res) > 0) {
1408
            $row = Database::fetch_array($res, 'ASSOC');
1409
            $result = $row['count'];
1410
        }
1411
1412
        return $result;
1413
    }
1414
1415
    /**
1416
     * Gets all exercise BEST results attempts (NO Exercises in LPs)
1417
     * from a given exercise id, course, session per user.
1418
     *
1419
     * @param int $exercise_id
1420
     * @param int $courseId
1421
     * @param int $session_id
1422
     * @param int $userId
1423
     *
1424
     * @return array with the results
1425
     *
1426
     * @todo rename this function
1427
     */
1428
    public static function get_best_exercise_results_by_user(
1429
        $exercise_id,
1430
        $courseId,
1431
        $session_id = 0,
1432
        $userId = 0
1433
    ) {
1434
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1435
        $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1436
        $courseId = (int) $courseId;
1437
        $exercise_id = (int) $exercise_id;
1438
        $session_id = (int) $session_id;
1439
1440
        $sql = "SELECT * FROM $table_track_exercises
1441
                WHERE
1442
                    status = '' AND
1443
                    c_id = $courseId AND
1444
                    exe_exo_id = $exercise_id AND
1445
                    session_id = $session_id AND
1446
                    orig_lp_id = 0 AND
1447
                    orig_lp_item_id = 0";
1448
1449
        if (!empty($userId)) {
1450
            $userId = (int) $userId;
1451
            $sql .= " AND exe_user_id = $userId ";
1452
        }
1453
        $sql .= ' ORDER BY exe_id';
1454
1455
        $res = Database::query($sql);
1456
        $list = [];
1457
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1458
            $list[$row['exe_id']] = $row;
1459
            $exeId = $row['exe_id'];
1460
            $sql = "SELECT * FROM $table_track_attempt 
1461
                    WHERE exe_id = $exeId";
1462
            $res_question = Database::query($sql);
1463
            while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
1464
                $list[$exeId]['question_list'][$row_q['question_id']] = $row_q;
1465
            }
1466
        }
1467
1468
        // Getting the best results of every student
1469
        $best_score_return = [];
1470
        foreach ($list as $student_result) {
1471
            $user_id = $student_result['exe_user_id'];
1472
            $current_best_score[$user_id] = $student_result['score'];
1473
            if (!isset($best_score_return[$user_id]['score'])) {
1474
                $best_score_return[$user_id] = $student_result;
1475
            }
1476
1477
            if ($current_best_score[$user_id] > $best_score_return[$user_id]['score']) {
1478
                $best_score_return[$user_id] = $student_result;
1479
            }
1480
        }
1481
1482
        return $best_score_return;
1483
    }
1484
1485
    /**
1486
     * @param int $user_id
1487
     * @param int $exercise_id
1488
     * @param int $courseId
1489
     * @param int $session_id
1490
     *
1491
     * @return array
1492
     */
1493
    public static function get_best_attempt_exercise_results_per_user(
1494
        $user_id,
1495
        $exercise_id,
1496
        $courseId,
1497
        $session_id = 0
1498
    ) {
1499
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1500
        $courseId = (int) $courseId;
1501
        $exercise_id = (int) $exercise_id;
1502
        $session_id = (int) $session_id;
1503
        $user_id = (int) $user_id;
1504
1505
        $sql = "SELECT * FROM $table_track_exercises
1506
                WHERE
1507
                    status = ''  AND
1508
                    c_id = $courseId AND
1509
                    exe_exo_id = $exercise_id AND
1510
                    session_id = $session_id  AND
1511
                    exe_user_id = $user_id AND
1512
                    orig_lp_id = 0 AND
1513
                    orig_lp_item_id = 0
1514
                ORDER BY exe_id";
1515
1516
        $res = Database::query($sql);
1517
        $list = [];
1518
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1519
            $list[$row['exe_id']] = $row;
1520
        }
1521
        //Getting the best results of every student
1522
        $best_score_return = [];
1523
        $best_score_return['score'] = 0;
1524
1525
        foreach ($list as $result) {
1526
            $current_best_score = $result;
1527
            if ($current_best_score['score'] > $best_score_return['score']) {
1528
                $best_score_return = $result;
1529
            }
1530
        }
1531
        if (!isset($best_score_return['max_score'])) {
1532
            $best_score_return = [];
1533
        }
1534
1535
        return $best_score_return;
1536
    }
1537
1538
    /**
1539
     * @param int $exercise_id
1540
     * @param int $courseId
1541
     * @param int $session_id
1542
     *
1543
     * @return mixed
1544
     */
1545
    public static function count_exercise_result_not_validated(
1546
        $exercise_id,
1547
        $courseId,
1548
        $session_id = 0
1549
    ) {
1550
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1551
        $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
1552
        $courseId = (int) $courseId;
1553
        $session_id = (int) $session_id;
1554
        $exercise_id = (int) $exercise_id;
1555
1556
        $sql = "SELECT count(e.exe_id) as count
1557
                FROM $table_track_exercises e
1558
                LEFT JOIN $table_track_attempt a
1559
                ON e.exe_id = a.exe_id
1560
                WHERE
1561
                    exe_exo_id = $exercise_id AND
1562
                    c_id = $courseId AND
1563
                    e.session_id = $session_id  AND
1564
                    orig_lp_id = 0 AND
1565
                    marks IS NULL AND
1566
                    status = '' AND
1567
                    orig_lp_item_id = 0
1568
                ORDER BY e.exe_id";
1569
        $res = Database::query($sql);
1570
        $row = Database::fetch_array($res, 'ASSOC');
1571
1572
        return $row['count'];
1573
    }
1574
1575
    /**
1576
     * Gets all exercise events from a Learning Path within a Course    nd Session.
1577
     *
1578
     * @param int $exercise_id
1579
     * @param int $courseId
1580
     * @param int $session_id
1581
     *
1582
     * @return array
1583
     */
1584
    public static function get_all_exercise_event_from_lp(
1585
        $exercise_id,
1586
        $courseId,
1587
        $session_id = 0
1588
    ) {
1589
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1590
        $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1591
        $courseId = (int) $courseId;
1592
        $exercise_id = (int) $exercise_id;
1593
        $session_id = (int) $session_id;
1594
1595
        $sql = "SELECT * FROM $table_track_exercises
1596
                WHERE
1597
                    status = '' AND
1598
                    c_id = $courseId AND
1599
                    exe_exo_id = $exercise_id AND
1600
                    session_id = $session_id AND
1601
                    orig_lp_id !=0 AND
1602
                    orig_lp_item_id != 0";
1603
1604
        $res = Database::query($sql);
1605
        $list = [];
1606
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1607
            $exeId = $row['exe_id'];
1608
            $list[$exeId] = $row;
1609
            $sql = "SELECT * FROM $table_track_attempt 
1610
                    WHERE exe_id = $exeId";
1611
            $res_question = Database::query($sql);
1612
            while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
1613
                $list[$exeId]['question_list'][$row_q['question_id']] = $row_q;
1614
            }
1615
        }
1616
1617
        return $list;
1618
    }
1619
1620
    /**
1621
     * Get a list of all the exercises in a given learning path.
1622
     *
1623
     * @param int $lp_id
1624
     * @param int $course_id This parameter is probably deprecated as lp_id now is a global iid
1625
     *
1626
     * @return array
1627
     */
1628
    public static function get_all_exercises_from_lp($lp_id, $course_id)
1629
    {
1630
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
1631
        $course_id = (int) $course_id;
1632
        $lp_id = (int) $lp_id;
1633
        $sql = "SELECT * FROM $lp_item_table
1634
                WHERE
1635
                    c_id = $course_id AND
1636
                    lp_id = $lp_id AND
1637
                    item_type = 'quiz'
1638
                ORDER BY parent_item_id, display_order";
1639
        $res = Database::query($sql);
1640
1641
        $list = [];
1642
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1643
            $list[] = $row;
1644
        }
1645
1646
        return $list;
1647
    }
1648
1649
    /**
1650
     * This function gets the comments of an exercise.
1651
     *
1652
     * @param int $exe_id
1653
     * @param int $question_id
1654
     *
1655
     * @return string the comment
1656
     */
1657
    public static function get_comments($exe_id, $question_id)
1658
    {
1659
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1660
        $exe_id = (int) $exe_id;
1661
        $question_id = (int) $question_id;
1662
        $sql = "SELECT teacher_comment 
1663
                FROM $table
1664
                WHERE
1665
                    exe_id = $exe_id AND
1666
                    question_id = $question_id
1667
                ORDER by question_id";
1668
        $sqlres = Database::query($sql);
1669
        $comm = strval(Database::result($sqlres, 0, 'teacher_comment'));
1670
        $comm = trim($comm);
1671
1672
        return $comm;
1673
    }
1674
1675
    /**
1676
     * @param int $exeId
1677
     *
1678
     * @return array
1679
     */
1680
    public static function getAllExerciseEventByExeId($exeId)
1681
    {
1682
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1683
        $exeId = (int) $exeId;
1684
1685
        $sql = "SELECT * FROM $table
1686
                WHERE exe_id = $exeId
1687
                ORDER BY position";
1688
        $res_question = Database::query($sql);
1689
        $list = [];
1690
        if (Database::num_rows($res_question)) {
1691
            while ($row = Database::fetch_array($res_question, 'ASSOC')) {
1692
                $list[$row['question_id']][] = $row;
1693
            }
1694
        }
1695
1696
        return $list;
1697
    }
1698
1699
    /**
1700
     * @param int $exeId
1701
     * @param int $user_id
1702
     * @param int $courseId
1703
     * @param int $session_id
1704
     * @param int $question_id
1705
     */
1706
    public static function delete_attempt(
1707
        $exeId,
1708
        $user_id,
1709
        $courseId,
1710
        $session_id,
1711
        $question_id
1712
    ) {
1713
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1714
1715
        $exeId = (int) $exeId;
1716
        $user_id = (int) $user_id;
1717
        $courseId = (int) $courseId;
1718
        $session_id = (int) $session_id;
1719
        $question_id = (int) $question_id;
1720
1721
        $sql = "DELETE FROM $table
1722
                WHERE
1723
                    exe_id = $exeId AND
1724
                    user_id = $user_id AND
1725
                    c_id = $courseId AND
1726
                    session_id = $session_id AND
1727
                    question_id = $question_id ";
1728
        Database::query($sql);
1729
1730
        self::addEvent(
1731
            LOG_QUESTION_RESULT_DELETE,
1732
            LOG_EXERCISE_ATTEMPT_QUESTION_ID,
1733
            $exeId.'-'.$question_id,
1734
            null,
1735
            null,
1736
            $courseId,
1737
            $session_id
1738
        );
1739
    }
1740
1741
    /**
1742
     * @param $exeId
1743
     * @param $user_id
1744
     * @param int $courseId
1745
     * @param $question_id
1746
     * @param int $sessionId
1747
     */
1748
    public static function delete_attempt_hotspot(
1749
        $exeId,
1750
        $user_id,
1751
        $courseId,
1752
        $question_id,
1753
        $sessionId = null
1754
    ) {
1755
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTSPOT);
1756
1757
        $exeId = (int) $exeId;
1758
        $user_id = (int) $user_id;
1759
        $courseId = (int) $courseId;
1760
        $question_id = (int) $question_id;
1761
        if (!isset($sessionId)) {
1762
            $sessionId = api_get_session_id();
1763
        }
1764
1765
        $sql = "DELETE FROM $table
1766
                WHERE   
1767
                    hotspot_exe_id = $exeId AND
1768
                    hotspot_user_id = $user_id AND
1769
                    c_id = $courseId AND
1770
                    hotspot_question_id = $question_id ";
1771
        Database::query($sql);
1772
        self::addEvent(
1773
            LOG_QUESTION_RESULT_DELETE,
1774
            LOG_EXERCISE_ATTEMPT_QUESTION_ID,
1775
            $exeId.'-'.$question_id,
1776
            null,
1777
            null,
1778
            $courseId,
1779
            $sessionId
1780
        );
1781
    }
1782
1783
    /**
1784
     * Registers in track_e_course_access when user logs in for the first time to a course.
1785
     *
1786
     * @param int $courseId  ID of the course
1787
     * @param int $user_id   ID of the user
1788
     * @param int $sessionId ID of the session (if any)
1789
     *
1790
     * @return bool
1791
     */
1792
    public static function eventCourseLogin($courseId, $user_id, $sessionId)
1793
    {
1794
        if (Session::read('login_as')) {
1795
            return false;
1796
        }
1797
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1798
        $loginDate = $logoutDate = api_get_utc_datetime();
1799
1800
        // $counter represents the number of time this record has been refreshed
1801
        $counter = 1;
1802
        $courseId = (int) $courseId;
1803
        $user_id = (int) $user_id;
1804
        $sessionId = (int) $sessionId;
1805
        $ip = Database::escape_string(api_get_real_ip());
1806
1807
        $sql = "INSERT INTO $table(c_id, user_ip, user_id, login_course_date, logout_course_date, counter, session_id)
1808
                VALUES($courseId, '$ip', $user_id, '$loginDate', '$logoutDate', $counter, $sessionId)";
1809
        $courseAccessId = Database::query($sql);
1810
1811
        if ($courseAccessId) {
1812
            // Course catalog stats modifications see #4191
1813
            CourseManager::update_course_ranking(
1814
                null,
1815
                null,
1816
                null,
1817
                null,
1818
                true,
1819
                false
1820
            );
1821
1822
            return true;
1823
        }
1824
    }
1825
1826
    /**
1827
     * Updates the user - course - session every X minutes
1828
     * In order to avoid.
1829
     *
1830
     * @param int $courseId
1831
     * @param int $userId
1832
     * @param int $sessionId
1833
     * @param int $minutes
1834
     *
1835
     * @return bool
1836
     */
1837
    public static function eventCourseLoginUpdate(
1838
        $courseId,
1839
        $userId,
1840
        $sessionId,
1841
        $minutes = 5
1842
    ) {
1843
        if (Session::read('login_as')) {
1844
            return false;
1845
        }
1846
1847
        if (empty($courseId) || empty($userId)) {
1848
            return false;
1849
        }
1850
1851
        $courseId = (int) $courseId;
1852
        $userId = (int) $userId;
1853
        $sessionId = (int) $sessionId;
1854
1855
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1856
        $sql = "SELECT course_access_id, logout_course_date 
1857
                FROM $table 
1858
                WHERE 
1859
                    c_id = $courseId AND
1860
                    session_id = $sessionId AND   
1861
                    user_id = $userId                     
1862
                ORDER BY login_course_date DESC
1863
                LIMIT 1";
1864
1865
        $result = Database::query($sql);
1866
1867
        // Save every 5 minutes by default
1868
        $seconds = $minutes * 60;
1869
        $maxSeconds = 3600; // Only update if max diff is one hour
1870
        if (Database::num_rows($result)) {
1871
            $row = Database::fetch_array($result);
1872
            $id = $row['course_access_id'];
1873
            $logout = $row['logout_course_date'];
1874
            $now = time();
1875
            $logout = api_strtotime($logout, 'UTC');
1876
            if ($now - $logout > $seconds &&
1877
                $now - $logout < $maxSeconds
1878
            ) {
1879
                $now = api_get_utc_datetime();
1880
                $sql = "UPDATE $table SET 
1881
                            logout_course_date = '$now', 
1882
                            counter = counter + 1
1883
                        WHERE course_access_id = $id";
1884
                Database::query($sql);
1885
            }
1886
1887
            return true;
1888
        }
1889
1890
        return false;
1891
    }
1892
1893
    /**
1894
     * Register the logout of the course (usually when logging out of the platform)
1895
     * from the track_e_course_access table.
1896
     *
1897
     * @param array $logoutInfo Information stored by local.inc.php
1898
     *                          before new context ['uid'=> x, 'cid'=>y, 'sid'=>z]
1899
     *
1900
     * @return bool
1901
     */
1902
    public static function courseLogout($logoutInfo)
1903
    {
1904
        if (Session::read('login_as')) {
1905
            return false;
1906
        }
1907
1908
        if (empty($logoutInfo['uid']) || empty($logoutInfo['cid'])) {
1909
            return false;
1910
        }
1911
1912
        $sessionLifetime = api_get_configuration_value('session_lifetime');
1913
        /*
1914
         * When $_configuration['session_lifetime'] is larger than ~100 hours
1915
         * (in order to let users take exercises with no problems)
1916
         * the function Tracking::get_time_spent_on_the_course() returns larger values (200h) due the condition:
1917
         * login_course_date > now() - INTERVAL $session_lifetime SECOND
1918
         */
1919
        if (empty($sessionLifetime) || $sessionLifetime > 86400) {
1920
            $sessionLifetime = 3600; // 1 hour
1921
        }
1922
        if (!empty($logoutInfo) && !empty($logoutInfo['cid'])) {
1923
            $tableCourseAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1924
            $userId = (int) $logoutInfo['uid'];
1925
            $courseId = (int) $logoutInfo['cid'];
1926
            $sessionId = 0;
1927
            if (!empty($logoutInfo['sid'])) {
1928
                $sessionId = (int) $logoutInfo['sid'];
1929
            }
1930
            $currentDate = api_get_utc_datetime();
1931
            // UTC time
1932
            $diff = time() - $sessionLifetime;
1933
            $time = api_get_utc_datetime($diff);
1934
            $sql = "SELECT course_access_id, logout_course_date
1935
                    FROM $tableCourseAccess
1936
                    WHERE 
1937
                        user_id = $userId AND
1938
                        c_id = $courseId  AND
1939
                        session_id = $sessionId AND
1940
                        login_course_date > '$time'
1941
                    ORDER BY login_course_date DESC 
1942
                    LIMIT 1";
1943
            $result = Database::query($sql);
1944
            $insert = false;
1945
            if (Database::num_rows($result) > 0) {
1946
                $row = Database::fetch_array($result, 'ASSOC');
1947
                $courseAccessId = $row['course_access_id'];
1948
                $sql = "UPDATE $tableCourseAccess SET 
1949
                                logout_course_date = '$currentDate', 
1950
                                counter = counter + 1
1951
                            WHERE course_access_id = $courseAccessId";
1952
                Database::query($sql);
1953
            } else {
1954
                $insert = true;
1955
            }
1956
1957
            if ($insert) {
1958
                $ip = Database::escape_string(api_get_real_ip());
1959
                $sql = "INSERT INTO $tableCourseAccess (c_id, user_ip, user_id, login_course_date, logout_course_date, counter, session_id)
1960
                        VALUES ($courseId, '$ip', $userId, '$currentDate', '$currentDate', 1, $sessionId)";
1961
                Database::query($sql);
1962
            }
1963
1964
            return true;
1965
        }
1966
    }
1967
1968
    /**
1969
     * Register a "fake" time spent on the platform, for example to match the
1970
     * estimated time he took to author an assignment/work, see configuration
1971
     * setting considered_working_time.
1972
     * This assumes there is already some connection of the student to the
1973
     * course, otherwise he wouldn't be able to upload an assignment.
1974
     * This works by creating a new record, copy of the current one, then
1975
     * updating the current one to be just the considered_working_time and
1976
     * end at the same second as the user connected to the course.
1977
     *
1978
     * @param int    $courseId    The course in which to add the time
1979
     * @param int    $userId      The user for whom to add the time
1980
     * @param int    $sessionId   The session in which to add the time (if any)
1981
     * @param string $virtualTime The amount of time to be added,
1982
     *                            in a hh:mm:ss format. If int, we consider it is expressed in hours.
1983
     *
1984
     * @return true on successful insertion, false otherwise
1985
     */
1986
    public static function eventAddVirtualCourseTime(
1987
        $courseId,
1988
        $userId,
1989
        $sessionId,
1990
        $virtualTime = ''
1991
    ) {
1992
        $courseId = (int) $courseId;
1993
        $userId = (int) $userId;
1994
        $sessionId = (int) $sessionId;
1995
1996
        $logoutDate = api_get_utc_datetime();
1997
        $loginDate = ChamiloApi::addOrSubTimeToDateTime(
1998
            $virtualTime,
1999
            $logoutDate,
2000
            false
2001
        );
2002
2003
        $params = [
2004
            'login_course_date' => $loginDate,
2005
            'logout_course_date' => $logoutDate,
2006
            'session_id' => $sessionId,
2007
            'user_id' => $userId,
2008
            'counter' => 0,
2009
            'c_id' => $courseId,
2010
            'user_ip' => api_get_real_ip(),
2011
        ];
2012
        $courseTrackingTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2013
        Database::insert($courseTrackingTable, $params);
2014
2015
        return true;
2016
    }
2017
2018
    /**
2019
     * Removes a "fake" time spent on the platform, for example to match the
2020
     * estimated time he took to author an assignment/work, see configuration
2021
     * setting considered_working_time.
2022
     * This method should be called when something that generated a fake
2023
     * time record is removed. Given the database link is weak (no real
2024
     * relationship kept between the deleted item and this record), this
2025
     * method just looks for the latest record that has the same time as the
2026
     * item's fake time, is in the past and in this course+session. If such a
2027
     * record cannot be found, it doesn't do anything.
2028
     * The IP address is not considered a useful filter here.
2029
     *
2030
     * @param int    $courseId    The course in which to add the time
2031
     * @param int    $userId      The user for whom to add the time
2032
     * @param int    $sessionId   The session in which to add the time (if any)
2033
     * @param string $virtualTime The amount of time to be added, in a hh:mm:ss format. If int, we consider it is expressed in hours.
2034
     *
2035
     * @return true on successful removal, false otherwise
2036
     */
2037
    public static function eventRemoveVirtualCourseTime(
2038
        $courseId,
2039
        $userId,
2040
        $sessionId = 0,
2041
        $virtualTime = ''
2042
    ) {
2043
        if (empty($virtualTime)) {
2044
            return false;
2045
        }
2046
        $courseTrackingTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2047
        $courseId = (int) $courseId;
2048
        $userId = (int) $userId;
2049
        $sessionId = (int) $sessionId;
2050
        // Change $virtualTime format from hh:mm:ss to hhmmss which is the
2051
        // format returned by SQL for a subtraction of two datetime values
2052
        // @todo make sure this is portable between DBMSes
2053
        if (preg_match('/:/', $virtualTime)) {
2054
            list($h, $m, $s) = preg_split('/:/', $virtualTime);
2055
            $virtualTime = $h * 3600 + $m * 60 + $s;
2056
        } else {
2057
            $virtualTime *= 3600;
2058
        }
2059
2060
        // Get the current latest course connection register. We need that
2061
        // record to re-use the data and create a new record.
2062
        $sql = "SELECT course_access_id
2063
                FROM $courseTrackingTable
2064
                WHERE
2065
                    user_id = $userId AND
2066
                    c_id = $courseId  AND
2067
                    session_id  = $sessionId AND
2068
                    counter = 0 AND
2069
                    (UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) = '$virtualTime'
2070
                ORDER BY login_course_date DESC LIMIT 0,1";
2071
        $result = Database::query($sql);
2072
2073
        // Ignore if we didn't find any course connection record in the last
2074
        // hour. In this case it wouldn't be right to add a "fake" time record.
2075
        if (Database::num_rows($result) > 0) {
2076
            // Found the latest connection
2077
            $row = Database::fetch_row($result);
2078
            $courseAccessId = $row[0];
2079
            $sql = "DELETE FROM $courseTrackingTable 
2080
                    WHERE course_access_id = $courseAccessId";
2081
            $result = Database::query($sql);
2082
2083
            return $result;
2084
        }
2085
2086
        return false;
2087
    }
2088
2089
    /**
2090
     * Register the logout of the course (usually when logging out of the platform)
2091
     * from the track_e_access_complete table.
2092
     *
2093
     * @param array $logInfo Information stored by local.inc.php
2094
     *
2095
     * @return bool
2096
     */
2097
    public static function registerLog($logInfo)
2098
    {
2099
        if (!Tracking::minimunTimeAvailable(api_get_session_id(), api_get_course_int_id())) {
2100
            return false;
2101
        }
2102
2103
        $loginAs = (int) Session::read('login_as') === true;
2104
2105
        $logInfo['user_id'] = api_get_user_id();
2106
        $logInfo['date_reg'] = api_get_utc_datetime();
2107
        $logInfo['tool'] = !empty($logInfo['tool']) ? $logInfo['tool'] : '';
2108
        $logInfo['tool_id'] = !empty($logInfo['tool_id']) ? (int) $logInfo['tool_id'] : 0;
2109
        $logInfo['tool_id_detail'] = !empty($logInfo['tool_id_detail']) ? (int) $logInfo['tool_id_detail'] : 0;
2110
        $logInfo['action'] = !empty($logInfo['action']) ? $logInfo['action'] : '';
2111
        $logInfo['action_details'] = !empty($logInfo['action_details']) ? $logInfo['action_details'] : '';
2112
        $logInfo['ip_user'] = api_get_real_ip();
2113
        $logInfo['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
2114
        $logInfo['session_id'] = api_get_session_id();
2115
        $logInfo['c_id'] = api_get_course_int_id();
2116
        $logInfo['ch_sid'] = session_id();
2117
        $logInfo['login_as'] = $loginAs;
2118
        $logInfo['info'] = !empty($logInfo['info']) ? $logInfo['info'] : '';
2119
        $logInfo['url'] = $_SERVER['REQUEST_URI'];
2120
        $logInfo['current_id'] = Session::read('last_id', 0);
2121
2122
        $id = Database::insert('track_e_access_complete', $logInfo);
2123
        if ($id && empty($logInfo['current_id'])) {
2124
            Session::write('last_id', $id);
2125
        }
2126
2127
        return true;
2128
    }
2129
}
2130