Passed
Push — 1.11.x ( 601fd2...69ae39 )
by Julito
11:13 queued 26s
created

ExerciseLink::needs_max()   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
3
/* For licensing terms, see /license.txt */
4
5
/**
6
 * Class ExerciseLink
7
 * Defines a gradebook ExerciseLink object.
8
 *
9
 * @author Bert Steppé
10
 */
11
class ExerciseLink extends AbstractLink
12
{
13
    private $course_info;
0 ignored issues
show
introduced by
The private property $course_info is not used, and could be removed.
Loading history...
14
    private $exercise_table;
15
    private $exercise_data = [];
16
    private $is_hp;
17
18
    /**
19
     * @param int $hp
20
     */
21
    public function __construct($hp = 0)
22
    {
23
        parent::__construct();
24
        $this->set_type(LINK_EXERCISE);
25
        $this->is_hp = $hp;
26
        if (1 == $this->is_hp) {
27
            $this->set_type(LINK_HOTPOTATOES);
28
        }
29
    }
30
31
    /**
32
     * Generate an array of all exercises available.
33
     *
34
     * @param bool $getOnlyHotPotatoes
35
     *
36
     * @return array 2-dimensional array - every element contains 2 subelements (id, name)
37
     */
38
    public function get_all_links($getOnlyHotPotatoes = false)
39
    {
40
        $TBL_DOCUMENT = Database::get_course_table(TABLE_DOCUMENT);
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
        $lpTable = Database::get_course_table(TABLE_LP_MAIN);
45
46
        $documentPath = api_get_path(SYS_COURSE_PATH).$this->course_code.'/document';
47
        if (empty($this->course_code)) {
48
            return [];
49
        }
50
        $sessionId = $this->get_session_id();
51
        if (empty($sessionId)) {
52
            $session_condition = api_get_session_condition(0, true, false, 'e.session_id');
53
        } else {
54
            $session_condition = api_get_session_condition($sessionId, true, true, 'e.session_id');
55
        }
56
57
        // @todo
58
        $uploadPath = null;
59
        $courseId = $this->course_id;
60
61
        $sql = "SELECT iid, title FROM $exerciseTable e
62
				WHERE
63
				    c_id = $courseId AND
64
				    active = 1
65
				    $session_condition ";
66
67
        $sqlLp = "SELECT e.iid, e.title, lp.name lp_name
68
                  FROM $exerciseTable e
69
                  INNER JOIN $lpItemTable i
70
                  ON (e.c_id = i.c_id AND e.id = i.path)
71
                  INNER JOIN $lpTable lp
72
                  ON (lp.c_id = e.c_id AND lp.id = i.lp_id)
73
				  WHERE
74
				    e.c_id = $courseId AND
75
				    active = 0 AND
76
				    item_type = 'quiz'
77
				    $session_condition";
78
79
        $sql2 = "SELECT d.path as path, d.comment as comment, ip.visibility as visibility, d.id
80
                FROM $TBL_DOCUMENT d
81
                INNER JOIN $tableItemProperty ip
82
                ON (d.id = ip.ref AND d.c_id = ip.c_id)
83
                WHERE
84
                    d.c_id = $courseId AND
85
                    ip.c_id = $courseId AND
86
                    ip.tool = '".TOOL_DOCUMENT."' AND
87
                    (d.path LIKE '%htm%') AND
88
                    (d.path LIKE '%HotPotatoes_files%') AND
89
                    d.path  LIKE '".Database::escape_string($uploadPath.'/%/%')."' AND
90
                    ip.visibility = '1'
91
                ";
92
93
        require_once api_get_path(SYS_CODE_PATH).'exercise/hotpotatoes.lib.php';
94
95
        $exerciseInLP = [];
96
        if (!$this->is_hp) {
97
            $result = Database::query($sql);
98
            $resultLp = Database::query($sqlLp);
99
            $exerciseInLP = Database::store_result($resultLp);
100
        } else {
101
            $result2 = Database::query($sql2);
102
        }
103
104
        $cats = [];
105
        if (isset($result)) {
106
            if (Database::num_rows($result) > 0) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $result does not seem to be defined for all execution paths leading up to this point.
Loading history...
107
                while ($data = Database::fetch_array($result)) {
108
                    $cats[] = [$data['iid'], $data['title']];
109
                }
110
            }
111
        }
112
        $hotPotatoes = [];
113
        if (isset($result2)) {
114
            if (Database::num_rows($result2) > 0) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $result2 does not seem to be defined for all execution paths leading up to this point.
Loading history...
115
                while ($row = Database::fetch_array($result2)) {
116
                    $attribute['path'][] = $row['path'];
117
                    $attribute['visibility'][] = $row['visibility'];
118
                    $attribute['comment'][] = $row['comment'];
119
                    $attribute['id'] = $row['id'];
120
121
                    if (isset($attribute['path']) && is_array($attribute['path'])) {
0 ignored issues
show
Comprehensibility Best Practice introduced by
The variable $attribute does not seem to be defined for all execution paths leading up to this point.
Loading history...
122
                        foreach ($attribute['path'] as $path) {
123
                            $title = GetQuizName($path, $documentPath);
124
                            if ($title == '') {
125
                                $title = basename($path);
126
                            }
127
                            $element = [$attribute['id'], $title.'(HP)'];
128
                            $cats[] = $element;
129
                            $hotPotatoes[] = $element;
130
                        }
131
                    }
132
                }
133
            }
134
        }
135
136
        if ($getOnlyHotPotatoes) {
137
            return $hotPotatoes;
138
        }
139
140
        if (!empty($exerciseInLP)) {
141
            foreach ($exerciseInLP as $exercise) {
142
                $lpName = strip_tags($exercise['lp_name']);
143
                $cats[] = [
144
                    $exercise['iid'],
145
                    strip_tags(Exercise::get_formated_title_variable($exercise['title'])).
146
                    ' ('.get_lang('ToolLearnpath').' - '.$lpName.')',
147
                ];
148
            }
149
        }
150
151
        return $cats;
152
    }
153
154
    /**
155
     * Has anyone done this exercise yet ?
156
     */
157
    public function has_results()
158
    {
159
        $tbl_stats = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
160
        $sessionId = $this->get_session_id();
161
        $course_id = api_get_course_int_id($this->get_course_code());
162
        $sql = "SELECT count(exe_id) AS number
163
                FROM $tbl_stats
164
                WHERE
165
                    session_id = $sessionId AND
166
                    c_id = $course_id AND
167
                    exe_exo_id = ".$this->get_ref_id();
168
        $result = Database::query($sql);
169
        $number = Database::fetch_row($result);
170
171
        return $number[0] != 0;
172
    }
173
174
    /**
175
     * Get the score of this exercise. Only the first attempts are taken into account.
176
     *
177
     * @param int    $stud_id student id (default: all students who have results -
178
     *                        then the average is returned)
179
     * @param string $type
180
     *
181
     * @return array (score, max) if student is given
182
     *               array (sum of scores, number of scores) otherwise
183
     *               or null if no scores available
184
     */
185
    public function calc_score($stud_id = null, $type = null)
186
    {
187
        $allowStats = api_get_configuration_value('allow_gradebook_stats');
188
189
        if ($allowStats) {
190
            $link = $this->entity;
191
            if (!empty($link)) {
192
                $weight = $link->getScoreWeight();
193
194
                switch ($type) {
195
                    case 'best':
196
                        $bestResult = $link->getBestScore();
197
                        $result = [$bestResult, $weight];
198
199
                        return $result;
200
                        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...
201
                    case 'average':
202
                        $count = count($this->getStudentList());
203
                        if (empty($count)) {
204
                            $result = [0, $weight];
205
206
                            return $result;
207
                        }
208
                        $sumResult = array_sum($link->getUserScoreList());
209
                        $result = [$sumResult / $count, $weight];
210
211
                        return $result;
212
                        break;
213
                    case 'ranking':
214
                        return [null, null];
215
                        break;
216
                    default:
217
                        if (!empty($stud_id)) {
218
                            $scoreList = $link->getUserScoreList();
219
                            $result = [0, $weight];
220
                            if (isset($scoreList[$stud_id])) {
221
                                $result = [$scoreList[$stud_id], $weight];
222
                            }
223
224
                            return $result;
225
                        } else {
226
                            $studentCount = count($this->getStudentList());
227
                            $sumResult = array_sum($link->getUserScoreList());
228
                            $result = [$sumResult, $studentCount];
229
                        }
230
231
                        return $result;
232
                        break;
233
                }
234
            }
235
        }
236
237
        $tblStats = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
238
        $tblHp = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTPOTATOES);
239
        $tblDoc = Database::get_course_table(TABLE_DOCUMENT);
240
241
        /* the following query should be similar (in conditions) to the one used
242
        in exercise/exercise.php, look for note-query-exe-results marker*/
243
        $sessionId = $this->get_session_id();
244
        $courseId = $this->getCourseId();
245
        $exerciseData = $this->get_exercise_data();
246
247
        $exerciseId = isset($exerciseData['id']) ? (int) $exerciseData['id'] : 0;
248
        $stud_id = (int) $stud_id;
249
250
        if (empty($exerciseId)) {
251
            return null;
252
        }
253
254
        $key = 'exercise_link_id:'.
255
            $this->get_id().
256
            'exerciseId:'.$exerciseId.'student:'.$stud_id.'session:'.$sessionId.'courseId:'.$courseId.'type:'.$type;
257
258
        $useCache = api_get_configuration_value('gradebook_use_apcu_cache');
259
        $cacheAvailable = api_get_configuration_value('apc') && $useCache;
260
        $cacheDriver = null;
261
        if ($cacheAvailable) {
262
            $cacheDriver = new \Doctrine\Common\Cache\ApcuCache();
263
            if ($cacheDriver->contains($key)) {
264
                return $cacheDriver->fetch($key);
265
            }
266
        }
267
268
        $exercise = new Exercise($courseId);
269
        $exercise->read($exerciseId);
270
271
        if (!$this->is_hp) {
272
            if (false == $exercise->exercise_was_added_in_lp) {
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...
273
                $sql = "SELECT * FROM $tblStats
274
                        WHERE
275
                            exe_exo_id = $exerciseId AND
276
                            orig_lp_id = 0 AND
277
                            orig_lp_item_id = 0 AND
278
                            status <> 'incomplete' AND
279
                            session_id = $sessionId AND
280
                            c_id = $courseId
281
                        ";
282
            } else {
283
                $lpId = null;
284
                if (!empty($exercise->lpList)) {
285
                    $lpId = $exercise->getLpBySession($sessionId);
286
                    $lpId = $lpId['lp_id'];
287
                }
288
289
                $sql = "SELECT *
290
                        FROM $tblStats
291
                        WHERE
292
                            exe_exo_id = $exerciseId AND
293
                            orig_lp_id = $lpId AND
294
                            status <> 'incomplete' AND
295
                            session_id = $sessionId AND
296
                            c_id = $courseId ";
297
            }
298
299
            if (!empty($stud_id) && 'ranking' != $type) {
300
                $sql .= " AND exe_user_id = $stud_id ";
301
            }
302
            $sql .= ' ORDER BY exe_id DESC';
303
        } else {
304
            $sql = "SELECT * FROM $tblHp hp
305
                    INNER JOIN $tblDoc doc
306
                    ON (hp.exe_name = doc.path AND doc.c_id = hp.c_id)
307
                    WHERE
308
                        hp.c_id = $courseId AND
309
                        doc.id = $exerciseId";
310
311
            if (!empty($stud_id)) {
312
                $sql .= " AND hp.exe_user_id = $stud_id ";
313
            }
314
        }
315
316
        $scores = Database::query($sql);
317
318
        if (isset($stud_id) && empty($type)) {
319
            // for 1 student
320
            if ($data = Database::fetch_array($scores, 'ASSOC')) {
321
                $attempts = Database::query($sql);
322
                $counter = 0;
323
                while ($attempt = Database::fetch_array($attempts)) {
324
                    $counter++;
325
                }
326
                $result = [$data['exe_result'], $data['exe_weighting'], $data['exe_date'], $counter];
327
                if ($cacheAvailable) {
328
                    $cacheDriver->save($key, $result);
329
                }
330
331
                return $result;
332
            } else {
333
                if ($cacheAvailable) {
334
                    $cacheDriver->save($key, null);
335
                }
336
337
                return null;
338
            }
339
        } else {
340
            // all students -> get average
341
            // normal way of getting the info
342
            $students = []; // user list, needed to make sure we only
343
            // take first attempts into account
344
            $student_count = 0;
345
            $sum = 0;
346
            $bestResult = 0;
347
            $weight = 0;
348
            $sumResult = 0;
349
350
            $studentList = $this->getStudentList();
351
            $studentIdList = [];
352
            if (!empty($studentList)) {
353
                $studentIdList = array_column($studentList, 'user_id');
354
            }
355
356
            while ($data = Database::fetch_array($scores, 'ASSOC')) {
357
                // Only take into account users in the current student list.
358
                if (!empty($studentIdList)) {
359
                    if (!in_array($data['exe_user_id'], $studentIdList)) {
360
                        continue;
361
                    }
362
                }
363
364
                if (!isset($students[$data['exe_user_id']])) {
365
                    if ($data['exe_weighting'] != 0) {
366
                        $students[$data['exe_user_id']] = $data['exe_result'];
367
                        $student_count++;
368
                        if ($data['exe_result'] > $bestResult) {
369
                            $bestResult = $data['exe_result'];
370
                        }
371
                        $sum += $data['exe_result'] / $data['exe_weighting'];
372
                        $sumResult += $data['exe_result'];
373
                        $weight = $data['exe_weighting'];
374
                    }
375
                }
376
            }
377
378
            if ($student_count == 0) {
379
                if ($cacheAvailable) {
380
                    $cacheDriver->save($key, null);
381
                }
382
383
                return null;
384
            } else {
385
                switch ($type) {
386
                    case 'best':
387
                        $result = [$bestResult, $weight];
388
                        if ($cacheAvailable) {
389
                            $cacheDriver->save($key, $result);
390
                        }
391
392
                        return $result;
393
                        break;
394
                    case 'average':
395
                        $count = count($this->getStudentList());
396
                        if (empty($count)) {
397
                            $result = [0, $weight];
398
                            if ($cacheAvailable) {
399
                                $cacheDriver->save($key, $result);
400
                            }
401
402
                            return $result;
403
                        }
404
405
                        $result = [$sumResult / $count, $weight];
406
407
                        if ($cacheAvailable) {
408
                            $cacheDriver->save($key, $result);
409
                        }
410
411
                        return $result;
412
                        break;
413
                    case 'ranking':
414
                        $ranking = AbstractLink::getCurrentUserRanking($stud_id, $students);
415
                        if ($cacheAvailable) {
416
                            $cacheDriver->save($key, $ranking);
417
                        }
418
419
                        return $ranking;
420
                        break;
421
                    default:
422
                        $result = [$sum, $student_count];
423
                        if ($cacheAvailable) {
424
                            $cacheDriver->save($key, $result);
425
                        }
426
427
                        return $result;
428
                        break;
429
                }
430
            }
431
        }
432
    }
433
434
    /**
435
     * Get URL where to go to if the user clicks on the link.
436
     * First we go to exercise_jump.php and then to the result page.
437
     * Check this php file for more info.
438
     */
439
    public function get_link()
440
    {
441
        $sessionId = $this->get_session_id();
442
        $data = $this->get_exercise_data();
443
        $exerciseId = $data['id'];
444
        $path = isset($data['path']) ? $data['path'] : '';
445
446
        return api_get_path(WEB_CODE_PATH).'gradebook/exercise_jump.php?'
447
            .http_build_query(
448
                [
449
                    'path' => $path,
450
                    'session_id' => $sessionId,
451
                    'cidReq' => $this->get_course_code(),
452
                    'gradebook' => 'view',
453
                    'exerciseId' => $exerciseId,
454
                    'type' => $this->get_type(),
455
                ]
456
            );
457
    }
458
459
    /**
460
     * Get name to display: same as exercise title.
461
     */
462
    public function get_name()
463
    {
464
        $documentPath = api_get_path(SYS_COURSE_PATH).$this->course_code.'/document';
465
        require_once api_get_path(SYS_CODE_PATH).'exercise/hotpotatoes.lib.php';
466
        $data = $this->get_exercise_data();
467
468
        if ($this->is_hp == 1) {
469
            if (isset($data['path'])) {
470
                $title = GetQuizName($data['path'], $documentPath);
471
                if ($title == '') {
472
                    $title = basename($data['path']);
473
                }
474
475
                return $title;
476
            }
477
        }
478
479
        return strip_tags(Exercise::get_formated_title_variable($data['title']));
480
    }
481
482
    public function getLpListToString()
483
    {
484
        $data = $this->get_exercise_data();
485
        $lpList = Exercise::getLpListFromExercise($data['id'], $this->getCourseId());
486
        $lpListToString = '';
487
        if (!empty($lpList)) {
488
            foreach ($lpList as &$list) {
489
                $list['name'] = Display::label($list['name'], 'warning');
490
            }
491
            $lpListToString = implode('&nbsp;', array_column($lpList, 'name'));
492
        }
493
494
        return $lpListToString;
495
    }
496
497
    /**
498
     * Get description to display: same as exercise description.
499
     */
500
    public function get_description()
501
    {
502
        $data = $this->get_exercise_data();
503
504
        return isset($data['description']) ? $data['description'] : null;
505
    }
506
507
    /**
508
     * Check if this still links to an exercise.
509
     */
510
    public function is_valid_link()
511
    {
512
        $exerciseData = $this->get_exercise_data();
513
514
        return !empty($exerciseData);
515
    }
516
517
    /**
518
     * @return string
519
     */
520
    public function get_type_name()
521
    {
522
        if ($this->is_hp == 1) {
523
            return 'HotPotatoes';
524
        }
525
526
        return get_lang('Quiz');
527
    }
528
529
    public function needs_name_and_description()
530
    {
531
        return false;
532
    }
533
534
    public function needs_max()
535
    {
536
        return false;
537
    }
538
539
    public function needs_results()
540
    {
541
        return false;
542
    }
543
544
    public function is_allowed_to_change_name()
545
    {
546
        return false;
547
    }
548
549
    /**
550
     * @return string
551
     */
552
    public function get_icon_name()
553
    {
554
        return 'exercise';
555
    }
556
557
    /**
558
     * @param bool $hp
559
     */
560
    public function setHp($hp)
561
    {
562
        $this->hp = $hp;
563
    }
564
565
    public function getBestScore()
566
    {
567
        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...
568
    }
569
570
    public function getStats($type)
571
    {
572
        switch ($type) {
573
            case 'best':
574
                break;
575
        }
576
    }
577
578
    /**
579
     * Lazy load function to get the database contents of this exercise.
580
     */
581
    public function get_exercise_data()
582
    {
583
        $tableItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
584
        if ($this->is_hp == 1) {
585
            $table = Database::get_course_table(TABLE_DOCUMENT);
586
        } else {
587
            $table = Database::get_course_table(TABLE_QUIZ_TEST);
588
        }
589
590
        $exerciseId = $this->get_ref_id();
591
592
        if (empty($this->exercise_data)) {
593
            if ($this->is_hp == 1) {
594
                $sql = "SELECT * FROM $table ex
595
                    INNER JOIN $tableItemProperty ip
596
                    ON (ip.ref = ex.id AND ip.c_id = ex.c_id)
597
                    WHERE
598
                        ip.c_id = $this->course_id AND
599
                        ex.c_id = $this->course_id AND
600
                        ip.ref = $exerciseId AND
601
                        ip.tool = '".TOOL_DOCUMENT."' AND
602
                        ex.path LIKE '%htm%' AND
603
                        ex.path LIKE '%HotPotatoes_files%' AND
604
                        ip.visibility = 1";
605
                $result = Database::query($sql);
606
                $this->exercise_data = Database::fetch_array($result);
607
            } else {
608
                // Try with iid
609
                $sql = 'SELECT * FROM '.$table.'
610
                        WHERE
611
                            c_id = '.$this->course_id.' AND
612
                            iid = '.$exerciseId;
613
                $result = Database::query($sql);
614
                $rows = Database::num_rows($result);
615
616
                if (!empty($rows)) {
617
                    $this->exercise_data = Database::fetch_array($result);
618
                } else {
619
                    // Try wit id
620
                    $sql = 'SELECT * FROM '.$table.'
621
                            WHERE
622
                                c_id = '.$this->course_id.' AND
623
                                id = '.$exerciseId;
624
                    $result = Database::query($sql);
625
                    $this->exercise_data = Database::fetch_array($result);
626
                }
627
            }
628
        }
629
630
        if (empty($this->exercise_data)) {
631
            return false;
632
        }
633
634
        return $this->exercise_data;
635
    }
636
637
    /**
638
     * Lazy load function to get the database table of the exercise.
639
     */
640
    private function get_exercise_table()
641
    {
642
        $this->exercise_table = Database::get_course_table(TABLE_QUIZ_TEST);
643
644
        return $this->exercise_table;
645
    }
646
}
647