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

Event::updateEventExercise()   B

Complexity

Conditions 7
Paths 17

Size

Total Lines 82
Code Lines 45

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 7
eloc 45
c 0
b 0
f 0
nc 17
nop 13
dl 0
loc 82
rs 8.2666

How to fix   Long Method    Many Parameters   

Long Method

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

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

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

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