Completed
Push — master ( 255b9b...7e1a98 )
by Julito
09:17
created

ExerciseLink::needs_results()   A

Complexity

Conditions 1
Paths 1

Size

Total Lines 3
Code Lines 1

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 1
eloc 1
nc 1
nop 0
dl 0
loc 3
rs 10
c 0
b 0
f 0
1
<?php
2
/* For licensing terms, see /license.txt */
3
4
/**
5
 * Class ExerciseLink
6
 * Defines a gradebook ExerciseLink object.
7
 *
8
 * @author Bert Steppé
9
 *
10
 * @package chamilo.gradebook
11
 */
12
class ExerciseLink extends AbstractLink
13
{
14
    private $course_info;
0 ignored issues
show
introduced by
The private property $course_info is not used, and could be removed.
Loading history...
15
    private $exercise_table;
16
    private $exercise_data = [];
17
    private $is_hp;
18
19
    /**
20
     * @param int $hp
21
     */
22
    public function __construct($hp = 0)
23
    {
24
        parent::__construct();
25
        $this->set_type(LINK_EXERCISE);
26
        $this->is_hp = $hp;
27
        if ($this->is_hp == 1) {
28
            $this->set_type(LINK_HOTPOTATOES);
29
        }
30
    }
31
32
    /**
33
     * Generate an array of all exercises available.
34
     *
35
     * @param bool $getOnlyHotPotatoes
36
     *
37
     * @return array 2-dimensional array - every element contains 2 subelements (id, name)
38
     */
39
    public function get_all_links($getOnlyHotPotatoes = false)
0 ignored issues
show
Unused Code introduced by
The parameter $getOnlyHotPotatoes is not used and could be removed. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-unused  annotation

39
    public function get_all_links(/** @scrutinizer ignore-unused */ $getOnlyHotPotatoes = false)

This check looks for parameters that have been defined for a function or method, but which are not used in the method body.

Loading history...
40
    {
41
        $tableItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
42
        $exerciseTable = $this->get_exercise_table();
43
        $lpItemTable = Database::get_course_table(TABLE_LP_ITEM);
44
45
        $documentPath = api_get_path(SYS_COURSE_PATH).$this->course_code.'/document';
46
        if (empty($this->course_code)) {
47
            return [];
48
        }
49
        $sessionId = $this->get_session_id();
50
        if (empty($sessionId)) {
51
            $session_condition = api_get_session_condition(0, true);
52
        } else {
53
            $session_condition = api_get_session_condition($sessionId, true, true);
54
        }
55
56
        // @todo
57
        $uploadPath = null;
58
59
        $sql = 'SELECT iid, title FROM '.$exerciseTable.'
60
				WHERE c_id = '.$this->course_id.' AND active=1  '.$session_condition;
61
62
        $sqlLp = "SELECT e.iid, e.title 
63
                  FROM $exerciseTable e 
64
                  INNER JOIN $lpItemTable i
65
                  ON (e.c_id = i.c_id AND e.id = i.path)
66
				  WHERE 
67
				    e.c_id = $this->course_id AND 
68
				    active = 0 AND 
69
				    item_type = 'quiz'
70
				  $session_condition";
71
72
        $exerciseInLP = [];
73
        $result = Database::query($sql);
74
        $resultLp = Database::query($sqlLp);
75
        $exerciseInLP = Database::store_result($resultLp);
76
77
78
        $cats = [];
79
        if (isset($result)) {
80
            if (Database::num_rows($result) > 0) {
81
                while ($data = Database::fetch_array($result)) {
82
                    $cats[] = [$data['iid'], $data['title']];
83
                }
84
            }
85
        }
86
87
        if (!empty($exerciseInLP)) {
88
            foreach ($exerciseInLP as $exercise) {
89
                $cats[] = [
90
                    $exercise['iid'],
91
                    $exercise['title'].' ('.get_lang('Learning path').')',
92
                ];
93
            }
94
        }
95
96
        return $cats;
97
    }
98
99
    /**
100
     * Has anyone done this exercise yet ?
101
     */
102
    public function has_results()
103
    {
104
        $tbl_stats = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
105
        $sessionId = $this->get_session_id();
106
        $course_id = api_get_course_int_id($this->get_course_code());
107
        $sql = "SELECT count(exe_id) AS number 
108
                FROM $tbl_stats
109
                WHERE
110
                    session_id = $sessionId AND
111
                    c_id = $course_id AND
112
                    exe_exo_id = ".$this->get_ref_id();
113
        $result = Database::query($sql);
114
        $number = Database::fetch_row($result);
115
116
        return $number[0] != 0;
117
    }
118
119
    /**
120
     * Get the score of this exercise. Only the first attempts are taken into account.
121
     *
122
     * @param int    $stud_id student id (default: all students who have results -
123
     *                        then the average is returned)
124
     * @param string $type
125
     *
126
     * @return array (score, max) if student is given
127
     *               array (sum of scores, number of scores) otherwise
128
     *               or null if no scores available
129
     */
130
    public function calc_score($stud_id = null, $type = null)
131
    {
132
        $allowStats = api_get_configuration_value('allow_gradebook_stats');
133
134
        if ($allowStats) {
135
            $link = $this->entity;
136
            if (!empty($link)) {
137
                $weight = $link->getScoreWeight();
0 ignored issues
show
Bug introduced by
The method getScoreWeight() does not exist on Chamilo\CoreBundle\Entity\GradebookLink. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

137
                /** @scrutinizer ignore-call */ 
138
                $weight = $link->getScoreWeight();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
138
139
                switch ($type) {
140
                    case 'best':
141
                        $bestResult = $link->getBestScore();
0 ignored issues
show
Bug introduced by
The method getBestScore() does not exist on Chamilo\CoreBundle\Entity\GradebookLink. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

141
                        /** @scrutinizer ignore-call */ 
142
                        $bestResult = $link->getBestScore();

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
142
                        $result = [$bestResult, $weight];
143
144
                        return $result;
145
                        break;
0 ignored issues
show
Unused Code introduced by
break is not strictly necessary here and could be removed.

The break statement is not necessary if it is preceded for example by a return statement:

switch ($x) {
    case 1:
        return 'foo';
        break; // This break is not necessary and can be left off.
}

If you would like to keep this construct to be consistent with other case statements, you can safely mark this issue as a false-positive.

Loading history...
146
                    case 'average':
147
                        $count = count($this->getStudentList());
148
                        if (empty($count)) {
149
                            $result = [0, $weight];
150
151
                            return $result;
152
                        }
153
                        $sumResult = array_sum($link->getUserScoreList());
0 ignored issues
show
Bug introduced by
The method getUserScoreList() does not exist on Chamilo\CoreBundle\Entity\GradebookLink. ( Ignorable by Annotation )

If this is a false-positive, you can also ignore this issue in your code via the ignore-call  annotation

153
                        $sumResult = array_sum($link->/** @scrutinizer ignore-call */ getUserScoreList());

This check looks for calls to methods that do not seem to exist on a given type. It looks for the method on the type itself as well as in inherited classes or implemented interfaces.

This is most likely a typographical error or the method has been renamed.

Loading history...
154
                        $result = [$sumResult / $count, $weight];
155
156
                        return $result;
157
                        break;
158
                    case 'ranking':
159
                        return [null, null];
160
                        break;
161
                    default:
162
                        if (!empty($stud_id)) {
163
                            $scoreList = $link->getUserScoreList();
164
                            $result = [0, $weight];
165
                            if (isset($scoreList[$stud_id])) {
166
                                $result = [$scoreList[$stud_id], $weight];
167
                            }
168
169
                            return $result;
170
                        } else {
171
                            $studentCount = count($this->getStudentList());
172
                            $sumResult = array_sum($link->getUserScoreList());
173
                            $result = [$sumResult, $studentCount];
174
                        }
175
176
                        return $result;
177
                        break;
178
                }
179
            }
180
        }
181
182
        $tblStats = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
183
        $tblHp = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTPOTATOES);
184
        $tblDoc = Database::get_course_table(TABLE_DOCUMENT);
185
186
        /* the following query should be similar (in conditions) to the one used
187
        in exercise/exercise.php, look for note-query-exe-results marker*/
188
        $sessionId = $this->get_session_id();
189
        $courseId = $this->getCourseId();
190
        $exerciseData = $this->get_exercise_data();
191
192
        $exerciseId = isset($exerciseData['id']) ? (int) $exerciseData['id'] : 0;
193
        $stud_id = (int) $stud_id;
194
195
        if (empty($exerciseId)) {
196
            return null;
197
        }
198
199
        $key = 'exercise_link_id:'.
200
            $this->get_id().
201
            'exerciseId:'.$exerciseId.'student:'.$stud_id.'session:'.$sessionId.'courseId:'.$courseId.'type:'.$type;
202
203
        $useCache = api_get_configuration_value('gradebook_use_apcu_cache');
204
        $cacheAvailable = api_get_configuration_value('apc') && $useCache;
205
        $cacheDriver = null;
206
        if ($cacheAvailable) {
207
            $cacheDriver = new \Doctrine\Common\Cache\ApcuCache();
208
            if ($cacheDriver->contains($key)) {
209
                return $cacheDriver->fetch($key);
210
            }
211
        }
212
213
        $exercise = new Exercise($courseId);
214
        $exercise->read($exerciseId);
215
216
        if (!$this->is_hp) {
217
            if ($exercise->exercise_was_added_in_lp == 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...
218
                $sql = "SELECT * FROM $tblStats
219
                        WHERE
220
                            exe_exo_id = $exerciseId AND
221
                            orig_lp_id = 0 AND
222
                            orig_lp_item_id = 0 AND
223
                            status <> 'incomplete' AND
224
                            session_id = $sessionId AND
225
                            c_id = $courseId
226
                        ";
227
            } else {
228
                $lpId = null;
229
                if (!empty($exercise->lpList)) {
230
                    // Taking only the first LP
231
                    $lpId = current($exercise->lpList);
232
                    $lpId = $lpId['lp_id'];
233
                }
234
235
                $sql = "SELECT * 
236
                        FROM $tblStats
237
                        WHERE
238
                            exe_exo_id = $exerciseId AND
239
                            orig_lp_id = $lpId AND
240
                            status <> 'incomplete' AND
241
                            session_id = $sessionId AND
242
                            c_id = $courseId ";
243
            }
244
245
            if (!empty($stud_id) && $type != 'ranking') {
246
                $sql .= " AND exe_user_id = $stud_id ";
247
            }
248
            $sql .= ' ORDER BY exe_id DESC';
249
        } else {
250
            $sql = "SELECT * FROM $tblHp hp 
251
                    INNER JOIN $tblDoc doc
252
                    ON (hp.exe_name = doc.path AND doc.c_id = hp.c_id)
253
                    WHERE
254
                        hp.c_id = $courseId AND                        
255
                        doc.id = $exerciseId";
256
257
            if (!empty($stud_id)) {
258
                $sql .= " AND hp.exe_user_id = $stud_id ";
259
            }
260
        }
261
262
        $scores = Database::query($sql);
263
264
        if (isset($stud_id) && empty($type)) {
265
            // for 1 student
266
            if ($data = Database::fetch_array($scores)) {
267
                $attempts = Database::query($sql);
268
                $counter = 0;
269
                while ($attempt = Database::fetch_array($attempts)) {
270
                    $counter++;
271
                }
272
                $result = [$data['score'], $data['max_score'], $data['exe_date'], $counter];
273
                if ($cacheAvailable) {
274
                    $cacheDriver->save($key, $result);
275
                }
276
277
                return $result;
278
            } else {
279
                if ($cacheAvailable) {
280
                    $cacheDriver->save($key, null);
281
                }
282
283
                return null;
284
            }
285
        } else {
286
            // all students -> get average
287
            // normal way of getting the info
288
            $students = []; // user list, needed to make sure we only
289
            // take first attempts into account
290
            $student_count = 0;
291
            $sum = 0;
292
            $bestResult = 0;
293
            $weight = 0;
294
            $sumResult = 0;
295
296
            $studentList = $this->getStudentList();
297
            $studentIdList = [];
298
            if (!empty($studentList)) {
299
                $studentIdList = array_column($studentList, 'user_id');
300
            }
301
302
            while ($data = Database::fetch_array($scores, 'ASSOC')) {
303
                // Only take into account users in the current student list.
304
                if (!empty($studentIdList)) {
305
                    if (!in_array($data['exe_user_id'], $studentIdList)) {
306
                        continue;
307
                    }
308
                }
309
310
                if (!isset($students[$data['exe_user_id']])) {
311
                    if ($data['max_score'] != 0) {
312
                        $students[$data['exe_user_id']] = $data['score'];
313
                        $student_count++;
314
                        if ($data['score'] > $bestResult) {
315
                            $bestResult = $data['score'];
316
                        }
317
                        $sum += $data['score'] / $data['max_score'];
318
                        $sumResult += $data['score'];
319
                        $weight = $data['max_score'];
320
                    }
321
                }
322
            }
323
324
            if ($student_count == 0) {
325
                if ($cacheAvailable) {
326
                    $cacheDriver->save($key, null);
327
                }
328
329
                return null;
330
            } else {
331
                switch ($type) {
332
                    case 'best':
333
                        $result = [$bestResult, $weight];
334
                        if ($cacheAvailable) {
335
                            $cacheDriver->save($key, $result);
336
                        }
337
338
                        return $result;
339
                        break;
340
                    case 'average':
341
                        $count = count($this->getStudentList());
342
                        if (empty($count)) {
343
                            $result = [0, $weight];
344
                            if ($cacheAvailable) {
345
                                $cacheDriver->save($key, $result);
346
                            }
347
348
                            return $result;
349
                        }
350
351
                        $result = [$sumResult / $count, $weight];
352
353
                        if ($cacheAvailable) {
354
                            $cacheDriver->save($key, $result);
355
                        }
356
357
                        return $result;
358
                        break;
359
                    case 'ranking':
360
                        $ranking = AbstractLink::getCurrentUserRanking($stud_id, $students);
361
                        if ($cacheAvailable) {
362
                            $cacheDriver->save($key, $ranking);
363
                        }
364
365
                        return $ranking;
366
                        break;
367
                    default:
368
                        $result = [$sum, $student_count];
369
                        if ($cacheAvailable) {
370
                            $cacheDriver->save($key, $result);
371
                        }
372
373
                        return $result;
374
                        break;
375
                }
376
            }
377
        }
378
    }
379
380
    /**
381
     * Get URL where to go to if the user clicks on the link.
382
     * First we go to exercise_jump.php and then to the result page.
383
     * Check this php file for more info.
384
     */
385
    public function get_link()
386
    {
387
        $sessionId = $this->get_session_id();
388
        $data = $this->get_exercise_data();
389
        $exerciseId = $data['id'];
390
        $path = isset($data['path']) ? $data['path'] : '';
391
392
        $url = api_get_path(WEB_CODE_PATH).'gradebook/exercise_jump.php?'
393
            .http_build_query(
394
                [
395
                    'path' => $path,
396
                    'session_id' => $sessionId,
397
                    'cidReq' => $this->get_course_code(),
398
                    'gradebook' => 'view',
399
                    'exerciseId' => $exerciseId,
400
                    'type' => $this->get_type(),
401
                ]
402
            );
403
404
        return $url;
405
    }
406
407
    /**
408
     * Get name to display: same as exercise title.
409
     */
410
    public function get_name()
411
    {
412
        $data = $this->get_exercise_data();
413
414
        return $data['title'];
415
    }
416
417
    /**
418
     * Get description to display: same as exercise description.
419
     */
420
    public function get_description()
421
    {
422
        $data = $this->get_exercise_data();
423
424
        return isset($data['description']) ? $data['description'] : null;
425
    }
426
427
    /**
428
     * Check if this still links to an exercise.
429
     */
430
    public function is_valid_link()
431
    {
432
        $exerciseData = $this->get_exercise_data();
433
434
        return !empty($exerciseData);
435
    }
436
437
    /**
438
     * @return string
439
     */
440
    public function get_type_name()
441
    {
442
        if ($this->is_hp == 1) {
443
            return 'HotPotatoes';
444
        }
445
446
        return get_lang('Tests');
447
    }
448
449
    public function needs_name_and_description()
450
    {
451
        return false;
452
    }
453
454
    public function needs_max()
455
    {
456
        return false;
457
    }
458
459
    public function needs_results()
460
    {
461
        return false;
462
    }
463
464
    public function is_allowed_to_change_name()
465
    {
466
        return false;
467
    }
468
469
    /**
470
     * @return string
471
     */
472
    public function get_icon_name()
473
    {
474
        return 'exercise';
475
    }
476
477
    /**
478
     * @param bool $hp
479
     */
480
    public function setHp($hp)
481
    {
482
        $this->hp = $hp;
0 ignored issues
show
Bug Best Practice introduced by
The property hp does not exist. Although not strictly required by PHP, it is generally a best practice to declare properties explicitly.
Loading history...
483
    }
484
485
    public function getBestScore()
486
    {
487
        return $this->getStats('best');
0 ignored issues
show
Bug introduced by
Are you sure the usage of $this->getStats('best') targeting ExerciseLink::getStats() seems to always return null.

This check looks for function or method calls that always return null and whose return value is used.

class A
{
    function getObject()
    {
        return null;
    }

}

$a = new A();
if ($a->getObject()) {

The method getObject() can return nothing but null, so it makes no sense to use the return value.

The reason is most likely that a function or method is imcomplete or has been reduced for debug purposes.

Loading history...
488
    }
489
490
    public function getStats($type)
491
    {
492
        switch ($type) {
493
            case 'best':
494
                break;
495
        }
496
    }
497
498
    /**
499
     * Lazy load function to get the database contents of this exercise.
500
     */
501
    public function get_exercise_data()
502
    {
503
        $tableItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
504
        if ($this->is_hp == 1) {
505
            $table = Database::get_course_table(TABLE_DOCUMENT);
506
        } else {
507
            $table = Database::get_course_table(TABLE_QUIZ_TEST);
508
        }
509
510
        $exerciseId = $this->get_ref_id();
511
512
        if (empty($this->exercise_data)) {
513
            if ($this->is_hp == 1) {
514
                $sql = "SELECT * FROM $table ex
515
                    INNER JOIN $tableItemProperty ip
516
                    ON (ip.ref = ex.id AND ip.c_id = ex.c_id)
517
                    WHERE
518
                        ip.c_id = $this->course_id AND
519
                        ex.c_id = $this->course_id AND
520
                        ip.ref = $exerciseId AND
521
                        ip.tool = '".TOOL_DOCUMENT."' AND
522
                        ex.path LIKE '%htm%' AND
523
                        ex.path LIKE '%HotPotatoes_files%' AND
524
                        ip.visibility = 1";
525
                $result = Database::query($sql);
526
                $this->exercise_data = Database::fetch_array($result);
527
            } else {
528
                // Try with iid
529
                $sql = 'SELECT * FROM '.$table.'
530
                    WHERE
531
                        c_id = '.$this->course_id.' AND
532
                        iid = '.$exerciseId;
533
                $result = Database::query($sql);
534
                $rows = Database::num_rows($result);
535
536
                if (!empty($rows)) {
537
                    $this->exercise_data = Database::fetch_array($result);
538
                } else {
539
                    // Try wit id
540
                    $sql = 'SELECT * FROM '.$table.'
541
                        WHERE
542
                            c_id = '.$this->course_id.' AND
543
                            id = '.$exerciseId;
544
                    $result = Database::query($sql);
545
                    $this->exercise_data = Database::fetch_array($result);
546
                }
547
            }
548
        }
549
550
        if (empty($this->exercise_data)) {
551
            return false;
552
        }
553
554
        return $this->exercise_data;
555
    }
556
557
    /**
558
     * Lazy load function to get the database table of the exercise.
559
     */
560
    private function get_exercise_table()
561
    {
562
        $this->exercise_table = Database::get_course_table(TABLE_QUIZ_TEST);
563
564
        return $this->exercise_table;
565
    }
566
}
567