Passed
Push — master ( 15378b...a2dd4f )
by Julito
09:24
created

Event::eventAddVirtualCourseTime()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 30
Code Lines 19

Duplication

Lines 0
Ratio 0 %

Importance

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