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