Passed
Push — master ( f801c1...492dcf )
by Julito
10:30 queued 10s
created

Event::saveQuestionAttempt()   F

Complexity

Conditions 26
Paths > 20000

Size

Total Lines 181
Code Lines 108

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 26
eloc 108
c 0
b 0
f 0
nc 115488
nop 13
dl 0
loc 181
rs 0

How to fix   Long Method    Complexity    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::subscribe_user($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;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
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;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
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;
0 ignored issues
show
Bug Best Practice introduced by
The expression return false returns the type false which is incompatible with the documented return type integer.
Loading history...
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;
0 ignored issues
show
Bug Best Practice introduced by
The expression return $attempt_id also could return the type integer|string which is incompatible with the documented return type boolean.
Loading history...
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) {
0 ignored issues
show
Coding Style Best Practice introduced by
It seems like you are loosely comparing two booleans. Considering using the strict comparison === instead.

When comparing two booleans, it is generally considered safer to use the strict comparison operator.

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