Completed
Push — master ( 88715c...d7c14e )
by Julito
09:34
created

Event   F

Complexity

Total Complexity 188

Size/Duplication

Total Lines 2206
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
eloc 1031
dl 0
loc 2206
rs 1.476
c 0
b 0
f 0
wmc 188

38 Methods

Rating   Name   Duplication   Size   Complexity  
A event_download() 0 31 2
A getLastAttemptDateOfExercise() 0 12 1
A get_attempt_count_not_finished() 0 33 2
A event_upload() 0 30 2
B saveExerciseAttemptHotspot() 0 66 9
C addEvent() 0 74 10
F saveQuestionAttempt() 0 181 26
A accessCourse() 0 42 4
A get_attempt_count() 0 35 2
A event_link() 0 28 2
B event_access_tool() 0 71 10
A getLatestQuestionIdFromAttempt() 0 15 2
B updateEventExercise() 0 82 7
B eventLogin() 0 34 8
A get_all_exercises_from_lp() 0 19 2
A get_best_attempt_exercise_results_per_user() 0 43 5
A delete_attempt() 0 32 1
A getAllExerciseEventByExeId() 0 17 3
A get_exercise_results_by_attempt() 0 37 4
A get_all_exercise_results_by_user() 0 34 3
A count_exercise_attempts_by_user() 0 30 2
B eventCourseLoginUpdate() 0 54 7
A eventCourseLogin() 0 31 3
A get_all_exercise_results_by_course() 0 32 4
B get_best_exercise_results_by_user() 0 55 7
B courseLogout() 0 63 11
A eventAddVirtualCourseTime() 0 78 3
A get_comments() 0 16 1
A get_all_exercise_results() 0 43 5
A get_all_exercise_event_from_lp() 0 34 3
A get_count_exercises_attempted_by_course() 0 24 2
A getExerciseResultsByUser() 0 57 5
C delete_student_lp_events() 0 119 9
B registerLog() 0 31 10
A delete_all_incomplete_attempts() 0 28 4
A count_exercise_result_not_validated() 0 28 1
A delete_attempt_hotspot() 0 32 2
A eventRemoveVirtualCourseTime() 0 52 4

How to fix   Complexity   

Complex Class

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

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

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

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
467
        // check user_id or get from context
468
        if (empty($user_id)) {
469
            $user_id = api_get_user_id();
470
            // anonymous
471
            if (empty($user_id)) {
472
                $user_id = api_get_anonymous_id();
473
            }
474
        }
475
        // check course_id or get from context
476
        if (empty($course_id) or intval($course_id) != $course_id) {
477
            $course_id = api_get_course_int_id();
478
        }
479
        // check session_id or get from context
480
        if (empty($session_id)) {
481
            $session_id = api_get_session_id();
482
        }
483
        // check learnpath_id or get from context
484
        if (empty($learnpath_id)) {
485
            global $learnpath_id;
486
        }
487
        // check learnpath_item_id or get from context
488
        if (empty($learnpath_item_id)) {
489
            global $learnpath_item_id;
490
        }
491
492
        $TBL_TRACK_ATTEMPT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
493
494
        if ($debug) {
495
            error_log("----- entering saveQuestionAttempt() function ------");
496
            error_log("answer: $answer");
497
            error_log("score: $score");
498
            error_log("question_id : $question_id");
499
            error_log("position: $position");
500
        }
501
502
        //Validation in case of fraud with active control time
503
        if (!ExerciseLib::exercise_time_control_is_valid($exercise_id, $learnpath_id, $learnpath_item_id)) {
504
            if ($debug) {
505
                error_log("exercise_time_control_is_valid is false");
506
            }
507
            $score = 0;
508
            $answer = 0;
509
        }
510
511
        $session_id = api_get_session_id();
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 $attempt['count'];
898
        } else {
899
            return 0;
900
        }
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(
980
            TABLE_STATISTIC_TRACK_E_EXERCISES
981
        );
982
        $track_attempts = Database::get_main_table(
983
            TABLE_STATISTIC_TRACK_E_ATTEMPT
984
        );
985
        $recording_table = Database::get_main_table(
986
            TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING
987
        );
988
989
        //Make sure we have the exact lp_view_id
990
        $sql = "SELECT id FROM $lp_view_table
991
                WHERE
992
                    c_id = $course_id AND
993
                    user_id = $user_id AND
994
                    lp_id = $lp_id AND
995
                    session_id = $session_id";
996
        $result = Database::query($sql);
997
998
        if (Database::num_rows($result)) {
999
            $view = Database::fetch_array($result, 'ASSOC');
1000
            $lp_view_id = $view['id'];
1001
1002
            $sql = "DELETE FROM $lp_item_view_table
1003
                    WHERE c_id = $course_id AND lp_view_id = $lp_view_id";
1004
            Database::query($sql);
1005
1006
            $sql = "DELETE FROM $lpInteraction
1007
                    WHERE c_id = $course_id AND lp_iv_id = $lp_view_id";
1008
            Database::query($sql);
1009
1010
            $sql = "DELETE FROM $lpObjective
1011
                    WHERE c_id = $course_id AND lp_iv_id = $lp_view_id";
1012
            Database::query($sql);
1013
        }
1014
1015
        if (api_get_configuration_value('lp_minimum_time')) {
1016
            $sql = "DELETE FROM track_e_access_complete
1017
                    WHERE 
1018
                        tool = 'learnpath' AND 
1019
                        c_id = $course_id AND 
1020
                        tool_id = $lp_id AND
1021
                        user_id = $user_id AND
1022
                        session_id = $session_id
1023
                    ";
1024
            Database::query($sql);
1025
        }
1026
1027
        $sql = "DELETE FROM $lp_view_table
1028
                WHERE
1029
                    c_id = $course_id AND
1030
                    user_id = $user_id AND
1031
                    lp_id= $lp_id AND
1032
                    session_id = $session_id
1033
            ";
1034
        Database::query($sql);
1035
1036
        $sql = "SELECT exe_id FROM $track_e_exercises
1037
                WHERE   
1038
                    exe_user_id = $user_id AND
1039
                    session_id = $session_id AND
1040
                    c_id = $course_id AND
1041
                    orig_lp_id = $lp_id";
1042
        $result = Database::query($sql);
1043
        $exe_list = [];
1044
        while ($row = Database::fetch_array($result, 'ASSOC')) {
1045
            $exe_list[] = $row['exe_id'];
1046
        }
1047
1048
        if (!empty($exe_list) && is_array($exe_list) && count($exe_list) > 0) {
1049
            $exeListString = implode(',', $exe_list);
1050
            $sql = "DELETE FROM $track_e_exercises
1051
                WHERE exe_id IN ($exeListString)";
1052
            Database::query($sql);
1053
1054
            $sql = "DELETE FROM $track_attempts
1055
                WHERE exe_id IN ($exeListString)";
1056
            Database::query($sql);
1057
1058
            $sql = "DELETE FROM $recording_table
1059
                WHERE exe_id IN ($exeListString)";
1060
            Database::query($sql);
1061
        }
1062
1063
        self::addEvent(
1064
            LOG_LP_ATTEMPT_DELETE,
1065
            LOG_LP_ID,
1066
            $lp_id,
1067
            null,
1068
            null,
1069
            $course_id,
1070
            $session_id
1071
        );
1072
1073
        return true;
1074
    }
1075
1076
    /**
1077
     * Delete all exercise attempts (included in LP or not).
1078
     *
1079
     * @param int user id
1080
     * @param int exercise id
1081
     * @param int $course_id
1082
     * @param int session id
1083
     */
1084
    public static function delete_all_incomplete_attempts(
1085
        $user_id,
1086
        $exercise_id,
1087
        $course_id,
1088
        $session_id = 0
1089
    ) {
1090
        $track_e_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1091
        $user_id = (int) $user_id;
1092
        $exercise_id = (int) $exercise_id;
1093
        $course_id = (int) $course_id;
1094
        $session_id = (int) $session_id;
1095
        if (!empty($user_id) && !empty($exercise_id) && !empty($course_code)) {
1096
            $sql = "DELETE FROM $track_e_exercises
1097
                    WHERE
1098
                        exe_user_id = $user_id AND
1099
                        exe_exo_id = $exercise_id AND
1100
                        c_id = $course_id AND
1101
                        session_id = $session_id AND
1102
                        status = 'incomplete' ";
1103
            Database::query($sql);
1104
            self::addEvent(
1105
                LOG_EXERCISE_RESULT_DELETE,
1106
                LOG_EXERCISE_AND_USER_ID,
1107
                $exercise_id.'-'.$user_id,
1108
                null,
1109
                null,
1110
                $course_id,
1111
                $session_id
1112
            );
1113
        }
1114
    }
1115
1116
    /**
1117
     * Gets all exercise results (NO Exercises in LPs ) from a given exercise id, course, session.
1118
     *
1119
     * @param   int     exercise id
1120
     * @param int $courseId
1121
     * @param   int     session id
1122
     *
1123
     * @return array with the results
1124
     */
1125
    public static function get_all_exercise_results(
1126
        $exercise_id,
1127
        $courseId,
1128
        $session_id = 0,
1129
        $load_question_list = true,
1130
        $user_id = null
1131
    ) {
1132
        $TABLETRACK_EXERCICES = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1133
        $TBL_TRACK_ATTEMPT = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1134
        $courseId = (int) $courseId;
1135
        $exercise_id = (int) $exercise_id;
1136
        $session_id = (int) $session_id;
1137
1138
        $user_condition = null;
1139
        if (!empty($user_id)) {
1140
            $user_id = (int) $user_id;
1141
            $user_condition = "AND exe_user_id = $user_id ";
1142
        }
1143
        $sql = "SELECT * FROM $TABLETRACK_EXERCICES
1144
                WHERE
1145
                    status = ''  AND
1146
                    c_id = $courseId AND
1147
                    exe_exo_id = $exercise_id AND
1148
                    session_id = $session_id  AND
1149
                    orig_lp_id =0 AND
1150
                    orig_lp_item_id = 0
1151
                    $user_condition
1152
                ORDER BY exe_id";
1153
        $res = Database::query($sql);
1154
        $list = [];
1155
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1156
            $list[$row['exe_id']] = $row;
1157
            if ($load_question_list) {
1158
                $sql = "SELECT * FROM $TBL_TRACK_ATTEMPT
1159
                        WHERE exe_id = {$row['exe_id']}";
1160
                $res_question = Database::query($sql);
1161
                while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
1162
                    $list[$row['exe_id']]['question_list'][$row_q['question_id']] = $row_q;
1163
                }
1164
            }
1165
        }
1166
1167
        return $list;
1168
    }
1169
1170
    /**
1171
     * Gets all exercise results (NO Exercises in LPs ) from a given exercise id, course, session.
1172
     *
1173
     * @param int $courseId
1174
     * @param   int     session id
1175
     * @param bool $get_count
1176
     *
1177
     * @return array with the results
1178
     */
1179
    public static function get_all_exercise_results_by_course(
1180
        $courseId,
1181
        $session_id = 0,
1182
        $get_count = true
1183
    ) {
1184
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1185
        $courseId = (int) $courseId;
1186
        $session_id = (int) $session_id;
1187
1188
        $select = '*';
1189
        if ($get_count) {
1190
            $select = 'count(*) as count';
1191
        }
1192
        $sql = "SELECT $select FROM $table_track_exercises
1193
                WHERE   status = ''  AND
1194
                        c_id = $courseId AND
1195
                        session_id = $session_id  AND
1196
                        orig_lp_id = 0 AND
1197
                        orig_lp_item_id = 0
1198
                ORDER BY exe_id";
1199
        $res = Database::query($sql);
1200
        if ($get_count) {
1201
            $row = Database::fetch_array($res, 'ASSOC');
1202
1203
            return $row['count'];
1204
        } else {
1205
            $list = [];
1206
            while ($row = Database::fetch_array($res, 'ASSOC')) {
1207
                $list[$row['exe_id']] = $row;
1208
            }
1209
1210
            return $list;
1211
        }
1212
    }
1213
1214
    /**
1215
     * Gets all exercise results (NO Exercises in LPs) from a given exercise id, course, session.
1216
     *
1217
     * @param   int     exercise id
1218
     * @param int $courseId
1219
     * @param   int     session id
1220
     *
1221
     * @return array with the results
1222
     */
1223
    public static function get_all_exercise_results_by_user(
1224
        $user_id,
1225
        $courseId,
1226
        $session_id = 0
1227
    ) {
1228
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1229
        $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1230
        $courseId = (int) $courseId;
1231
        $session_id = (int) $session_id;
1232
        $user_id = (int) $user_id;
1233
1234
        $sql = "SELECT * FROM $table_track_exercises
1235
                WHERE
1236
                    status = '' AND
1237
                    exe_user_id = $user_id AND
1238
                    c_id = $courseId AND
1239
                    session_id = $session_id AND
1240
                    orig_lp_id = 0 AND
1241
                    orig_lp_item_id = 0
1242
                ORDER by exe_id";
1243
1244
        $res = Database::query($sql);
1245
        $list = [];
1246
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1247
            $list[$row['exe_id']] = $row;
1248
            $sql = "SELECT * FROM $table_track_attempt 
1249
                    WHERE exe_id = {$row['exe_id']}";
1250
            $res_question = Database::query($sql);
1251
            while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
1252
                $list[$row['exe_id']]['question_list'][$row_q['question_id']] = $row_q;
1253
            }
1254
        }
1255
1256
        return $list;
1257
    }
1258
1259
    /**
1260
     * Gets exercise results (NO Exercises in LPs) from a given exercise id, course, session.
1261
     *
1262
     * @param int    $exe_id exercise id
1263
     * @param string $status
1264
     *
1265
     * @return array with the results
1266
     */
1267
    public static function get_exercise_results_by_attempt($exe_id, $status = null)
1268
    {
1269
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1270
        $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1271
        $table_track_attempt_recording = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
1272
        $exe_id = (int) $exe_id;
1273
1274
        $status = Database::escape_string($status);
1275
1276
        $sql = "SELECT * FROM $table_track_exercises
1277
                WHERE status = '$status' AND exe_id = $exe_id";
1278
1279
        $res = Database::query($sql);
1280
        $list = [];
1281
        if (Database::num_rows($res)) {
1282
            $row = Database::fetch_array($res, 'ASSOC');
1283
1284
            //Checking if this attempt was revised by a teacher
1285
            $sql_revised = "SELECT exe_id FROM $table_track_attempt_recording
1286
                            WHERE author != '' AND exe_id = $exe_id 
1287
                            LIMIT 1";
1288
            $res_revised = Database::query($sql_revised);
1289
            $row['attempt_revised'] = 0;
1290
            if (Database::num_rows($res_revised) > 0) {
1291
                $row['attempt_revised'] = 1;
1292
            }
1293
            $list[$exe_id] = $row;
1294
            $sql = "SELECT * FROM $table_track_attempt
1295
                    WHERE exe_id = $exe_id 
1296
                    ORDER BY tms ASC";
1297
            $res_question = Database::query($sql);
1298
            while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
1299
                $list[$exe_id]['question_list'][$row_q['question_id']] = $row_q;
1300
            }
1301
        }
1302
1303
        return $list;
1304
    }
1305
1306
    /**
1307
     * Gets exercise results (NO Exercises in LPs) from a given user, exercise id, course, session, lp_id, lp_item_id.
1308
     *
1309
     * @param   int     user id
1310
     * @param   int     exercise id
1311
     * @param   string  course code
1312
     * @param   int     session id
1313
     * @param   int     lp id
1314
     * @param   int     lp item id
1315
     * @param   string order asc or desc
1316
     *
1317
     * @return array with the results
1318
     */
1319
    public static function getExerciseResultsByUser(
1320
        $user_id,
1321
        $exercise_id,
1322
        $courseId,
1323
        $session_id = 0,
1324
        $lp_id = 0,
1325
        $lp_item_id = 0,
1326
        $order = null
1327
    ) {
1328
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1329
        $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1330
        $table_track_attempt_recording = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
1331
        $courseId = (int) $courseId;
1332
        $exercise_id = (int) $exercise_id;
1333
        $session_id = (int) $session_id;
1334
        $user_id = (int) $user_id;
1335
        $lp_id = (int) $lp_id;
1336
        $lp_item_id = (int) $lp_item_id;
1337
1338
        if (!in_array(strtolower($order), ['asc', 'desc'])) {
1339
            $order = 'asc';
1340
        }
1341
1342
        $sql = "SELECT * FROM $table_track_exercises
1343
                WHERE
1344
                    status 			= '' AND
1345
                    exe_user_id 	= $user_id AND
1346
                    c_id 	        = $courseId AND
1347
                    exe_exo_id 		= $exercise_id AND
1348
                    session_id 		= $session_id AND
1349
                    orig_lp_id 		= $lp_id AND
1350
                    orig_lp_item_id = $lp_item_id
1351
                ORDER by exe_id $order ";
1352
1353
        $res = Database::query($sql);
1354
        $list = [];
1355
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1356
            // Checking if this attempt was revised by a teacher
1357
            $exeId = $row['exe_id'];
1358
            $sql = "SELECT exe_id FROM $table_track_attempt_recording
1359
                    WHERE author != '' AND exe_id = $exeId
1360
                    LIMIT 1";
1361
            $res_revised = Database::query($sql);
1362
            $row['attempt_revised'] = 0;
1363
            if (Database::num_rows($res_revised) > 0) {
1364
                $row['attempt_revised'] = 1;
1365
            }
1366
            $list[$row['exe_id']] = $row;
1367
            $sql = "SELECT * FROM $table_track_attempt
1368
                    WHERE exe_id = $exeId";
1369
            $res_question = Database::query($sql);
1370
            while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
1371
                $list[$row['exe_id']]['question_list'][$row_q['question_id']][] = $row_q;
1372
            }
1373
        }
1374
1375
        return $list;
1376
    }
1377
1378
    /**
1379
     * Count exercise attempts (NO Exercises in LPs ) from a given exercise id, course, session.
1380
     *
1381
     * @param int $user_id
1382
     * @param   int     exercise id
1383
     * @param int $courseId
1384
     * @param   int     session id
1385
     *
1386
     * @return array with the results
1387
     */
1388
    public static function count_exercise_attempts_by_user(
1389
        $user_id,
1390
        $exercise_id,
1391
        $courseId,
1392
        $session_id = 0
1393
    ) {
1394
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1395
        $courseId = (int) $courseId;
1396
        $exercise_id = (int) $exercise_id;
1397
        $session_id = (int) $session_id;
1398
        $user_id = (int) $user_id;
1399
1400
        $sql = "SELECT count(*) as count 
1401
                FROM $table
1402
                WHERE status = ''  AND
1403
                    exe_user_id = $user_id AND
1404
                    c_id = $courseId AND
1405
                    exe_exo_id = $exercise_id AND
1406
                    session_id = $session_id AND
1407
                    orig_lp_id =0 AND
1408
                    orig_lp_item_id = 0
1409
                ORDER BY exe_id";
1410
        $res = Database::query($sql);
1411
        $result = 0;
1412
        if (Database::num_rows($res) > 0) {
1413
            $row = Database::fetch_array($res, 'ASSOC');
1414
            $result = $row['count'];
1415
        }
1416
1417
        return $result;
1418
    }
1419
1420
    /**
1421
     * Gets all exercise BEST results attempts (NO Exercises in LPs)
1422
     * from a given exercise id, course, session per user.
1423
     *
1424
     * @param int $exercise_id
1425
     * @param int $courseId
1426
     * @param int $session_id
1427
     * @param int $userId
1428
     *
1429
     * @return array with the results
1430
     *
1431
     * @todo rename this function
1432
     */
1433
    public static function get_best_exercise_results_by_user(
1434
        $exercise_id,
1435
        $courseId,
1436
        $session_id = 0,
1437
        $userId = 0
1438
    ) {
1439
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1440
        $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1441
        $courseId = (int) $courseId;
1442
        $exercise_id = (int) $exercise_id;
1443
        $session_id = (int) $session_id;
1444
1445
        $sql = "SELECT * FROM $table_track_exercises
1446
                WHERE
1447
                    status = '' AND
1448
                    c_id = $courseId AND
1449
                    exe_exo_id = $exercise_id AND
1450
                    session_id = $session_id AND
1451
                    orig_lp_id = 0 AND
1452
                    orig_lp_item_id = 0";
1453
1454
        if (!empty($userId)) {
1455
            $userId = (int) $userId;
1456
            $sql .= " AND exe_user_id = $userId ";
1457
        }
1458
        $sql .= " ORDER BY exe_id";
1459
1460
        $res = Database::query($sql);
1461
        $list = [];
1462
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1463
            $list[$row['exe_id']] = $row;
1464
            $exeId = $row['exe_id'];
1465
            $sql = "SELECT * FROM $table_track_attempt 
1466
                    WHERE exe_id = $exeId";
1467
            $res_question = Database::query($sql);
1468
            while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
1469
                $list[$exeId]['question_list'][$row_q['question_id']] = $row_q;
1470
            }
1471
        }
1472
1473
        // Getting the best results of every student
1474
        $best_score_return = [];
1475
        foreach ($list as $student_result) {
1476
            $user_id = $student_result['exe_user_id'];
1477
            $current_best_score[$user_id] = $student_result['score'];
1478
            if (!isset($best_score_return[$user_id]['score'])) {
1479
                $best_score_return[$user_id] = $student_result;
1480
            }
1481
1482
            if ($current_best_score[$user_id] > $best_score_return[$user_id]['score']) {
1483
                $best_score_return[$user_id] = $student_result;
1484
            }
1485
        }
1486
1487
        return $best_score_return;
1488
    }
1489
1490
    /**
1491
     * @param int $user_id
1492
     * @param int $exercise_id
1493
     * @param int $courseId
1494
     * @param int $session_id
1495
     *
1496
     * @return array
1497
     */
1498
    public static function get_best_attempt_exercise_results_per_user(
1499
        $user_id,
1500
        $exercise_id,
1501
        $courseId,
1502
        $session_id = 0
1503
    ) {
1504
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1505
        $courseId = (int) $courseId;
1506
        $exercise_id = (int) $exercise_id;
1507
        $session_id = (int) $session_id;
1508
        $user_id = (int) $user_id;
1509
1510
        $sql = "SELECT * FROM $table_track_exercises
1511
                WHERE
1512
                    status = ''  AND
1513
                    c_id = $courseId AND
1514
                    exe_exo_id = $exercise_id AND
1515
                    session_id = $session_id  AND
1516
                    exe_user_id = $user_id AND
1517
                    orig_lp_id = 0 AND
1518
                    orig_lp_item_id = 0
1519
                ORDER BY exe_id";
1520
1521
        $res = Database::query($sql);
1522
        $list = [];
1523
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1524
            $list[$row['exe_id']] = $row;
1525
        }
1526
        //Getting the best results of every student
1527
        $best_score_return = [];
1528
        $best_score_return['score'] = 0;
1529
1530
        foreach ($list as $result) {
1531
            $current_best_score = $result;
1532
            if ($current_best_score['score'] > $best_score_return['score']) {
1533
                $best_score_return = $result;
1534
            }
1535
        }
1536
        if (!isset($best_score_return['max_score'])) {
1537
            $best_score_return = [];
1538
        }
1539
1540
        return $best_score_return;
1541
    }
1542
1543
    /**
1544
     * @param int $exercise_id
1545
     * @param int $courseId
1546
     * @param int $session_id
1547
     *
1548
     * @return mixed
1549
     */
1550
    public static function count_exercise_result_not_validated(
1551
        $exercise_id,
1552
        $courseId,
1553
        $session_id = 0
1554
    ) {
1555
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1556
        $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
1557
        $courseId = (int) $courseId;
1558
        $session_id = (int) $session_id;
1559
        $exercise_id = (int) $exercise_id;
1560
1561
        $sql = "SELECT count(e.exe_id) as count
1562
                FROM $table_track_exercises e
1563
                LEFT JOIN $table_track_attempt a
1564
                ON e.exe_id = a.exe_id
1565
                WHERE
1566
                    exe_exo_id = $exercise_id AND
1567
                    c_id = $courseId AND
1568
                    e.session_id = $session_id  AND
1569
                    orig_lp_id = 0 AND
1570
                    marks IS NULL AND
1571
                    status = '' AND
1572
                    orig_lp_item_id = 0
1573
                ORDER BY e.exe_id";
1574
        $res = Database::query($sql);
1575
        $row = Database::fetch_array($res, 'ASSOC');
1576
1577
        return $row['count'];
1578
    }
1579
1580
    /**
1581
     * Gets all exercise BEST results attempts (NO Exercises in LPs)
1582
     * from a given exercise id, course, session per user.
1583
     *
1584
     * @param   int     exercise id
1585
     * @param   int   course id
1586
     * @param   int     session id
1587
     *
1588
     * @return array with the results
1589
     */
1590
    public static function get_count_exercises_attempted_by_course(
1591
        $courseId,
1592
        $session_id = 0
1593
    ) {
1594
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1595
        $courseId = (int) $courseId;
1596
        $session_id = (int) $session_id;
1597
1598
        $sql = "SELECT DISTINCT exe_exo_id, exe_user_id
1599
                FROM $table_track_exercises
1600
                WHERE
1601
                    status = '' AND
1602
                    c_id = $courseId AND
1603
                    session_id = $session_id AND
1604
                    orig_lp_id = 0 AND
1605
                    orig_lp_item_id = 0
1606
                ORDER BY exe_id";
1607
        $res = Database::query($sql);
1608
        $count = 0;
1609
        if (Database::num_rows($res) > 0) {
1610
            $count = Database::num_rows($res);
1611
        }
1612
1613
        return $count;
1614
    }
1615
1616
    /**
1617
     * Gets all exercise events from a Learning Path within a Course    nd Session.
1618
     *
1619
     * @param int $exercise_id
1620
     * @param int $courseId
1621
     * @param int $session_id
1622
     *
1623
     * @return array
1624
     */
1625
    public static function get_all_exercise_event_from_lp(
1626
        $exercise_id,
1627
        $courseId,
1628
        $session_id = 0
1629
    ) {
1630
        $table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1631
        $table_track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1632
        $courseId = (int) $courseId;
1633
        $exercise_id = (int) $exercise_id;
1634
        $session_id = (int) $session_id;
1635
1636
        $sql = "SELECT * FROM $table_track_exercises
1637
                WHERE
1638
                    status = '' AND
1639
                    c_id = $courseId AND
1640
                    exe_exo_id = $exercise_id AND
1641
                    session_id = $session_id AND
1642
                    orig_lp_id !=0 AND
1643
                    orig_lp_item_id != 0";
1644
1645
        $res = Database::query($sql);
1646
        $list = [];
1647
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1648
            $exeId = $row['exe_id'];
1649
            $list[$exeId] = $row;
1650
            $sql = "SELECT * FROM $table_track_attempt 
1651
                    WHERE exe_id = $exeId";
1652
            $res_question = Database::query($sql);
1653
            while ($row_q = Database::fetch_array($res_question, 'ASSOC')) {
1654
                $list[$exeId]['question_list'][$row_q['question_id']] = $row_q;
1655
            }
1656
        }
1657
1658
        return $list;
1659
    }
1660
1661
    /**
1662
     * Get a list of all the exercises in a given learning path.
1663
     *
1664
     * @param int $lp_id
1665
     * @param int $course_id This parameter is probably deprecated as lp_id now is a global iid
1666
     *
1667
     * @return array
1668
     */
1669
    public static function get_all_exercises_from_lp($lp_id, $course_id)
1670
    {
1671
        $lp_item_table = Database::get_course_table(TABLE_LP_ITEM);
1672
        $course_id = (int) $course_id;
1673
        $lp_id = (int) $lp_id;
1674
        $sql = "SELECT * FROM $lp_item_table
1675
                WHERE
1676
                    c_id = $course_id AND
1677
                    lp_id = $lp_id AND
1678
                    item_type = 'quiz'
1679
                ORDER BY parent_item_id, display_order";
1680
        $res = Database::query($sql);
1681
1682
        $my_exercise_list = [];
1683
        while ($row = Database::fetch_array($res, 'ASSOC')) {
1684
            $my_exercise_list[] = $row;
1685
        }
1686
1687
        return $my_exercise_list;
1688
    }
1689
1690
    /**
1691
     * This function gets the comments of an exercise.
1692
     *
1693
     * @param int $exe_id
1694
     * @param int $question_id
1695
     *
1696
     * @return string the comment
1697
     */
1698
    public static function get_comments($exe_id, $question_id)
1699
    {
1700
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1701
        $exe_id = (int) $exe_id;
1702
        $question_id = (int) $question_id;
1703
        $sql = "SELECT teacher_comment 
1704
                FROM $table
1705
                WHERE
1706
                    exe_id = $exe_id AND
1707
                    question_id = $question_id
1708
                ORDER by question_id";
1709
        $sqlres = Database::query($sql);
1710
        $comm = strval(Database::result($sqlres, 0, 'teacher_comment'));
1711
        $comm = trim($comm);
1712
1713
        return $comm;
1714
    }
1715
1716
    /**
1717
     * @param int $exeId
1718
     *
1719
     * @return array
1720
     */
1721
    public static function getAllExerciseEventByExeId($exeId)
1722
    {
1723
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1724
        $exeId = (int) $exeId;
1725
1726
        $sql = "SELECT * FROM $table
1727
                WHERE exe_id = $exeId
1728
                ORDER BY position";
1729
        $res_question = Database::query($sql);
1730
        $list = [];
1731
        if (Database::num_rows($res_question)) {
1732
            while ($row = Database::fetch_array($res_question, 'ASSOC')) {
1733
                $list[$row['question_id']][] = $row;
1734
            }
1735
        }
1736
1737
        return $list;
1738
    }
1739
1740
    /**
1741
     * @param int $exeId
1742
     * @param int $user_id
1743
     * @param int $courseId
1744
     * @param int $session_id
1745
     * @param int $question_id
1746
     */
1747
    public static function delete_attempt(
1748
        $exeId,
1749
        $user_id,
1750
        $courseId,
1751
        $session_id,
1752
        $question_id
1753
    ) {
1754
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1755
1756
        $exeId = (int) $exeId;
1757
        $user_id = (int) $user_id;
1758
        $courseId = (int) $courseId;
1759
        $session_id = (int) $session_id;
1760
        $question_id = (int) $question_id;
1761
1762
        $sql = "DELETE FROM $table
1763
                WHERE
1764
                    exe_id = $exeId AND
1765
                    user_id = $user_id AND
1766
                    c_id = $courseId AND
1767
                    session_id = $session_id AND
1768
                    question_id = $question_id ";
1769
        Database::query($sql);
1770
1771
        self::addEvent(
1772
            LOG_QUESTION_RESULT_DELETE,
1773
            LOG_EXERCISE_ATTEMPT_QUESTION_ID,
1774
            $exeId.'-'.$question_id,
1775
            null,
1776
            null,
1777
            $courseId,
1778
            $session_id
1779
        );
1780
    }
1781
1782
    /**
1783
     * @param $exeId
1784
     * @param $user_id
1785
     * @param int $courseId
1786
     * @param $question_id
1787
     * @param int $sessionId
1788
     */
1789
    public static function delete_attempt_hotspot(
1790
        $exeId,
1791
        $user_id,
1792
        $courseId,
1793
        $question_id,
1794
        $sessionId = null
1795
    ) {
1796
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTSPOT);
1797
1798
        $exeId = (int) $exeId;
1799
        $user_id = (int) $user_id;
1800
        $courseId = (int) $courseId;
1801
        $question_id = (int) $question_id;
1802
        if (!isset($sessionId)) {
1803
            $sessionId = api_get_session_id();
1804
        }
1805
1806
        $sql = "DELETE FROM $table
1807
                WHERE   
1808
                    hotspot_exe_id = $exeId AND
1809
                    hotspot_user_id = $user_id AND
1810
                    c_id = $courseId AND
1811
                    hotspot_question_id = $question_id ";
1812
        Database::query($sql);
1813
        self::addEvent(
1814
            LOG_QUESTION_RESULT_DELETE,
1815
            LOG_EXERCISE_ATTEMPT_QUESTION_ID,
1816
            $exeId.'-'.$question_id,
1817
            null,
1818
            null,
1819
            $courseId,
1820
            $sessionId
1821
        );
1822
    }
1823
1824
    /**
1825
     * Registers in track_e_course_access when user logs in for the first time to a course.
1826
     *
1827
     * @param int $courseId  ID of the course
1828
     * @param int $user_id   ID of the user
1829
     * @param int $sessionId ID of the session (if any)
1830
     *
1831
     * @return bool
1832
     */
1833
    public static function eventCourseLogin($courseId, $user_id, $sessionId)
1834
    {
1835
        if (Session::read('login_as')) {
1836
            return false;
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
        $sessionId = (int) $sessionId;
1846
        $ip = Database::escape_string(api_get_real_ip());
1847
1848
        $sql = "INSERT INTO $table(c_id, user_ip, user_id, login_course_date, logout_course_date, counter, session_id)
1849
                VALUES($courseId, '$ip', $user_id, '$loginDate', '$logoutDate', $counter, $sessionId)";
1850
        $courseAccessId = Database::query($sql);
1851
1852
        if ($courseAccessId) {
1853
            // Course catalog stats modifications see #4191
1854
            CourseManager::update_course_ranking(
1855
                null,
1856
                null,
1857
                null,
1858
                null,
1859
                true,
1860
                false
1861
            );
1862
1863
            return true;
1864
        }
1865
    }
1866
1867
    /**
1868
     * Updates the user - course - session every X minutes
1869
     * In order to avoid.
1870
     *
1871
     * @param int $courseId
1872
     * @param int $userId
1873
     * @param int $sessionId
1874
     * @param int $minutes
1875
     *
1876
     * @return bool
1877
     */
1878
    public static function eventCourseLoginUpdate(
1879
        $courseId,
1880
        $userId,
1881
        $sessionId,
1882
        $minutes = 5
1883
    ) {
1884
        if (Session::read('login_as')) {
1885
            return false;
1886
        }
1887
1888
        if (empty($courseId) || empty($userId)) {
1889
            return false;
1890
        }
1891
1892
        $courseId = (int) $courseId;
1893
        $userId = (int) $userId;
1894
        $sessionId = (int) $sessionId;
1895
1896
        $table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1897
        $sql = "SELECT course_access_id, logout_course_date 
1898
                FROM $table 
1899
                WHERE 
1900
                    c_id = $courseId AND
1901
                    session_id = $sessionId AND   
1902
                    user_id = $userId                     
1903
                ORDER BY login_course_date DESC
1904
                LIMIT 1";
1905
1906
        $result = Database::query($sql);
1907
1908
        // Save every 5 minutes by default
1909
        $seconds = $minutes * 60;
1910
        $maxSeconds = 3600; // Only update if max diff is one hour
1911
        if (Database::num_rows($result)) {
1912
            $row = Database::fetch_array($result);
1913
            $id = $row['course_access_id'];
1914
            $logout = $row['logout_course_date'];
1915
            $now = time();
1916
            $logout = api_strtotime($logout, 'UTC');
1917
            if ($now - $logout > $seconds &&
1918
                $now - $logout < $maxSeconds
1919
            ) {
1920
                $now = api_get_utc_datetime();
1921
                $sql = "UPDATE $table SET 
1922
                            logout_course_date = '$now', 
1923
                            counter = counter + 1
1924
                        WHERE course_access_id = $id";
1925
                Database::query($sql);
1926
            }
1927
1928
            return true;
1929
        }
1930
1931
        return false;
1932
    }
1933
1934
    /**
1935
     * Register the logout of the course (usually when logging out of the platform)
1936
     * from the track_e_course_access table.
1937
     *
1938
     * @param array $logoutInfo Information stored by local.inc.php
1939
     *                          before new context ['uid'=> x, 'cid'=>y, 'sid'=>z]
1940
     *
1941
     * @return bool
1942
     */
1943
    public static function courseLogout($logoutInfo)
1944
    {
1945
        if (Session::read('login_as')) {
1946
            return false;
1947
        }
1948
1949
        if (empty($logoutInfo['uid']) || empty($logoutInfo['cid'])) {
1950
            return false;
1951
        }
1952
1953
        $sessionLifetime = api_get_configuration_value('session_lifetime');
1954
        /*
1955
         * When $_configuration['session_lifetime'] is larger than ~100 hours
1956
         * (in order to let users take exercises with no problems)
1957
         * the function Tracking::get_time_spent_on_the_course() returns larger values (200h) due the condition:
1958
         * login_course_date > now() - INTERVAL $session_lifetime SECOND
1959
         */
1960
        if (empty($sessionLifetime) || $sessionLifetime > 86400) {
1961
            $sessionLifetime = 3600; // 1 hour
1962
        }
1963
        if (!empty($logoutInfo) && !empty($logoutInfo['cid'])) {
1964
            $tableCourseAccess = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
1965
            $userId = (int) $logoutInfo['uid'];
1966
            $courseId = (int) $logoutInfo['cid'];
1967
            $sessionId = 0;
1968
            if (!empty($logoutInfo['sid'])) {
1969
                $sessionId = (int) $logoutInfo['sid'];
1970
            }
1971
            $currentDate = api_get_utc_datetime();
1972
            // UTC time
1973
            $diff = time() - $sessionLifetime;
1974
            $time = api_get_utc_datetime($diff);
1975
            $sql = "SELECT course_access_id, logout_course_date
1976
                    FROM $tableCourseAccess
1977
                    WHERE 
1978
                        user_id = $userId AND
1979
                        c_id = $courseId  AND
1980
                        session_id = $sessionId AND
1981
                        login_course_date > '$time'
1982
                    ORDER BY login_course_date DESC 
1983
                    LIMIT 1";
1984
            $result = Database::query($sql);
1985
            $insert = false;
1986
            if (Database::num_rows($result) > 0) {
1987
                $row = Database::fetch_array($result, 'ASSOC');
1988
                $courseAccessId = $row['course_access_id'];
1989
                $sql = "UPDATE $tableCourseAccess SET 
1990
                                logout_course_date = '$currentDate', 
1991
                                counter = counter + 1
1992
                            WHERE course_access_id = $courseAccessId";
1993
                Database::query($sql);
1994
            } else {
1995
                $insert = true;
1996
            }
1997
1998
            if ($insert) {
1999
                $ip = Database::escape_string(api_get_real_ip());
2000
                $sql = "INSERT INTO $tableCourseAccess (c_id, user_ip, user_id, login_course_date, logout_course_date, counter, session_id)
2001
                        VALUES ($courseId, '$ip', $userId, '$currentDate', '$currentDate', 1, $sessionId)";
2002
                Database::query($sql);
2003
            }
2004
2005
            return true;
2006
        }
2007
    }
2008
2009
    /**
2010
     * Register a "fake" time spent on the platform, for example to match the
2011
     * estimated time he took to author an assignment/work, see configuration
2012
     * setting considered_working_time.
2013
     * This assumes there is already some connection of the student to the
2014
     * course, otherwise he wouldn't be able to upload an assignment.
2015
     * This works by creating a new record, copy of the current one, then
2016
     * updating the current one to be just the considered_working_time and
2017
     * end at the same second as the user connected to the course.
2018
     *
2019
     * @param int    $courseId    The course in which to add the time
2020
     * @param int    $userId      The user for whom to add the time
2021
     * @param int    $sessionId   The session in which to add the time (if any)
2022
     * @param string $virtualTime The amount of time to be added,
2023
     *                            in a hh:mm:ss format. If int, we consider it is expressed in hours.
2024
     * @param string $ip          IP address to go on record for this time record
2025
     *
2026
     * @return true on successful insertion, false otherwise
2027
     */
2028
    public static function eventAddVirtualCourseTime(
2029
        $courseId,
2030
        $userId,
2031
        $sessionId,
2032
        $virtualTime = '',
2033
        $ip = ''
2034
    ) {
2035
        $courseTrackingTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2036
        $time = $loginDate = $logoutDate = api_get_utc_datetime();
2037
2038
        $courseId = (int) $courseId;
2039
        $userId = (int) $userId;
2040
        $sessionId = (int) $sessionId;
2041
        $ip = Database::escape_string($ip);
2042
2043
        // Get the current latest course connection register. We need that
2044
        // record to re-use the data and create a new record.
2045
        $sql = "SELECT *
2046
                FROM $courseTrackingTable
2047
                WHERE
2048
                    user_id = $userId AND
2049
                    c_id = $courseId  AND
2050
                    session_id  = $sessionId AND
2051
                    login_course_date > '$time' - INTERVAL 3600 SECOND
2052
                ORDER BY login_course_date DESC 
2053
                LIMIT 0,1";
2054
        $result = Database::query($sql);
2055
2056
        // Ignore if we didn't find any course connection record in the last
2057
        // hour. In this case it wouldn't be right to add a "fake" time record.
2058
        if (Database::num_rows($result) > 0) {
2059
            // Found the latest connection
2060
            $row = Database::fetch_array($result);
2061
            $courseAccessId = $row['course_access_id'];
2062
            $courseAccessLoginDate = $row['login_course_date'];
2063
            $counter = $row['counter'];
2064
            $counter = $counter ? $counter : 0;
2065
            // Insert a new record, copy of the current one (except the logout
2066
            // date that we update to the current time)
2067
            $sql = "INSERT INTO $courseTrackingTable(
2068
                    c_id,
2069
                    user_ip, 
2070
                    user_id, 
2071
                    login_course_date, 
2072
                    logout_course_date, 
2073
                    counter, 
2074
                    session_id
2075
                ) VALUES(
2076
                    $courseId, 
2077
                    '$ip', 
2078
                    $userId, 
2079
                    '$courseAccessLoginDate', 
2080
                    '$logoutDate', 
2081
                    $counter, 
2082
                    $sessionId
2083
                )";
2084
            Database::query($sql);
2085
2086
            $loginDate = ChamiloApi::addOrSubTimeToDateTime(
2087
                $virtualTime,
2088
                $courseAccessLoginDate,
2089
                false
2090
            );
2091
            // We update the course tracking table
2092
            $sql = "UPDATE $courseTrackingTable  
2093
                    SET 
2094
                        login_course_date = '$loginDate',
2095
                        logout_course_date = '$courseAccessLoginDate',
2096
                        counter = 0
2097
                    WHERE 
2098
                        course_access_id = ".intval($courseAccessId)." AND 
2099
                        session_id = ".$sessionId;
2100
            Database::query($sql);
2101
2102
            return true;
2103
        }
2104
2105
        return false;
2106
    }
2107
2108
    /**
2109
     * Removes a "fake" time spent on the platform, for example to match the
2110
     * estimated time he took to author an assignment/work, see configuration
2111
     * setting considered_working_time.
2112
     * This method should be called when something that generated a fake
2113
     * time record is removed. Given the database link is weak (no real
2114
     * relationship kept between the deleted item and this record), this
2115
     * method just looks for the latest record that has the same time as the
2116
     * item's fake time, is in the past and in this course+session. If such a
2117
     * record cannot be found, it doesn't do anything.
2118
     * The IP address is not considered a useful filter here.
2119
     *
2120
     * @param int    $courseId    The course in which to add the time
2121
     * @param int    $userId      The user for whom to add the time
2122
     * @param int    $sessionId   The session in which to add the time (if any)
2123
     * @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.
2124
     *
2125
     * @return true on successful removal, false otherwise
2126
     */
2127
    public static function eventRemoveVirtualCourseTime(
2128
        $courseId,
2129
        $userId,
2130
        $sessionId = 0,
2131
        $virtualTime = ''
2132
    ) {
2133
        if (empty($virtualTime)) {
2134
            return false;
2135
        }
2136
        $courseTrackingTable = Database::get_main_table(TABLE_STATISTIC_TRACK_E_COURSE_ACCESS);
2137
        $time = $loginDate = $logoutDate = api_get_utc_datetime();
2138
2139
        $courseId = (int) $courseId;
2140
        $userId = (int) $userId;
2141
        $sessionId = (int) $sessionId;
2142
        // Change $virtualTime format from hh:mm:ss to hhmmss which is the
2143
        // format returned by SQL for a subtraction of two datetime values
2144
        // @todo make sure this is portable between DBMSes
2145
        if (preg_match('/:/', $virtualTime)) {
2146
            list($h, $m, $s) = preg_split('/:/', $virtualTime);
2147
            $virtualTime = $h * 3600 + $m * 60 + $s;
2148
        } else {
2149
            $virtualTime *= 3600;
2150
        }
2151
2152
        // Get the current latest course connection register. We need that
2153
        // record to re-use the data and create a new record.
2154
        $sql = "SELECT course_access_id
2155
                FROM $courseTrackingTable
2156
                WHERE
2157
                    user_id = $userId AND
2158
                    c_id = $courseId  AND
2159
                    session_id  = $sessionId AND
2160
                    counter = 0 AND
2161
                    (UNIX_TIMESTAMP(logout_course_date) - UNIX_TIMESTAMP(login_course_date)) = '$virtualTime'
2162
                ORDER BY login_course_date DESC LIMIT 0,1";
2163
        $result = Database::query($sql);
2164
2165
        // Ignore if we didn't find any course connection record in the last
2166
        // hour. In this case it wouldn't be right to add a "fake" time record.
2167
        if (Database::num_rows($result) > 0) {
2168
            // Found the latest connection
2169
            $row = Database::fetch_row($result);
2170
            $courseAccessId = $row[0];
2171
            $sql = "DELETE FROM $courseTrackingTable 
2172
                    WHERE course_access_id = $courseAccessId";
2173
            $result = Database::query($sql);
2174
2175
            return $result;
2176
        }
2177
2178
        return false;
2179
    }
2180
2181
    /**
2182
     * Register the logout of the course (usually when logging out of the platform)
2183
     * from the track_e_access_complete table.
2184
     *
2185
     * @param array $logInfo Information stored by local.inc.php
2186
     *
2187
     * @return bool
2188
     */
2189
    public static function registerLog($logInfo)
2190
    {
2191
        if (!api_get_configuration_value('lp_minimum_time')) {
2192
            return false;
2193
        }
2194
2195
        $loginAs = (int) (Session::read('login_as') === true);
2196
2197
        $logInfo['user_id'] = api_get_user_id();
2198
        $logInfo['date_reg'] = api_get_utc_datetime();
2199
        $logInfo['tool'] = !empty($logInfo['tool']) ? $logInfo['tool'] : '';
2200
        $logInfo['tool_id'] = !empty($logInfo['tool_id']) ? (int) $logInfo['tool_id'] : 0;
2201
        $logInfo['tool_id_detail'] = !empty($logInfo['tool_id_detail']) ? (int) $logInfo['tool_id_detail'] : 0;
2202
        $logInfo['action'] = !empty($logInfo['action']) ? $logInfo['action'] : '';
2203
        $logInfo['action_details'] = !empty($logInfo['action_details']) ? $logInfo['action_details'] : '';
2204
        $logInfo['ip_user'] = api_get_real_ip();
2205
        $logInfo['user_agent'] = $_SERVER['HTTP_USER_AGENT'];
2206
        $logInfo['session_id'] = api_get_session_id();
2207
        $logInfo['c_id'] = api_get_course_int_id();
2208
        $logInfo['ch_sid'] = session_id();
2209
        $logInfo['login_as'] = $loginAs;
2210
        $logInfo['info'] = !empty($logInfo['info']) ? $logInfo['info'] : '';
2211
        $logInfo['url'] = $_SERVER['REQUEST_URI'];
2212
        $logInfo['current_id'] = Session::read('last_id', 0);
2213
2214
        $id = Database::insert('track_e_access_complete', $logInfo);
2215
        if ($id && empty($logInfo['current_id'])) {
2216
            Session::write('last_id', $id);
2217
        }
2218
2219
        return true;
2220
    }
2221
}
2222