Completed
Push — master ( d0e06e...1fcdba )
by Julito
09:05
created

ExerciseLink::getLpListToString()   A

Complexity

Conditions 3
Paths 2

Size

Total Lines 13
Code Lines 8

Duplication

Lines 0
Ratio 0 %

Importance

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

134
                /** @scrutinizer ignore-call */ 
135
                $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...
135
136
                switch ($type) {
137
                    case 'best':
138
                        $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

138
                        /** @scrutinizer ignore-call */ 
139
                        $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...
139
                        $result = [$bestResult, $weight];
140
141
                        return $result;
142
                        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...
143
                    case 'average':
144
                        $count = count($this->getStudentList());
145
                        if (empty($count)) {
146
                            $result = [0, $weight];
147
148
                            return $result;
149
                        }
150
                        $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

150
                        $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...
151
                        $result = [$sumResult / $count, $weight];
152
153
                        return $result;
154
                        break;
155
                    case 'ranking':
156
                        return [null, null];
157
                        break;
158
                    default:
159
                        if (!empty($stud_id)) {
160
                            $scoreList = $link->getUserScoreList();
161
                            $result = [0, $weight];
162
                            if (isset($scoreList[$stud_id])) {
163
                                $result = [$scoreList[$stud_id], $weight];
164
                            }
165
166
                            return $result;
167
                        } else {
168
                            $studentCount = count($this->getStudentList());
169
                            $sumResult = array_sum($link->getUserScoreList());
170
                            $result = [$sumResult, $studentCount];
171
                        }
172
173
                        return $result;
174
                        break;
175
                }
176
            }
177
        }
178
179
        $tblStats = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
180
        $tblHp = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTPOTATOES);
181
        $tblDoc = Database::get_course_table(TABLE_DOCUMENT);
182
183
        /* the following query should be similar (in conditions) to the one used
184
        in exercise/exercise.php, look for note-query-exe-results marker*/
185
        $sessionId = $this->get_session_id();
186
        $courseId = $this->getCourseId();
187
        $exerciseData = $this->get_exercise_data();
188
        $exerciseId = isset($exerciseData['id']) ? (int) $exerciseData['id'] : 0;
189
        $stud_id = (int) $stud_id;
190
191
        if (empty($exerciseId)) {
192
            return null;
193
        }
194
195
        $key = 'exercise_link_id:'.
196
            $this->get_id().
197
            'exerciseId:'.$exerciseId.'student:'.$stud_id.'session:'.$sessionId.'courseId:'.$courseId.'type:'.$type;
198
199
        $useCache = api_get_configuration_value('gradebook_use_apcu_cache');
200
        $cacheAvailable = api_get_configuration_value('apc') && $useCache;
201
        $cacheDriver = null;
202
        if ($cacheAvailable) {
203
            $cacheDriver = new \Doctrine\Common\Cache\ApcuCache();
204
            if ($cacheDriver->contains($key)) {
205
                return $cacheDriver->fetch($key);
206
            }
207
        }
208
209
        $exercise = new Exercise($courseId);
210
        $exercise->read($exerciseId);
211
212
        if (!$this->is_hp) {
213
            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...
214
                $sql = "SELECT * FROM $tblStats
215
                        WHERE
216
                            exe_exo_id = $exerciseId AND
217
                            orig_lp_id = 0 AND
218
                            orig_lp_item_id = 0 AND
219
                            status <> 'incomplete' AND
220
                            session_id = $sessionId AND
221
                            c_id = $courseId
222
                        ";
223
            } else {
224
                $lpCondition = null;
225
                if (!empty($exercise->lpList)) {
226
                    //$lpId = $exercise->getLpBySession($sessionId);
227
                    $lpList = [];
228
                    foreach ($exercise->lpList as $lpData) {
229
                        if ((int) $lpData['session_id'] == $sessionId) {
230
                            $lpList[] = $lpData['lp_id'];
231
                        }
232
                    }
233
                    $lpCondition = ' orig_lp_id = 0 OR (orig_lp_id IN ("'.implode('", "', $lpList).'")) AND ';
234
                }
235
236
                $sql = "SELECT *
237
                        FROM $tblStats
238
                        WHERE
239
                            exe_exo_id = $exerciseId AND
240
                            $lpCondition
241
                            status <> 'incomplete' AND
242
                            session_id = $sessionId AND
243
                            c_id = $courseId ";
244
            }
245
246
            if (!empty($stud_id) && 'ranking' !== $type) {
247
                $sql .= " AND exe_user_id = $stud_id ";
248
            }
249
            $sql .= ' ORDER BY exe_id DESC';
250
        } else {
251
            $sql = "SELECT * FROM $tblHp hp
252
                    INNER JOIN $tblDoc doc
253
                    ON (hp.exe_name = doc.path AND doc.c_id = hp.c_id)
254
                    WHERE
255
                        hp.c_id = $courseId AND
256
                        doc.id = $exerciseId";
257
258
            if (!empty($stud_id)) {
259
                $sql .= " AND hp.exe_user_id = $stud_id ";
260
            }
261
        }
262
263
        $scores = Database::query($sql);
264
265
        if (isset($stud_id) && empty($type)) {
266
            // for 1 student
267
            if ($data = Database::fetch_array($scores, 'ASSOC')) {
268
                $attempts = Database::query($sql);
269
                $counter = 0;
270
                while ($attempt = Database::fetch_array($attempts)) {
271
                    $counter++;
272
                }
273
                $result = [$data['score'], $data['max_score'], $data['exe_date'], $counter];
274
                if ($cacheAvailable) {
275
                    $cacheDriver->save($key, $result);
276
                }
277
278
                return $result;
279
            } else {
280
                if ($cacheAvailable) {
281
                    $cacheDriver->save($key, null);
282
                }
283
284
                return null;
285
            }
286
        } else {
287
            // all students -> get average
288
            // normal way of getting the info
289
            $students = []; // user list, needed to make sure we only
290
            // take first attempts into account
291
            $student_count = 0;
292
            $sum = 0;
293
            $bestResult = 0;
294
            $weight = 0;
295
            $sumResult = 0;
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 (0 != $data['max_score']) {
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 (0 == $student_count) {
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
        return api_get_path(WEB_CODE_PATH).'gradebook/exercise_jump.php?'
393
            .http_build_query(
394
                [
395
                    'path' => $path,
396
                    'sid' => $sessionId,
397
                    'cid' => $this->getCourseId(),
398
                    'gradebook' => 'view',
399
                    'exerciseId' => $exerciseId,
400
                    'type' => $this->get_type(),
401
                ]
402
            );
403
    }
404
405
    /**
406
     * Get name to display: same as exercise title.
407
     */
408
    public function get_name()
409
    {
410
        $data = $this->get_exercise_data();
411
412
        return $data['title'];
413
    }
414
415
    public function getLpListToString()
416
    {
417
        $data = $this->get_exercise_data();
418
        $lpList = Exercise::getLpListFromExercise($data['id'], $this->getCourseId());
419
        $lpListToString = '';
420
        if (!empty($lpList)) {
421
            foreach ($lpList as &$list) {
422
                $list['name'] = Display::label($list['name'], 'warning');
423
            }
424
            $lpListToString = implode('&nbsp;', array_column($lpList, 'name'));
425
        }
426
427
        return $lpListToString;
428
    }
429
430
    /**
431
     * Get description to display: same as exercise description.
432
     */
433
    public function get_description()
434
    {
435
        $data = $this->get_exercise_data();
436
437
        return isset($data['description']) ? $data['description'] : null;
438
    }
439
440
    /**
441
     * Check if this still links to an exercise.
442
     */
443
    public function is_valid_link()
444
    {
445
        $exerciseData = $this->get_exercise_data();
446
447
        return !empty($exerciseData);
448
    }
449
450
    /**
451
     * @return string
452
     */
453
    public function get_type_name()
454
    {
455
        if (1 == $this->is_hp) {
456
            return 'HotPotatoes';
457
        }
458
459
        return get_lang('Tests');
460
    }
461
462
    public function needs_name_and_description()
463
    {
464
        return false;
465
    }
466
467
    public function needs_max()
468
    {
469
        return false;
470
    }
471
472
    public function needs_results()
473
    {
474
        return false;
475
    }
476
477
    public function is_allowed_to_change_name()
478
    {
479
        return false;
480
    }
481
482
    /**
483
     * @return string
484
     */
485
    public function get_icon_name()
486
    {
487
        return 'exercise';
488
    }
489
490
    /**
491
     * @param bool $hp
492
     */
493
    public function setHp($hp)
494
    {
495
        $this->hp = $hp;
496
    }
497
498
    public function getBestScore()
499
    {
500
        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...
501
    }
502
503
    public function getStats($type)
504
    {
505
        switch ($type) {
506
            case 'best':
507
                break;
508
        }
509
    }
510
511
    /**
512
     * Lazy load function to get the database contents of this exercise.
513
     */
514
    public function get_exercise_data()
515
    {
516
        $tableItemProperty = Database::get_course_table(TABLE_ITEM_PROPERTY);
517
        if (1 == $this->is_hp) {
518
            $table = Database::get_course_table(TABLE_DOCUMENT);
519
        } else {
520
            $table = Database::get_course_table(TABLE_QUIZ_TEST);
521
        }
522
523
        $exerciseId = $this->get_ref_id();
524
525
        if (empty($this->exercise_data)) {
526
            if (1 == $this->is_hp) {
527
                $sql = "SELECT * FROM $table ex
528
                    INNER JOIN $tableItemProperty ip
529
                    ON (ip.ref = ex.id AND ip.c_id = ex.c_id)
530
                    WHERE
531
                        ip.c_id = $this->course_id AND
532
                        ex.c_id = $this->course_id AND
533
                        ip.ref = $exerciseId AND
534
                        ip.tool = '".TOOL_DOCUMENT."' AND
535
                        ex.path LIKE '%htm%' AND
536
                        ex.path LIKE '%HotPotatoes_files%' AND
537
                        ip.visibility = 1";
538
                $result = Database::query($sql);
539
                $this->exercise_data = Database::fetch_array($result);
540
            } else {
541
                // Try with iid
542
                $sql = 'SELECT * FROM '.$table.'
543
                    WHERE
544
                        c_id = '.$this->course_id.' AND
545
                        iid = '.$exerciseId;
546
                $result = Database::query($sql);
547
                $rows = Database::num_rows($result);
548
549
                if (!empty($rows)) {
550
                    $this->exercise_data = Database::fetch_array($result);
551
                } else {
552
                    // Try wit id
553
                    $sql = 'SELECT * FROM '.$table.'
554
                        WHERE
555
                            c_id = '.$this->course_id.' AND
556
                            id = '.$exerciseId;
557
                    $result = Database::query($sql);
558
                    $this->exercise_data = Database::fetch_array($result);
559
                }
560
            }
561
        }
562
563
        if (empty($this->exercise_data)) {
564
            return false;
565
        }
566
567
        return $this->exercise_data;
568
    }
569
570
    /**
571
     * Lazy load function to get the database table of the exercise.
572
     */
573
    private function get_exercise_table()
574
    {
575
        $this->exercise_table = Database::get_course_table(TABLE_QUIZ_TEST);
576
577
        return $this->exercise_table;
578
    }
579
}
580