Completed
Push — master ( 27e209...a08afa )
by Julito
186:04 queued 150:53
created

ExerciseLib   F

Complexity

Total Complexity 585

Size/Duplication

Total Lines 4530
Duplicated Lines 0 %

Importance

Changes 0
Metric Value
dl 0
loc 4530
rs 0.6314
c 0
b 0
f 0
wmc 585

56 Methods

Rating   Name   Duplication   Size   Complexity  
F check_fill_in_blanks() 0 126 19
B getBestScoreByExercise() 0 21 5
A get_count_exam_hotpotatoes_results() 0 10 1
F get_number_students_answer_count() 0 95 12
B getJsCode() 0 37 3
A isPassPercentageEnabled() 0 3 1
B displayGroupMenu() 0 27 3
A getFeedbackText() 0 5 1
B get_number_students_question_with_answer_count() 0 79 5
B isSuccessExerciseResult() 0 12 5
B get_exam_results_hotpotatoes_data() 0 71 5
F get_all_exercises() 0 82 11
B get_best_average_score_by_exercise() 0 30 6
A get_session_time_control_key() 0 15 3
B getLatestHotPotatoResult() 0 25 2
F get_exam_results_data() 0 739 97
B exercise_time_control_is_valid() 0 34 4
F displayQuestionListByAttempt() 0 375 63
A delete_chat_exercise_session() 0 4 2
F show_score() 0 55 13
A getScoreModels() 0 3 1
B convertScoreToModel() 0 17 6
A getQuestionRibbon() 0 7 1
B get_average_score() 0 19 5
B getOralFeedbackAudio() 0 28 5
F get_exercise_result_ranking() 0 71 17
B get_exercises_to_be_taken() 0 15 5
A exercise_time_control_delete() 0 11 1
B convert_score() 0 17 6
A getModelStyle() 0 7 1
B get_number_students_finish_exercise() 0 27 2
A convert_to_percentage() 0 7 2
A get_count_exam_results() 0 15 1
A detectInputAppropriateClass() 0 19 3
C getNumberStudentsFillBlanksAnswerCount() 0 38 7
B getTotalScoreRibbon() 0 53 6
B get_average_score_by_course() 0 23 5
F showQuestion() 0 1303 184
B get_exercise_track_exercise_info() 0 24 2
A get_time_control_key() 0 15 1
A get_number_students_answer_hotspot_count() 0 62 3
B get_all_exercises_for_course_id() 0 39 4
A getNotificationSettings() 0 10 1
B get_average_score_by_course_by_user() 0 23 5
A getNotCorrectedYetText() 0 3 1
B addScoreModelInput() 0 32 4
A create_chat_exercise_session() 0 6 2
A get_exercise_by_id() 0 16 2
A getOralFeedbackForm() 0 10 1
F get_exercise_result_ranking_by_attempt() 0 59 15
B get_best_attempt_in_course() 0 26 6
B get_student_stats_by_question() 0 37 2
B get_best_attempt_by_user() 0 28 6
C getCourseScoreModel() 0 23 7
A getEmailNotification() 0 20 1
B showSuccessMessage() 0 37 3

How to fix   Complexity   

Complex Class

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

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

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

1
<?php
2
/* For licensing terms, see /license.txt */
3
4
use ChamiloSession as Session;
5
6
/**
7
 * Class ExerciseLib
8
 * shows a question and its answers
9
 * @author Olivier Brouckaert <[email protected]>
10
 * @author Hubert Borderiou 2011-10-21
11
 * @author ivantcholakov2009-07-20
12
 *
13
 */
14
class ExerciseLib
15
{
16
    /**
17
     * Shows a question
18
     *
19
     * @param Exercise $exercise
20
     * @param int $questionId $questionId question id
21
     * @param bool $only_questions if true only show the questions, no exercise title
22
     * @param bool $origin i.e = learnpath
23
     * @param string $current_item current item from the list of questions
24
     * @param bool $show_title
25
     * @param bool $freeze
26
     * @param array $user_choice
27
     * @param bool $show_comment
28
     * @param bool $show_answers
29
     *
30
     * @return bool|int
31
     */
32
    public static function showQuestion(
33
        $exercise,
34
        $questionId,
35
        $only_questions = false,
36
        $origin = false,
37
        $current_item = '',
38
        $show_title = true,
39
        $freeze = false,
40
        $user_choice = [],
41
        $show_comment = false,
42
        $show_answers = false
43
    ) {
44
        $course_id = empty($exercise->course_id) ? api_get_course_int_id() : $exercise->course_id;
45
        $course = api_get_course_info_by_id($course_id);
46
        // Change false to true in the following line to enable answer hinting
47
        $debug_mark_answer = $show_answers;
48
49
        // Reads question information
50
        if (!$objQuestionTmp = Question::read($questionId)) {
51
            // Question not found
52
            return false;
53
        }
54
55
        if ($exercise->feedback_type != EXERCISE_FEEDBACK_TYPE_END) {
56
            $show_comment = false;
57
        }
58
59
        $answerType = $objQuestionTmp->selectType();
60
        $pictureName = $objQuestionTmp->getPictureFilename();
61
        $s = '';
62
        if ($answerType != HOT_SPOT &&
63
            $answerType != HOT_SPOT_DELINEATION &&
64
            $answerType != ANNOTATION
65
        ) {
66
            // Question is not a hotspot
67
            if (!$only_questions) {
68
                $questionDescription = $objQuestionTmp->selectDescription();
69
                if ($show_title) {
70
                    TestCategory::displayCategoryAndTitle($objQuestionTmp->id);
71
                    $titleToDisplay = null;
72
                    if ($answerType == READING_COMPREHENSION) {
73
                        // In READING_COMPREHENSION, the title of the question
74
                        // contains the question itself, which can only be
75
                        // shown at the end of the given time, so hide for now
76
                        $titleToDisplay = Display::div(
77
                            $current_item.'. '.get_lang('ReadingComprehension'),
78
                            ['class' => 'question_title']
79
                        );
80
                    } else {
81
                        $titleToDisplay = $objQuestionTmp->getTitleToDisplay($current_item);
82
                    }
83
                    echo $titleToDisplay;
84
                }
85
                if (!empty($questionDescription) && $answerType != READING_COMPREHENSION) {
86
                    echo Display::div(
87
                        $questionDescription,
88
                        ['class' => 'question_description']
89
                    );
90
                }
91
            }
92
93
            if (in_array($answerType, [FREE_ANSWER, ORAL_EXPRESSION]) &&
94
                $freeze
95
            ) {
96
                return '';
97
            }
98
99
            echo '<div class="question_options">';
100
            // construction of the Answer object (also gets all answers details)
101
            $objAnswerTmp = new Answer($questionId, api_get_course_int_id(), $exercise);
102
            $nbrAnswers = $objAnswerTmp->selectNbrAnswers();
103
            $quiz_question_options = Question::readQuestionOption(
104
                $questionId,
105
                $course_id
106
            );
107
108
            // For "matching" type here, we need something a little bit special
109
            // because the match between the suggestions and the answers cannot be
110
            // done easily (suggestions and answers are in the same table), so we
111
            // have to go through answers first (elems with "correct" value to 0).
112
            $select_items = [];
113
            //This will contain the number of answers on the left side. We call them
114
            // suggestions here, for the sake of comprehensions, while the ones
115
            // on the right side are called answers
116
            $num_suggestions = 0;
117
            if (in_array($answerType, [MATCHING, DRAGGABLE, MATCHING_DRAGGABLE])) {
118
                if ($answerType == DRAGGABLE) {
119
                    $isVertical = $objQuestionTmp->extra == 'v';
120
                    $s .= '
121
                        <div class="col-md-12 ui-widget ui-helper-clearfix">
122
                            <div class="clearfix">
123
                            <ul class="exercise-draggable-answer '.($isVertical ? '' : 'list-inline').'"
124
                                id="question-'.$questionId.'" data-question="'.$questionId.'">
125
                    ';
126
                } else {
127
                    $s .= '<div id="drag'.$questionId.'_question" class="drag_question">
128
                           <table class="data_table">';
129
                }
130
131
                // Iterate through answers
132
                $x = 1;
133
                //mark letters for each answer
134
                $letter = 'A';
135
                $answer_matching = [];
136
                $cpt1 = [];
137
                for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) {
138
                    $answerCorrect = $objAnswerTmp->isCorrect($answerId);
139
                    $numAnswer = $objAnswerTmp->selectAutoId($answerId);
140
                    if ($answerCorrect == 0) {
141
                        // options (A, B, C, ...) that will be put into the list-box
142
                        // have the "correct" field set to 0 because they are answer
143
                        $cpt1[$x] = $letter;
144
                        $answer_matching[$x] = $objAnswerTmp->selectAnswerByAutoId(
145
                            $numAnswer
146
                        );
147
                        $x++;
148
                        $letter++;
149
                    }
150
                }
151
152
                $i = 1;
153
                $select_items[0]['id'] = 0;
154
                $select_items[0]['letter'] = '--';
155
                $select_items[0]['answer'] = '';
156
                foreach ($answer_matching as $id => $value) {
157
                    $select_items[$i]['id'] = $value['id_auto'];
158
                    $select_items[$i]['letter'] = $cpt1[$id];
159
                    $select_items[$i]['answer'] = $value['answer'];
160
                    $i++;
161
                }
162
163
                $user_choice_array_position = [];
164
                if (!empty($user_choice)) {
165
                    foreach ($user_choice as $item) {
166
                        $user_choice_array_position[$item['position']] = $item['answer'];
167
                    }
168
                }
169
                $num_suggestions = ($nbrAnswers - $x) + 1;
170
            } elseif ($answerType == FREE_ANSWER) {
171
                $fck_content = isset($user_choice[0]) && !empty($user_choice[0]['answer']) ? $user_choice[0]['answer'] : null;
172
                $form = new FormValidator('free_choice_'.$questionId);
173
                $config = [
174
                    'ToolbarSet' => 'TestFreeAnswer'
175
                ];
176
                $form->addHtmlEditor(
177
                    "choice[".$questionId."]",
178
                    null,
179
                    false,
180
                    false,
181
                    $config
182
                );
183
                $form->setDefaults(
184
                    ["choice[".$questionId."]" => $fck_content]
185
                );
186
                $s .= $form->returnForm();
187
            } elseif ($answerType == ORAL_EXPRESSION) {
188
                // Add nanog
189
                if (api_get_setting('enable_record_audio') == 'true') {
190
                    //@todo pass this as a parameter
191
                    global $exercise_stat_info, $exerciseId;
192
193
                    if (!empty($exercise_stat_info)) {
194
                        $objQuestionTmp->initFile(
195
                            api_get_session_id(),
196
                            api_get_user_id(),
197
                            $exercise_stat_info['exe_exo_id'],
198
                            $exercise_stat_info['exe_id']
199
                        );
200
                    } else {
201
                        $objQuestionTmp->initFile(
202
                            api_get_session_id(),
203
                            api_get_user_id(),
204
                            $exerciseId,
205
                            'temp_exe'
206
                        );
207
                    }
208
209
                    echo $objQuestionTmp->returnRecorder();
210
                }
211
212
                $form = new FormValidator('free_choice_'.$questionId);
213
                $config = ['ToolbarSet' => 'TestFreeAnswer'];
214
215
                $form->addHtml('<div id="'.'hide_description_'.$questionId.'_options" style="display: none;">');
216
                $form->addHtmlEditor(
217
                    "choice[".$questionId."]",
218
                    null,
219
                    false,
220
                    false,
221
                    $config
222
                );
223
                $form->addHtml('</div>');
224
                $s .= $form->returnForm();
225
            }
226
227
            // Now navigate through the possible answers, using the max number of
228
            // answers for the question as a limiter
229
            $lines_count = 1; // a counter for matching-type answers
230
            if ($answerType == MULTIPLE_ANSWER_TRUE_FALSE ||
231
                $answerType == MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE
232
            ) {
233
                $header = Display::tag('th', get_lang('Options'));
234
                foreach ($objQuestionTmp->options as $item) {
235
                    if ($answerType == MULTIPLE_ANSWER_TRUE_FALSE) {
236
                        if (in_array($item, $objQuestionTmp->options)) {
237
                            $header .= Display::tag('th', get_lang($item));
238
                        } else {
239
                            $header .= Display::tag('th', $item);
240
                        }
241
                    } else {
242
                        $header .= Display::tag('th', $item);
243
                    }
244
                }
245
                if ($show_comment) {
246
                    $header .= Display::tag('th', get_lang('Feedback'));
247
                }
248
                $s .= '<table class="table table-hover table-striped">';
249
                $s .= Display::tag(
250
                    'tr',
251
                    $header,
252
                    ['style' => 'text-align:left;']
253
                );
254
            }
255
256
            if ($show_comment) {
257
                if (in_array(
258
                    $answerType,
259
                    [
260
                        MULTIPLE_ANSWER,
261
                        MULTIPLE_ANSWER_COMBINATION,
262
                        UNIQUE_ANSWER,
263
                        UNIQUE_ANSWER_IMAGE,
264
                        UNIQUE_ANSWER_NO_OPTION,
265
                        GLOBAL_MULTIPLE_ANSWER,
266
                    ]
267
                )) {
268
                    $header = Display::tag('th', get_lang('Options'));
269
                    if ($exercise->feedback_type == EXERCISE_FEEDBACK_TYPE_END) {
270
                        $header .= Display::tag('th', get_lang('Feedback'));
271
                    }
272
                    $s .= '<table class="table table-hover table-striped">';
273
                    $s .= Display::tag(
274
                        'tr',
275
                        $header,
276
                        ['style' => 'text-align:left;']
277
                    );
278
                }
279
            }
280
281
            $matching_correct_answer = 0;
282
            $user_choice_array = [];
283
            if (!empty($user_choice)) {
284
                foreach ($user_choice as $item) {
285
                    $user_choice_array[] = $item['answer'];
286
                }
287
            }
288
289
            $hidingClass = '';
290
            if ($answerType == READING_COMPREHENSION) {
291
                $objQuestionTmp->processText(
292
                    $objQuestionTmp->selectDescription()
293
                );
294
                $hidingClass = 'hide-reading-answers';
295
                $s .= Display::div(
296
                    $objQuestionTmp->selectTitle(),
297
                    ['class' => 'question_title '.$hidingClass]
298
                );
299
            }
300
301
            for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) {
302
                $answer = $objAnswerTmp->selectAnswer($answerId);
303
                $answerCorrect = $objAnswerTmp->isCorrect($answerId);
304
                $numAnswer = $objAnswerTmp->selectAutoId($answerId);
305
                $comment = $objAnswerTmp->selectComment($answerId);
306
                $attributes = [];
307
308
                switch ($answerType) {
309
                    case UNIQUE_ANSWER:
310
                    case UNIQUE_ANSWER_NO_OPTION:
311
                    case UNIQUE_ANSWER_IMAGE:
312
                    case READING_COMPREHENSION:
313
                        $input_id = 'choice-'.$questionId.'-'.$answerId;
314
                        if (isset($user_choice[0]['answer']) && $user_choice[0]['answer'] == $numAnswer) {
315
                            $attributes = [
316
                                'id' => $input_id,
317
                                'checked' => 1,
318
                                'selected' => 1
319
                            ];
320
                        } else {
321
                            $attributes = ['id' => $input_id];
322
                        }
323
324
                        if ($debug_mark_answer) {
325
                            if ($answerCorrect) {
326
                                $attributes['checked'] = 1;
327
                                $attributes['selected'] = 1;
328
                            }
329
                        }
330
331
                        if ($show_comment) {
332
                            $s .= '<tr><td>';
333
                        }
334
335
                        if ($answerType == UNIQUE_ANSWER_IMAGE) {
336
                            if ($show_comment) {
337
                                if (empty($comment)) {
338
                                    $s .= '<div id="answer'.$questionId.$numAnswer.'" '
339
                                        . 'class="exercise-unique-answer-image" style="text-align: center">';
340
                                } else {
341
                                    $s .= '<div id="answer'.$questionId.$numAnswer.'" '
342
                                        . 'class="exercise-unique-answer-image col-xs-6 col-sm-12" style="text-align: center">';
343
                                }
344
                            } else {
345
                                $s .= '<div id="answer'.$questionId.$numAnswer.'" '
346
                                    . 'class="exercise-unique-answer-image col-xs-6 col-md-3" style="text-align: center">';
347
                            }
348
                        }
349
350
                        $answer = Security::remove_XSS($answer, STUDENT);
351
                        $s .= Display::input(
352
                            'hidden',
353
                            'choice2['.$questionId.']',
354
                            '0'
355
                        );
356
357
                        $answer_input = null;
358
                        $attributes['class'] = 'checkradios';
359
                        if ($answerType == UNIQUE_ANSWER_IMAGE) {
360
                            $attributes['class'] = '';
361
                            $attributes['style'] = 'display: none;';
362
                            $answer = '<div class="thumbnail">'.$answer.'</div>';
363
                        }
364
365
                        $answer_input .= '<label class="radio '.$hidingClass.'">';
366
                        $answer_input .= Display::input(
367
                            'radio',
368
                            'choice['.$questionId.']',
369
                            $numAnswer,
370
                            $attributes
371
                        );
372
                        $answer_input .= $answer;
373
                        $answer_input .= '</label>';
374
375
                        if ($answerType == UNIQUE_ANSWER_IMAGE) {
376
                            $answer_input .= "</div>";
377
                        }
378
379
                        if ($show_comment) {
380
                            $s .= $answer_input;
381
                            $s .= '</td>';
382
                            $s .= '<td>';
383
                            $s .= $comment;
384
                            $s .= '</td>';
385
                            $s .= '</tr>';
386
                        } else {
387
                            $s .= $answer_input;
388
                        }
389
                        break;
390
                    case MULTIPLE_ANSWER:
391
                    case MULTIPLE_ANSWER_TRUE_FALSE:
392
                    case GLOBAL_MULTIPLE_ANSWER:
393
                        $input_id = 'choice-'.$questionId.'-'.$answerId;
394
                        $answer = Security::remove_XSS($answer, STUDENT);
395
396
                        if (in_array($numAnswer, $user_choice_array)) {
397
                            $attributes = [
398
                                'id' => $input_id,
399
                                'checked' => 1,
400
                                'selected' => 1
401
                            ];
402
                        } else {
403
                            $attributes = ['id' => $input_id];
404
                        }
405
406
                        if ($debug_mark_answer) {
407
                            if ($answerCorrect) {
408
                                $attributes['checked'] = 1;
409
                                $attributes['selected'] = 1;
410
                            }
411
                        }
412
413
                        if ($answerType == MULTIPLE_ANSWER || $answerType == GLOBAL_MULTIPLE_ANSWER) {
414
                            $s .= '<input type="hidden" name="choice2['.$questionId.']" value="0" />';
415
                            $attributes['class'] = 'checkradios';
416
                            $answer_input = '<label class="checkbox">';
417
                            $answer_input .= Display::input(
418
                                'checkbox',
419
                                'choice['.$questionId.']['.$numAnswer.']',
420
                                $numAnswer,
421
                                $attributes
422
                            );
423
                            $answer_input .= $answer;
424
                            $answer_input .= '</label>';
425
426
                            if ($show_comment) {
427
                                $s .= '<tr><td>';
428
                                $s .= $answer_input;
429
                                $s .= '</td>';
430
                                $s .= '<td>';
431
                                $s .= $comment;
432
                                $s .= '</td>';
433
                                $s .= '</tr>';
434
                            } else {
435
                                $s .= $answer_input;
436
                            }
437
                        } elseif ($answerType == MULTIPLE_ANSWER_TRUE_FALSE) {
438
                            $my_choice = [];
439
                            if (!empty($user_choice_array)) {
440
                                foreach ($user_choice_array as $item) {
441
                                    $item = explode(':', $item);
442
                                    $my_choice[$item[0]] = $item[1];
443
                                }
444
                            }
445
446
                            $s .= '<tr>';
447
                            $s .= Display::tag('td', $answer);
448
449
                            if (!empty($quiz_question_options)) {
450
                                foreach ($quiz_question_options as $id => $item) {
451
                                    if (isset($my_choice[$numAnswer]) && $id == $my_choice[$numAnswer]) {
452
                                        $attributes = [
453
                                            'checked' => 1,
454
                                            'selected' => 1
455
                                        ];
456
                                    } else {
457
                                        $attributes = [];
458
                                    }
459
460
                                    if ($debug_mark_answer) {
461
                                        if ($id == $answerCorrect) {
462
                                            $attributes['checked'] = 1;
463
                                            $attributes['selected'] = 1;
464
                                        }
465
                                    }
466
                                    $s .= Display::tag(
467
                                        'td',
468
                                        Display::input(
469
                                            'radio',
470
                                            'choice['.$questionId.']['.$numAnswer.']',
471
                                            $id,
472
                                            $attributes
473
                                        ),
474
                                        ['style' => '']
475
                                    );
476
                                }
477
                            }
478
479
                            if ($show_comment) {
480
                                $s .= '<td>';
481
                                $s .= $comment;
482
                                $s .= '</td>';
483
                            }
484
                            $s .= '</tr>';
485
                        }
486
                        break;
487
                    case MULTIPLE_ANSWER_COMBINATION:
488
                        // multiple answers
489
                        $input_id = 'choice-'.$questionId.'-'.$answerId;
490
491
                        if (in_array($numAnswer, $user_choice_array)) {
492
                            $attributes = [
493
                                'id' => $input_id,
494
                                'checked' => 1,
495
                                'selected' => 1
496
                            ];
497
                        } else {
498
                            $attributes = ['id' => $input_id];
499
                        }
500
501
                        if ($debug_mark_answer) {
502
                            if ($answerCorrect) {
503
                                $attributes['checked'] = 1;
504
                                $attributes['selected'] = 1;
505
                            }
506
                        }
507
508
                        $answer = Security::remove_XSS($answer, STUDENT);
509
                        $answer_input = '<input type="hidden" name="choice2['.$questionId.']" value="0" />';
510
                        $answer_input .= '<label class="checkbox">';
511
                        $answer_input .= Display::input(
512
                            'checkbox',
513
                            'choice['.$questionId.']['.$numAnswer.']',
514
                            1,
515
                            $attributes
516
                        );
517
                        $answer_input .= $answer;
518
                        $answer_input .= '</label>';
519
520
                        if ($show_comment) {
521
                            $s .= '<tr>';
522
                            $s .= '<td>';
523
                            $s .= $answer_input;
524
                            $s .= '</td>';
525
                            $s .= '<td>';
526
                            $s .= $comment;
527
                            $s .= '</td>';
528
                            $s .= '</tr>';
529
                        } else {
530
                            $s .= $answer_input;
531
                        }
532
                        break;
533
                    case MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE:
534
                        $s .= '<input type="hidden" name="choice2['.$questionId.']" value="0" />';
535
                        $my_choice = [];
536
                        if (!empty($user_choice_array)) {
537
                            foreach ($user_choice_array as $item) {
538
                                $item = explode(':', $item);
539
                                if (isset($item[1]) && isset($item[0])) {
540
                                    $my_choice[$item[0]] = $item[1];
541
                                }
542
                            }
543
                        }
544
                        $answer = Security::remove_XSS($answer, STUDENT);
545
                        $s .= '<tr>';
546
                        $s .= Display::tag('td', $answer);
547
                        foreach ($objQuestionTmp->options as $key => $item) {
548
                            if (isset($my_choice[$numAnswer]) && $key == $my_choice[$numAnswer]) {
549
                                $attributes = [
550
                                    'checked' => 1,
551
                                    'selected' => 1
552
                                ];
553
                            } else {
554
                                $attributes = [];
555
                            }
556
557
                            if ($debug_mark_answer) {
558
                                if ($key == $answerCorrect) {
559
                                    $attributes['checked'] = 1;
560
                                    $attributes['selected'] = 1;
561
                                }
562
                            }
563
                            $s .= Display::tag(
564
                                'td',
565
                                Display::input(
566
                                    'radio',
567
                                    'choice['.$questionId.']['.$numAnswer.']',
568
                                    $key,
569
                                    $attributes
570
                                )
571
                            );
572
                        }
573
574
                        if ($show_comment) {
575
                            $s .= '<td>';
576
                            $s .= $comment;
577
                            $s .= '</td>';
578
                        }
579
                        $s .= '</tr>';
580
                        break;
581
                    case FILL_IN_BLANKS:
582
                        // display the question, with field empty, for student to fill it,
583
                        // or filled to display the answer in the Question preview of the exercise/admin.php page
584
                        $displayForStudent = true;
585
                        $listAnswerInfo = FillBlanks::getAnswerInfo($answer);
586
587
                        // Correct answers
588
                        $correctAnswerList = $listAnswerInfo['words'];
589
590
                        // Student's answer
591
                        $studentAnswerList = [];
592
                        if (isset($user_choice[0]['answer'])) {
593
                            $arrayStudentAnswer = FillBlanks::getAnswerInfo(
594
                                $user_choice[0]['answer'],
595
                                true
596
                            );
597
                            $studentAnswerList = $arrayStudentAnswer['student_answer'];
598
                        }
599
600
                        // If the question must be shown with the answer (in page exercise/admin.php) for teacher preview
601
                        // set the student-answer to the correct answer
602
                        if ($debug_mark_answer) {
603
                            $studentAnswerList = $correctAnswerList;
604
                            $displayForStudent = false;
605
                        }
606
607
                        if (!empty($correctAnswerList) && !empty($studentAnswerList)) {
608
                            $answer = '';
609
                            for ($i = 0; $i < count($listAnswerInfo['common_words']) - 1; $i++) {
610
                                // display the common word
611
                                $answer .= $listAnswerInfo['common_words'][$i];
612
                                // display the blank word
613
                                $correctItem = $listAnswerInfo['words'][$i];
614
                                if (isset($studentAnswerList[$i])) {
615
                                    // If student already started this test and answered this question,
616
                                    // fill the blank with his previous answers
617
                                    // may be "" if student viewed the question, but did not fill the blanks
618
                                    $correctItem = $studentAnswerList[$i];
619
                                }
620
                                $attributes['style'] = "width:".$listAnswerInfo['input_size'][$i]."px";
621
                                $answer .= FillBlanks::getFillTheBlankHtml(
622
                                    $current_item,
623
                                    $questionId,
624
                                    $correctItem,
625
                                    $attributes,
626
                                    $answer,
627
                                    $listAnswerInfo,
628
                                    $displayForStudent,
629
                                    $i
630
                                );
631
                            }
632
                            // display the last common word
633
                            $answer .= $listAnswerInfo['common_words'][$i];
634
                        } else {
635
                            // display empty [input] with the right width for student to fill it
636
                            $answer = '';
637
                            for ($i = 0; $i < count($listAnswerInfo['common_words']) - 1; $i++) {
638
                                // display the common words
639
                                $answer .= $listAnswerInfo['common_words'][$i];
640
                                // display the blank word
641
                                $attributes['style'] = "width:".$listAnswerInfo['input_size'][$i]."px";
642
                                $answer .= FillBlanks::getFillTheBlankHtml(
643
                                    $current_item,
644
                                    $questionId,
645
                                    '',
646
                                    $attributes,
647
                                    $answer,
648
                                    $listAnswerInfo,
649
                                    $displayForStudent,
650
                                    $i
651
                                );
652
                            }
653
                            // display the last common word
654
                            $answer .= $listAnswerInfo['common_words'][$i];
655
                        }
656
                        $s .= $answer;
657
                        break;
658
                    case CALCULATED_ANSWER:
659
                        /*
660
                         * In the CALCULATED_ANSWER test
661
                         * you mustn't have [ and ] in the textarea
662
                         * you mustn't have @@ in the textarea
663
                         * the text to find mustn't be empty or contains only spaces
664
                         * the text to find mustn't contains HTML tags
665
                         * the text to find mustn't contains char "
666
                         */
667
                        if ($origin !== null) {
668
                            global $exe_id;
669
                            $trackAttempts = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
670
                            $sql = 'SELECT answer FROM '.$trackAttempts.'
671
                                    WHERE exe_id=' . $exe_id.' AND question_id='.$questionId;
672
                            $rsLastAttempt = Database::query($sql);
673
                            $rowLastAttempt = Database::fetch_array($rsLastAttempt);
674
                            $answer = $rowLastAttempt['answer'];
675
                            if (empty($answer)) {
676
                                $_SESSION['calculatedAnswerId'][$questionId] = mt_rand(
677
                                    1,
678
                                    $nbrAnswers
679
                                );
680
                                $answer = $objAnswerTmp->selectAnswer(
681
                                    $_SESSION['calculatedAnswerId'][$questionId]
682
                                );
683
                            }
684
                        }
685
686
                        list($answer) = explode('@@', $answer);
687
                        // $correctAnswerList array of array with correct anwsers 0=> [0=>[\p] 1=>[plop]]
688
                        api_preg_match_all(
689
                            '/\[[^]]+\]/',
690
                            $answer,
691
                            $correctAnswerList
692
                        );
693
694
                        // get student answer to display it if student go back to previous calculated answer question in a test
695
                        if (isset($user_choice[0]['answer'])) {
696
                            api_preg_match_all(
697
                                '/\[[^]]+\]/',
698
                                $answer,
699
                                $studentAnswerList
700
                            );
701
                            $studentAnswerListTobecleaned = $studentAnswerList[0];
702
                            $studentAnswerList = [];
703
704
                            for ($i = 0; $i < count(
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
705
                                $studentAnswerListTobecleaned
706
                            ); $i++) {
707
                                $answerCorrected = $studentAnswerListTobecleaned[$i];
708
                                $answerCorrected = api_preg_replace(
709
                                    '| / <font color="green"><b>.*$|',
710
                                    '',
711
                                    $answerCorrected
712
                                );
713
                                $answerCorrected = api_preg_replace(
714
                                    '/^\[/',
715
                                    '',
716
                                    $answerCorrected
717
                                );
718
                                $answerCorrected = api_preg_replace(
719
                                    '|^<font color="red"><s>|',
720
                                    '',
721
                                    $answerCorrected
722
                                );
723
                                $answerCorrected = api_preg_replace(
724
                                    '|</s></font>$|',
725
                                    '',
726
                                    $answerCorrected
727
                                );
728
                                $answerCorrected = '['.$answerCorrected.']';
729
                                $studentAnswerList[] = $answerCorrected;
730
                            }
731
                        }
732
733
                        // If display preview of answer in test view for exemple, set the student answer to the correct answers
734
                        if ($debug_mark_answer) {
735
                            // contain the rights answers surronded with brackets
736
                            $studentAnswerList = $correctAnswerList[0];
737
                        }
738
739
                        /*
740
                        Split the response by bracket
741
                        tabComments is an array with text surrounding the text to find
742
                        we add a space before and after the answerQuestion to be sure to
743
                        have a block of text before and after [xxx] patterns
744
                        so we have n text to find ([xxx]) and n+1 block of texts before,
745
                        between and after the text to find
746
                        */
747
                        $tabComments = api_preg_split(
748
                            '/\[[^]]+\]/',
749
                            ' '.$answer.' '
750
                        );
751
                        if (!empty($correctAnswerList) && !empty($studentAnswerList)) {
752
                            $answer = "";
753
                            $i = 0;
754
                            foreach ($studentAnswerList as $studentItem) {
755
                                // remove surronding brackets
756
                                $studentResponse = api_substr(
757
                                    $studentItem,
758
                                    1,
759
                                    api_strlen($studentItem) - 2
760
                                );
761
                                $size = strlen($studentItem);
762
                                $attributes['class'] = self::detectInputAppropriateClass(
763
                                    $size
764
                                );
765
766
                                $answer .= $tabComments[$i].
767
                                    Display::input(
768
                                        'text',
769
                                        "choice[$questionId][]",
770
                                        $studentResponse,
771
                                        $attributes
772
                                    );
773
                                $i++;
774
                            }
775
                            $answer .= $tabComments[$i];
776
                        } else {
777
                            // display exercise with empty input fields
778
                            // every [xxx] are replaced with an empty input field
779
                            foreach ($correctAnswerList[0] as $item) {
780
                                $size = strlen($item);
781
                                $attributes['class'] = self::detectInputAppropriateClass(
782
                                    $size
783
                                );
784
                                $answer = str_replace(
785
                                    $item,
786
                                    Display::input(
787
                                        'text',
788
                                        "choice[$questionId][]",
789
                                        '',
790
                                        $attributes
791
                                    ),
792
                                    $answer
793
                                );
794
                            }
795
                        }
796
                        if ($origin !== null) {
797
                            $s = $answer;
798
                            break;
799
                        } else {
800
                            $s .= $answer;
801
                        }
802
                        break;
803
                    case MATCHING:
804
                        // matching type, showing suggestions and answers
805
                        // TODO: replace $answerId by $numAnswer
806
                        if ($answerCorrect != 0) {
807
                            // only show elements to be answered (not the contents of
808
                            // the select boxes, who are correct = 0)
809
                            $s .= '<tr><td width="45%" valign="top">';
810
                            $parsed_answer = $answer;
811
                            // Left part questions
812
                            $s .= '<p class="indent">'.$lines_count.'.&nbsp;'.$parsed_answer.'</p></td>';
813
                            // Middle part (matches selects)
814
                            // Id of select is # question + # of option
815
                            $s .= '<td width="10%" valign="top" align="center">
816
                                <div class="select-matching">
817
                                <select id="choice_id_'.$current_item.'_'.$lines_count.'" name="choice['.$questionId.']['.$numAnswer.']">';
818
819
                            // fills the list-box
820
                            foreach ($select_items as $key => $val) {
821
                                // set $debug_mark_answer to true at function start to
822
                                // show the correct answer with a suffix '-x'
823
                                $selected = '';
824
                                if ($debug_mark_answer) {
825
                                    if ($val['id'] == $answerCorrect) {
826
                                        $selected = 'selected="selected"';
827
                                    }
828
                                }
829
                                //$user_choice_array_position
830
                                if (isset($user_choice_array_position[$numAnswer]) &&
831
                                    $val['id'] == $user_choice_array_position[$numAnswer]
832
                                ) {
833
                                    $selected = 'selected="selected"';
834
                                }
835
                                $s .= '<option value="'.$val['id'].'" '.$selected.'>'.$val['letter'].'</option>';
836
                            }  // end foreach()
837
838
                            $s .= '</select></div></td><td width="5%" class="separate">&nbsp;</td>';
839
                            $s .= '<td width="40%" valign="top" >';
840
                            if (isset($select_items[$lines_count])) {
841
                                $s .= '<div class="text-right"><p class="indent">'.$select_items[$lines_count]['letter'].'.&nbsp; '.$select_items[$lines_count]['answer'].'</p></div>';
842
                            } else {
843
                                $s .= '&nbsp;';
844
                            }
845
                            $s .= '</td>';
846
                            $s .= '</tr>';
847
                            $lines_count++;
848
                            //if the left side of the "matching" has been completely
849
                            // shown but the right side still has values to show...
850
                            if (($lines_count - 1) == $num_suggestions) {
851
                                // if it remains answers to shown at the right side
852
                                while (isset($select_items[$lines_count])) {
853
                                    $s .= '<tr>
854
                                      <td colspan="2"></td>
855
                                      <td valign="top">';
856
                                    $s .= '<b>'.$select_items[$lines_count]['letter'].'.</b> '.$select_items[$lines_count]['answer'];
857
                                    $s .= "</td>
858
                                </tr>";
859
                                    $lines_count++;
860
                                }    // end while()
861
                            }  // end if()
862
                            $matching_correct_answer++;
863
                        }
864
                        break;
865
                    case DRAGGABLE:
866
                        if ($answerCorrect) {
867
                            $windowId = $questionId.'_'.$lines_count;
868
                            $s .= '<li class="touch-items" id="'.$windowId.'">';
869
                            $s .= Display::div(
870
                                $answer,
871
                                [
872
                                    'id' => "window_$windowId",
873
                                    'class' => "window{$questionId}_question_draggable exercise-draggable-answer-option"
874
                                ]
875
                            );
876
877
                            $draggableSelectOptions = [];
878
                            $selectedValue = 0;
879
                            $selectedIndex = 0;
880
881
                            if ($user_choice) {
882
                                foreach ($user_choice as $chosen) {
883
                                    if ($answerCorrect != $chosen['answer']) {
884
                                        continue;
885
                                    }
886
887
                                    $selectedValue = $chosen['answer'];
888
                                }
889
                            }
890
891
                            foreach ($select_items as $key => $select_item) {
892
                                $draggableSelectOptions[$select_item['id']] = $select_item['letter'];
893
                            }
894
895
                            foreach ($draggableSelectOptions as $value => $text) {
896
                                if ($value == $selectedValue) {
897
                                    break;
898
                                }
899
                                $selectedIndex++;
900
                            }
901
902
                            $s .= Display::select(
903
                                "choice[$questionId][$numAnswer]",
904
                                $draggableSelectOptions,
905
                                $selectedValue,
906
                                [
907
                                    'id' => "window_{$windowId}_select",
908
                                    'class' => 'select_option hidden',
909
                                ],
910
                                false
911
                            );
912
913
                            if ($selectedValue && $selectedIndex) {
914
                                $s .= "
915
                                    <script>
916
                                        $(function() {
917
                                            DraggableAnswer.deleteItem(
918
                                                $('#{$questionId}_$lines_count'),
919
                                                $('#drop_{$questionId}_{$selectedIndex}')
920
                                            );
921
                                        });
922
                                    </script>
923
                                ";
924
                            }
925
926
                            if (isset($select_items[$lines_count])) {
927
                                $s .= Display::div(
928
                                    Display::tag(
929
                                        'b',
930
                                        $select_items[$lines_count]['letter']
931
                                    ).$select_items[$lines_count]['answer'],
932
                                    [
933
                                        'id' => "window_{$windowId}_answer",
934
                                        'class' => 'hidden'
935
                                    ]
936
                                );
937
                            } else {
938
                                $s .= '&nbsp;';
939
                            }
940
941
                            $lines_count++;
942
                            if (($lines_count - 1) == $num_suggestions) {
943
                                while (isset($select_items[$lines_count])) {
944
                                    $s .= Display::tag('b', $select_items[$lines_count]['letter']);
945
                                    $s .= $select_items[$lines_count]['answer'];
946
                                    $lines_count++;
947
                                }
948
                            }
949
950
                            $matching_correct_answer++;
951
                            $s .= '</li>';
952
                        }
953
                        break;
954
                    case MATCHING_DRAGGABLE:
955
                        if ($answerId == 1) {
956
                            echo $objAnswerTmp->getJs();
957
                        }
958
                        if ($answerCorrect != 0) {
959
                            $windowId = "{$questionId}_{$lines_count}";
960
                            $s .= <<<HTML
961
                            <tr>
962
                                <td width="45%">
963
                                    <div id="window_{$windowId}" class="window window_left_question window{$questionId}_question">
964
                                        <strong>$lines_count.</strong> 
965
                                        $answer
966
                                    </div>
967
                                </td>
968
                                <td width="10%">
969
HTML;
970
971
                            $draggableSelectOptions = [];
972
                            $selectedValue = 0;
973
                            $selectedIndex = 0;
974
975
                            if ($user_choice) {
976
                                foreach ($user_choice as $chosen) {
977
                                    if ($numAnswer == $chosen['position']) {
978
                                        $selectedValue = $chosen['answer'];
979
                                        break;
980
                                    }
981
                                }
982
                            }
983
984
                            foreach ($select_items as $key => $selectItem) {
985
                                $draggableSelectOptions[$selectItem['id']] = $selectItem['letter'];
986
                            }
987
988
989
                            foreach ($draggableSelectOptions as $value => $text) {
990
                                if ($value == $selectedValue) {
991
                                    break;
992
                                }
993
                                $selectedIndex++;
994
                            }
995
996
                            $s .= Display::select(
997
                                "choice[$questionId][$numAnswer]",
998
                                $draggableSelectOptions,
999
                                $selectedValue,
1000
                                [
1001
                                    'id' => "window_{$windowId}_select",
1002
                                    'class' => 'hidden'
1003
                                ],
1004
                                false
1005
                            );
1006
1007
                            if (!empty($answerCorrect) && !empty($selectedValue)) {
1008
                                // Show connect if is not freeze (question preview)
1009
                                if (!$freeze) {
1010
                                    $s .= "
1011
                                        <script>
1012
                                            $(document).on('ready', function () {
1013
                                                jsPlumb.ready(function() {
1014
                                                    jsPlumb.connect({
1015
                                                        source: 'window_$windowId',
1016
                                                        target: 'window_{$questionId}_{$selectedIndex}_answer',
1017
                                                        endpoint: ['Blank', {radius: 15}],
1018
                                                        anchors: ['RightMiddle', 'LeftMiddle'],
1019
                                                        paintStyle: {strokeStyle: '#8A8888', lineWidth: 8},
1020
                                                        connector: [
1021
                                                            MatchingDraggable.connectorType,
1022
                                                            {curvines: MatchingDraggable.curviness}
1023
                                                        ]
1024
                                                    });
1025
                                                });
1026
                                            });
1027
                                        </script>
1028
                                    ";
1029
                                }
1030
                            }
1031
1032
                            $s .= '</td><td width="45%">';
1033
                            if (isset($select_items[$lines_count])) {
1034
                                $s .= <<<HTML
1035
                                <div id="window_{$windowId}_answer" class="window window_right_question">
1036
                                    <strong>{$select_items[$lines_count]['letter']}.</strong> {$select_items[$lines_count]['answer']}
1037
                                </div>
1038
HTML;
1039
                            } else {
1040
                                $s .= '&nbsp;';
1041
                            }
1042
1043
                            $s .= '</td></tr>';
1044
                            $lines_count++;
1045
                            if (($lines_count - 1) == $num_suggestions) {
1046
                                while (isset($select_items[$lines_count])) {
1047
                                    $s .= <<<HTML
1048
                                    <tr>
1049
                                        <td colspan="2"></td>
1050
                                        <td>
1051
                                            <strong>{$select_items[$lines_count]['letter']}</strong>
1052
                                            {$select_items[$lines_count]['answer']}
1053
                                        </td>
1054
                                    </tr>
1055
HTML;
1056
                                    $lines_count++;
1057
                                }
1058
                            }
1059
                            $matching_correct_answer++;
1060
                        }
1061
                        break;
1062
                }
1063
            } // end for()
1064
1065
            if ($show_comment) {
1066
                $s .= '</table>';
1067
            } elseif (in_array(
1068
                $answerType,
1069
                [
1070
                    MATCHING,
1071
                    MATCHING_DRAGGABLE,
1072
                    UNIQUE_ANSWER_NO_OPTION,
1073
                    MULTIPLE_ANSWER_TRUE_FALSE,
1074
                    MULTIPLE_ANSWER_COMBINATION_TRUE_FALSE,
1075
                ]
1076
            )) {
1077
                $s .= '</table>';
1078
            }
1079
1080
            if ($answerType == DRAGGABLE) {
1081
                $isVertical = $objQuestionTmp->extra == 'v';
1082
1083
                $s .= "</ul>";
1084
                $s .= "</div>"; //clearfix
1085
                $counterAnswer = 1;
1086
                $s .= $isVertical ? '' : '<div class="row">';
1087
                for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) {
1088
                    $answerCorrect = $objAnswerTmp->isCorrect($answerId);
1089
                    $windowId = $questionId.'_'.$counterAnswer;
1090
                    if ($answerCorrect) {
1091
                        $s .= $isVertical ? '<div class="row">' : '';
1092
                        $s .= '
1093
                            <div class="'.($isVertical ? 'col-md-12' : 'col-xs-12 col-sm-4 col-md-3 col-lg-2').'">
1094
                                <div id="drop_'.$windowId.'" class="droppable">&nbsp;</div>
1095
                            </div>
1096
                        ';
1097
                        $s .= $isVertical ? '</div>' : '';
1098
                        $counterAnswer++;
1099
                    }
1100
                }
1101
1102
                $s .= $isVertical ? '' : '</div>'; // row
1103
                $s .= '</div>'; // col-md-12 ui-widget ui-helper-clearfix
1104
            }
1105
1106
            if (in_array($answerType, [MATCHING, MATCHING_DRAGGABLE])) {
1107
                $s .= '</div>'; //drag_question
1108
            }
1109
1110
            $s .= '</div>'; //question_options row
1111
1112
            // destruction of the Answer object
1113
            unset($objAnswerTmp);
1114
1115
            // destruction of the Question object
1116
            unset($objQuestionTmp);
1117
1118
            if ($origin == 'export') {
1119
                return $s;
1120
            }
1121
            echo $s;
1122
        } elseif ($answerType == HOT_SPOT || $answerType == HOT_SPOT_DELINEATION) {
1123
            global $exerciseId, $exe_id;
1124
            // Question is a HOT_SPOT
1125
            //checking document/images visibility
1126
            if (api_is_platform_admin() || api_is_course_admin()) {
1127
                $doc_id = $objQuestionTmp->getPictureId();
1128
                if (is_numeric($doc_id)) {
1129
                    $images_folder_visibility = api_get_item_visibility(
1130
                        $course,
1131
                        'document',
1132
                        $doc_id,
1133
                        api_get_session_id()
1134
                    );
1135
                    if (!$images_folder_visibility) {
1136
                        // Show only to the course/platform admin if the image is set to visibility = false
1137
                        echo Display::return_message(
1138
                            get_lang('ChangeTheVisibilityOfTheCurrentImage'),
1139
                            'warning'
1140
                        );
1141
                    }
1142
                }
1143
            }
1144
            $questionName = $objQuestionTmp->selectTitle();
1145
            $questionDescription = $objQuestionTmp->selectDescription();
1146
1147
            // Get the answers, make a list
1148
            $objAnswerTmp = new Answer($questionId);
1149
            $nbrAnswers = $objAnswerTmp->selectNbrAnswers();
1150
1151
            // get answers of hotpost
1152
            $answers_hotspot = [];
1153
            for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) {
1154
                $answers = $objAnswerTmp->selectAnswerByAutoId(
1155
                    $objAnswerTmp->selectAutoId($answerId)
1156
                );
1157
                $answers_hotspot[$answers['id']] = $objAnswerTmp->selectAnswer(
1158
                    $answerId
1159
                );
1160
            }
1161
1162
            $answerList = '';
1163
            $hotspotColor = 0;
1164
            if ($answerType != HOT_SPOT_DELINEATION) {
1165
                $answerList = '
1166
                    <div class="well well-sm">
1167
                        <h5 class="page-header">' . get_lang('HotspotZones').'</h5>
1168
                        <ol>
1169
                ';
1170
1171
                if (!empty($answers_hotspot)) {
1172
                    Session::write("hotspot_ordered$questionId", array_keys($answers_hotspot));
1173
                    foreach ($answers_hotspot as $value) {
1174
                        $answerList .= "<li>";
1175
                        if ($freeze) {
1176
                            $answerList .= '<span class="hotspot-color-'.$hotspotColor
1177
                                .' fa fa-square" aria-hidden="true"></span>'.PHP_EOL;
1178
                        }
1179
                        $answerList .= $value;
1180
                        $answerList .= "</li>";
1181
                        $hotspotColor++;
1182
                    }
1183
                }
1184
1185
                $answerList .= '
1186
                        </ul>
1187
                    </div>
1188
                ';
1189
1190
                if ($freeze) {
1191
                    $relPath = api_get_path(WEB_CODE_PATH);
1192
                    echo "
1193
                        <div class=\"row\">
1194
                            <div class=\"col-sm-9\">
1195
                                <div id=\"hotspot-preview-$questionId\"></div>                                
1196
                            </div>
1197
                            <div class=\"col-sm-3\">
1198
                                $answerList
1199
                            </div>
1200
                        </div>
1201
                        <script>
1202
                                new ".($answerType == HOT_SPOT ? "HotspotQuestion" : "DelineationQuestion")."({
1203
                                    questionId: $questionId,
1204
                                    exerciseId: $exerciseId,
1205
                                    selector: '#hotspot-preview-$questionId',
1206
                                    for: 'preview',
1207
                                    relPath: '$relPath'
1208
                                });
1209
                        </script>
1210
                    ";
1211
                    return;
1212
                }
1213
            }
1214
1215
            if (!$only_questions) {
1216
                if ($show_title) {
1217
                    TestCategory::displayCategoryAndTitle($objQuestionTmp->id);
1218
1219
                    echo $objQuestionTmp->getTitleToDisplay($current_item);
1220
                }
1221
                //@todo I need to the get the feedback type
1222
                echo <<<HOTSPOT
1223
                    <input type="hidden" name="hidden_hotspot_id" value="$questionId" />
1224
                    <div class="exercise_questions">
1225
                        $questionDescription
1226
                        <div class="row">
1227
HOTSPOT;
1228
            }
1229
1230
            $relPath = api_get_path(WEB_CODE_PATH);
1231
            $s .= "<div class=\"col-sm-8 col-md-9\">
1232
                   <div class=\"hotspot-image\"></div>
1233
                    <script>
1234
                        $(document).on('ready', function () {
1235
                            new ".($answerType == HOT_SPOT_DELINEATION ? 'DelineationQuestion' : 'HotspotQuestion')."({
1236
                                questionId: $questionId,
1237
                                exerciseId: $exe_id,
1238
                                selector: '#question_div_' + $questionId + ' .hotspot-image',
1239
                                for: 'user',
1240
                                relPath: '$relPath'
1241
                            });
1242
                        });
1243
                    </script>
1244
                </div>
1245
                <div class=\"col-sm-4 col-md-3\">
1246
                    $answerList
1247
                </div>
1248
            ";
1249
1250
            echo <<<HOTSPOT
1251
                            $s
1252
                        </div>
1253
                    </div>
1254
HOTSPOT;
1255
        } elseif ($answerType == ANNOTATION) {
1256
            global $exe_id;
1257
            $relPath = api_get_path(WEB_CODE_PATH);
1258
            if (api_is_platform_admin() || api_is_course_admin()) {
1259
                $docId = DocumentManager::get_document_id($course, '/images/'.$pictureName);
1260
                if ($docId) {
1261
                    $images_folder_visibility = api_get_item_visibility(
1262
                        $course,
1263
                        'document',
1264
                        $docId,
1265
                        api_get_session_id()
1266
                    );
1267
1268
                    if (!$images_folder_visibility) {
1269
                        echo Display::return_message(get_lang('ChangeTheVisibilityOfTheCurrentImage'), 'warning');
1270
                    }
1271
                }
1272
1273
                if ($freeze) {
1274
                    echo Display::img(
1275
                        api_get_path(WEB_COURSE_PATH).$course['path'].'/document/images/'.$pictureName,
1276
                        $objQuestionTmp->selectTitle(),
1277
                        ['width' => '600px']
1278
                    );
1279
1280
                    return 0;
1281
                }
1282
            }
1283
1284
            if (!$only_questions) {
1285
                if ($show_title) {
1286
                    TestCategory::displayCategoryAndTitle($objQuestionTmp->id);
1287
                    echo $objQuestionTmp->getTitleToDisplay($current_item);
1288
                }
1289
                echo '
1290
                    <input type="hidden" name="hidden_hotspot_id" value="'.$questionId.'" />
1291
                    <div class="exercise_questions">
1292
                        '.$objQuestionTmp->selectDescription().'
1293
                        <div class="row">
1294
                            <div class="col-sm-8 col-md-9">
1295
                                <div id="annotation-canvas-'.$questionId.'" class="annotation-canvas center-block">
1296
                                </div>
1297
                                <script>
1298
                                    AnnotationQuestion({
1299
                                        questionId: '.$questionId.',
1300
                                        exerciseId: '.$exe_id.',
1301
                                        relPath: \''.$relPath.'\'
1302
                                    });
1303
                                </script>
1304
                            </div>
1305
                            <div class="col-sm-4 col-md-3">
1306
                                <div class="well well-sm" id="annotation-toolbar-'.$questionId.'">
1307
                                    <div class="btn-toolbar">
1308
                                        <div class="btn-group" data-toggle="buttons">
1309
                                            <label class="btn btn-default active"
1310
                                                aria-label="'.get_lang('AddAnnotationPath').'">
1311
                                                <input type="radio" value="0" name="'.$questionId.'-options" autocomplete="off" checked>
1312
                                                <span class="fa fa-pencil" aria-hidden="true"></span>
1313
                                            </label>
1314
                                            <label class="btn btn-default"
1315
                                                aria-label="'.get_lang('AddAnnotationText').'">
1316
                                                <input type="radio" value="1" name="'.$questionId.'-options" autocomplete="off">
1317
                                                <span class="fa fa-font fa-fw" aria-hidden="true"></span>
1318
                                            </label>
1319
                                        </div>
1320
                                    </div>
1321
                                    <ul class="list-unstyled"></ul>
1322
                                </div>
1323
                            </div>
1324
                        </div>
1325
                    </div>
1326
                ';
1327
            }
1328
1329
            $objAnswerTmp = new Answer($questionId);
1330
            $nbrAnswers = $objAnswerTmp->selectNbrAnswers();
1331
1332
            unset($objAnswerTmp, $objQuestionTmp);
1333
        }
1334
        return $nbrAnswers;
1335
    }
1336
1337
    /**
1338
     * @param int $exe_id
1339
     * @return array
1340
     */
1341
    public static function get_exercise_track_exercise_info($exe_id)
1342
    {
1343
        $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
1344
        $TBL_TRACK_EXERCICES = Database::get_main_table(
1345
            TABLE_STATISTIC_TRACK_E_EXERCISES
1346
        );
1347
        $TBL_COURSE = Database::get_main_table(TABLE_MAIN_COURSE);
1348
        $exe_id = intval($exe_id);
1349
        $result = [];
1350
        if (!empty($exe_id)) {
1351
            $sql = " SELECT q.*, tee.*
1352
                FROM $TBL_EXERCICES as q
1353
                INNER JOIN $TBL_TRACK_EXERCICES as tee
1354
                ON q.id = tee.exe_exo_id
1355
                INNER JOIN $TBL_COURSE c
1356
                ON c.id = tee.c_id
1357
                WHERE tee.exe_id = $exe_id
1358
                AND q.c_id = c.id";
1359
1360
            $res_fb_type = Database::query($sql);
1361
            $result = Database::fetch_array($res_fb_type, 'ASSOC');
1362
        }
1363
1364
        return $result;
1365
    }
1366
1367
    /**
1368
     * Validates the time control key
1369
     * @param int $exercise_id
1370
     * @param int $lp_id
1371
     * @param int $lp_item_id
1372
     * @return bool
1373
     */
1374
    public static function exercise_time_control_is_valid(
1375
        $exercise_id,
1376
        $lp_id = 0,
1377
        $lp_item_id = 0
1378
    ) {
1379
        $course_id = api_get_course_int_id();
1380
        $exercise_id = intval($exercise_id);
1381
        $table = Database::get_course_table(TABLE_QUIZ_TEST);
1382
        $sql = "SELECT expired_time FROM $table
1383
                WHERE c_id = $course_id AND id = $exercise_id";
1384
        $result = Database::query($sql);
1385
        $row = Database::fetch_array($result, 'ASSOC');
1386
        if (!empty($row['expired_time'])) {
1387
            $current_expired_time_key = self::get_time_control_key(
1388
                $exercise_id,
1389
                $lp_id,
1390
                $lp_item_id
1391
            );
1392
            if (isset($_SESSION['expired_time'][$current_expired_time_key])) {
1393
                $current_time = time();
1394
                $expired_time = api_strtotime(
1395
                    $_SESSION['expired_time'][$current_expired_time_key],
1396
                    'UTC'
1397
                );
1398
                $total_time_allowed = $expired_time + 30;
1399
                if ($total_time_allowed < $current_time) {
1400
                    return false;
1401
                }
1402
                return true;
1403
            } else {
1404
                return false;
1405
            }
1406
        } else {
1407
            return true;
1408
        }
1409
    }
1410
1411
    /**
1412
     * Deletes the time control token
1413
     *
1414
     * @param int $exercise_id
1415
     * @param int $lp_id
1416
     * @param int $lp_item_id
1417
     */
1418
    public static function exercise_time_control_delete(
1419
        $exercise_id,
1420
        $lp_id = 0,
1421
        $lp_item_id = 0
1422
    ) {
1423
        $current_expired_time_key = self::get_time_control_key(
1424
            $exercise_id,
1425
            $lp_id,
1426
            $lp_item_id
1427
        );
1428
        unset($_SESSION['expired_time'][$current_expired_time_key]);
1429
    }
1430
1431
    /**
1432
     * Generates the time control key
1433
     * @param int $exercise_id
1434
     * @param int $lp_id
1435
     * @param int $lp_item_id
1436
     * @return string
1437
     */
1438
    public static function get_time_control_key(
1439
        $exercise_id,
1440
        $lp_id = 0,
1441
        $lp_item_id = 0
1442
    ) {
1443
        $exercise_id = intval($exercise_id);
1444
        $lp_id = intval($lp_id);
1445
        $lp_item_id = intval($lp_item_id);
1446
        return
1447
            api_get_course_int_id().'_'.
1448
            api_get_session_id().'_'.
1449
            $exercise_id.'_'.
1450
            api_get_user_id().'_'.
1451
            $lp_id.'_'.
1452
            $lp_item_id;
1453
    }
1454
1455
    /**
1456
     * Get session time control
1457
     *
1458
     * @param int $exercise_id
1459
     * @param int $lp_id
1460
     * @param int $lp_item_id
1461
     * @return int
1462
     */
1463
    public static function get_session_time_control_key(
1464
        $exercise_id,
1465
        $lp_id = 0,
1466
        $lp_item_id = 0
1467
    ) {
1468
        $return_value = 0;
1469
        $time_control_key = self::get_time_control_key(
1470
            $exercise_id,
1471
            $lp_id,
1472
            $lp_item_id
1473
        );
1474
        if (isset($_SESSION['expired_time']) && isset($_SESSION['expired_time'][$time_control_key])) {
1475
            $return_value = $_SESSION['expired_time'][$time_control_key];
1476
        }
1477
        return $return_value;
1478
    }
1479
1480
    /**
1481
     * Gets count of exam results
1482
     * @param int $exerciseId
1483
     * @param array $conditions
1484
     * @param string $courseCode
1485
     * @param bool $showSession
1486
     *
1487
     * @return array
1488
     */
1489
    public static function get_count_exam_results($exerciseId, $conditions, $courseCode = '', $showSession = false)
1490
    {
1491
        $count = self::get_exam_results_data(
1492
            null,
1493
            null,
1494
            null,
1495
            null,
1496
            $exerciseId,
1497
            $conditions,
1498
            true,
1499
            $courseCode,
1500
            $showSession
1501
        );
1502
1503
        return $count;
1504
    }
1505
1506
    /**
1507
     * @param string $in_hotpot_path
1508
     * @return int
1509
     */
1510
    public static function get_count_exam_hotpotatoes_results($in_hotpot_path)
1511
    {
1512
        return self::get_exam_results_hotpotatoes_data(
1513
            0,
1514
            0,
1515
            '',
1516
            '',
1517
            $in_hotpot_path,
1518
            true,
1519
            ''
1520
        );
1521
    }
1522
1523
    /**
1524
     * @param int $in_from
1525
     * @param int $in_number_of_items
1526
     * @param int $in_column
1527
     * @param int $in_direction
1528
     * @param string $in_hotpot_path
1529
     * @param bool $in_get_count
1530
     * @param null $where_condition
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $where_condition is correct as it would always require null to be passed?
Loading history...
1531
     * @return array|int
1532
     */
1533
    public static function get_exam_results_hotpotatoes_data(
1534
        $in_from,
1535
        $in_number_of_items,
1536
        $in_column,
1537
        $in_direction,
1538
        $in_hotpot_path,
1539
        $in_get_count = false,
1540
        $where_condition = null
1541
    ) {
1542
        $courseId = api_get_course_int_id();
1543
        // by default in_column = 1 If parameters given, it is the name of the column witch is the bdd field name
1544
        if ($in_column == 1) {
1545
            $in_column = 'firstname';
1546
        }
1547
        $in_hotpot_path = Database::escape_string($in_hotpot_path);
1548
        $in_direction = Database::escape_string($in_direction);
1549
        $in_column = Database::escape_string($in_column);
1550
        $in_number_of_items = intval($in_number_of_items);
1551
        $in_from = intval($in_from);
1552
1553
        $TBL_TRACK_HOTPOTATOES = Database::get_main_table(
1554
            TABLE_STATISTIC_TRACK_E_HOTPOTATOES
1555
        );
1556
        $TBL_USER = Database::get_main_table(TABLE_MAIN_USER);
1557
1558
        $sql = "SELECT * FROM $TBL_TRACK_HOTPOTATOES thp
1559
            JOIN $TBL_USER u ON thp.exe_user_id = u.user_id
1560
            WHERE thp.c_id = $courseId AND exe_name LIKE '$in_hotpot_path%'";
1561
1562
        // just count how many answers
1563
        if ($in_get_count) {
1564
            $res = Database::query($sql);
1565
            return Database::num_rows($res);
1566
        }
1567
        // get a number of sorted results
1568
        $sql .= " $where_condition
1569
            ORDER BY $in_column $in_direction
1570
            LIMIT $in_from, $in_number_of_items";
1571
1572
        $res = Database::query($sql);
1573
        $result = [];
1574
        $apiIsAllowedToEdit = api_is_allowed_to_edit();
1575
        $urlBase = api_get_path(WEB_CODE_PATH).
1576
            'exercise/hotpotatoes_exercise_report.php?action=delete&'.
1577
            api_get_cidreq().'&id=';
1578
        while ($data = Database::fetch_array($res)) {
1579
            $actions = null;
1580
1581
            if ($apiIsAllowedToEdit) {
1582
                $url = $urlBase.$data['id'].'&path='.$data['exe_name'];
1583
                $actions = Display::url(
1584
                    Display::return_icon('delete.png', get_lang('Delete')),
1585
                    $url
1586
                );
1587
            }
1588
1589
            $result[] = [
1590
                'firstname' => $data['firstname'],
1591
                'lastname' => $data['lastname'],
1592
                'username' => $data['username'],
1593
                'group_name' => implode(
1594
                    "<br/>",
1595
                    GroupManager::get_user_group_name($data['user_id'])
1596
                ),
1597
                'exe_date' => $data['exe_date'],
1598
                'score' => $data['exe_result'].' / '.$data['exe_weighting'],
1599
                'actions' => $actions,
1600
            ];
1601
        }
1602
1603
        return $result;
1604
    }
1605
1606
    /**
1607
     * @param string $exercisePath
1608
     * @param int $userId
1609
     * @param int $courseId
1610
     * @param int $sessionId
1611
     *
1612
     * @return array
1613
     */
1614
    public static function getLatestHotPotatoResult(
1615
        $exercisePath,
1616
        $userId,
1617
        $courseId,
1618
        $sessionId
1619
    ) {
1620
        $table = Database::get_main_table(
1621
            TABLE_STATISTIC_TRACK_E_HOTPOTATOES
1622
        );
1623
        $exercisePath = Database::escape_string($exercisePath);
1624
        $userId = intval($userId);
1625
1626
        $sql = "SELECT * FROM $table
1627
                WHERE
1628
                    c_id = $courseId AND
1629
                    exe_name LIKE '$exercisePath%' AND
1630
                    exe_user_id = $userId
1631
                ORDER BY id
1632
                LIMIT 1";
1633
        $result = Database::query($sql);
1634
        $attempt = [];
1635
        if (Database::num_rows($result)) {
1636
            $attempt = Database::fetch_array($result, 'ASSOC');
1637
        }
1638
        return $attempt;
1639
    }
1640
1641
    /**
1642
     * Gets the exam'data results
1643
     * @todo this function should be moved in a library  + no global calls
1644
     * @param int $from
1645
     * @param int $number_of_items
1646
     * @param int $column
1647
     * @param string $direction
1648
     * @param int $exercise_id
1649
     * @param null $extra_where_conditions
0 ignored issues
show
Documentation Bug introduced by
Are you sure the doc-type for parameter $extra_where_conditions is correct as it would always require null to be passed?
Loading history...
1650
     * @param bool $get_count
1651
     * @param string $courseCode
1652
     * @param bool $showSessionField
1653
     * @param bool $showExerciseCategories
1654
     * @param array $userExtraFieldsToAdd
1655
     *
1656
     * @return array
1657
     */
1658
    public static function get_exam_results_data(
1659
        $from,
1660
        $number_of_items,
1661
        $column,
1662
        $direction,
1663
        $exercise_id,
1664
        $extra_where_conditions = null,
1665
        $get_count = false,
1666
        $courseCode = null,
1667
        $showSessionField = false,
1668
        $showExerciseCategories = false,
1669
        $userExtraFieldsToAdd = []
1670
    ) {
1671
        //@todo replace all this globals
1672
        global $documentPath, $filter;
1673
1674
        $courseCode = empty($courseCode) ? api_get_course_id() : $courseCode;
1675
        $courseInfo = api_get_course_info($courseCode);
1676
1677
        if (empty($courseInfo)) {
1678
            return [];
1679
        }
1680
1681
        $course_id = $courseInfo['real_id'];
1682
        $is_allowedToEdit = api_is_allowed_to_edit(null, true) || api_is_allowed_to_edit(true) || api_is_drh() || api_is_student_boss();
1683
        $TBL_USER = Database::get_main_table(TABLE_MAIN_USER);
1684
        $TBL_EXERCICES = Database::get_course_table(TABLE_QUIZ_TEST);
1685
        $TBL_GROUP_REL_USER = Database::get_course_table(TABLE_GROUP_USER);
1686
        $TBL_GROUP = Database::get_course_table(TABLE_GROUP);
1687
        $TBL_TRACK_EXERCICES = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1688
        $TBL_TRACK_HOTPOTATOES = Database::get_main_table(TABLE_STATISTIC_TRACK_E_HOTPOTATOES);
1689
        $TBL_TRACK_ATTEMPT_RECORDING = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
1690
        $sessionId = api_get_session_id();
1691
        $session_id_and = '';
1692
        $sessionCondition = '';
1693
        if (!$showSessionField) {
1694
            $session_id_and = " AND te.session_id = $sessionId ";
1695
            $sessionCondition = " AND ttte.session_id = $sessionId";
1696
        }
1697
        $exercise_id = intval($exercise_id);
1698
        $exercise_where = '';
1699
        if (!empty($exercise_id)) {
1700
            $exercise_where .= ' AND te.exe_exo_id = '.$exercise_id.'  ';
1701
        }
1702
1703
        $hotpotatoe_where = '';
1704
        if (!empty($_GET['path'])) {
1705
            $hotpotatoe_path = Database::escape_string($_GET['path']);
1706
            $hotpotatoe_where .= ' AND exe_name = "'.$hotpotatoe_path.'"  ';
1707
        }
1708
1709
        // sql for chamilo-type tests for teacher / tutor view
1710
        $sql_inner_join_tbl_track_exercices = "
1711
        (
1712
            SELECT DISTINCT ttte.*, if(tr.exe_id,1, 0) as revised
1713
            FROM $TBL_TRACK_EXERCICES ttte 
1714
            LEFT JOIN $TBL_TRACK_ATTEMPT_RECORDING tr
1715
            ON (ttte.exe_id = tr.exe_id)
1716
            WHERE
1717
                c_id = $course_id AND
1718
                exe_exo_id = $exercise_id 
1719
                $sessionCondition
1720
        )";
1721
1722
        if ($is_allowedToEdit) {
1723
            //@todo fix to work with COURSE_RELATION_TYPE_RRHH in both queries
1724
            // Hack in order to filter groups
1725
            $sql_inner_join_tbl_user = '';
1726
            if (strpos($extra_where_conditions, 'group_id')) {
1727
                $sql_inner_join_tbl_user = "
1728
                (
1729
                    SELECT
1730
                        u.user_id,
1731
                        firstname,
1732
                        lastname,
1733
                        official_code,
1734
                        email,
1735
                        username,
1736
                        g.name as group_name,
1737
                        g.id as group_id
1738
                    FROM $TBL_USER u
1739
                    INNER JOIN $TBL_GROUP_REL_USER gru
1740
                    ON (gru.user_id = u.user_id AND gru.c_id= $course_id )
1741
                    INNER JOIN $TBL_GROUP g
1742
                    ON (gru.group_id = g.id AND g.c_id= $course_id )
1743
                )";
1744
            }
1745
1746
            if (strpos($extra_where_conditions, 'group_all')) {
1747
                $extra_where_conditions = str_replace(
1748
                    "AND (  group_id = 'group_all'  )",
1749
                    '',
1750
                    $extra_where_conditions
1751
                );
1752
                $extra_where_conditions = str_replace(
1753
                    "AND group_id = 'group_all'",
1754
                    '',
1755
                    $extra_where_conditions
1756
                );
1757
                $extra_where_conditions = str_replace(
1758
                    "group_id = 'group_all' AND",
1759
                    '',
1760
                    $extra_where_conditions
1761
                );
1762
1763
                $sql_inner_join_tbl_user = "
1764
                (
1765
                    SELECT
1766
                        u.user_id,
1767
                        firstname,
1768
                        lastname,
1769
                        official_code,
1770
                        email,
1771
                        username,
1772
                        '' as group_name,
1773
                        '' as group_id
1774
                    FROM $TBL_USER u
1775
                )";
1776
                $sql_inner_join_tbl_user = null;
1777
            }
1778
1779
            if (strpos($extra_where_conditions, 'group_none')) {
1780
                $extra_where_conditions = str_replace(
1781
                    "AND (  group_id = 'group_none'  )",
1782
                    "AND (  group_id is null  )",
1783
                    $extra_where_conditions
1784
                );
1785
                $extra_where_conditions = str_replace(
1786
                    "AND group_id = 'group_none'",
1787
                    "AND (  group_id is null  )",
1788
                    $extra_where_conditions
1789
                );
1790
                $sql_inner_join_tbl_user = "
1791
            (
1792
                SELECT
1793
                    u.user_id,
1794
                    firstname,
1795
                    lastname,
1796
                    official_code,
1797
                    email,
1798
                    username,
1799
                    g.name as group_name,
1800
                    g.id as group_id
1801
                FROM $TBL_USER u
1802
                LEFT OUTER JOIN $TBL_GROUP_REL_USER gru
1803
                ON ( gru.user_id = u.user_id AND gru.c_id= $course_id )
1804
                LEFT OUTER JOIN $TBL_GROUP g
1805
                ON (gru.group_id = g.id AND g.c_id = $course_id )
1806
            )";
1807
            }
1808
1809
            // All
1810
            $is_empty_sql_inner_join_tbl_user = false;
1811
            if (empty($sql_inner_join_tbl_user)) {
1812
                $is_empty_sql_inner_join_tbl_user = true;
1813
                $sql_inner_join_tbl_user = "
1814
            (
1815
                SELECT u.user_id, firstname, lastname, email, username, ' ' as group_name, '' as group_id, official_code
1816
                FROM $TBL_USER u
1817
                WHERE u.status NOT IN(".api_get_users_status_ignored_in_reports('string').")
1818
            )";
1819
            }
1820
1821
            $sqlFromOption = " , $TBL_GROUP_REL_USER AS gru ";
1822
            $sqlWhereOption = "  AND gru.c_id = $course_id AND gru.user_id = user.user_id ";
1823
            $first_and_last_name = api_is_western_name_order() ? "firstname, lastname" : "lastname, firstname";
1824
1825
            if ($get_count) {
1826
                $sql_select = "SELECT count(te.exe_id) ";
1827
            } else {
1828
                $sql_select = "SELECT DISTINCT
1829
                    user_id,
1830
                    $first_and_last_name,
1831
                    official_code,
1832
                    ce.title,
1833
                    username,
1834
                    te.exe_result,
1835
                    te.exe_weighting,
1836
                    te.exe_date,
1837
                    te.exe_id,
1838
                    te.session_id,
1839
                    email as exemail,
1840
                    te.start_date,
1841
                    ce.expired_time,
1842
                    steps_counter,
1843
                    exe_user_id,
1844
                    te.exe_duration,
1845
                    te.status as completion_status,
1846
                    propagate_neg,
1847
                    revised,
1848
                    group_name,
1849
                    group_id,
1850
                    orig_lp_id,
1851
                    te.user_ip";
1852
            }
1853
1854
            $sql = " $sql_select
1855
                FROM $TBL_EXERCICES AS ce
1856
                INNER JOIN $sql_inner_join_tbl_track_exercices AS te
1857
                ON (te.exe_exo_id = ce.id)
1858
                INNER JOIN $sql_inner_join_tbl_user AS user
1859
                ON (user.user_id = exe_user_id)
1860
                WHERE
1861
                    te.c_id = $course_id $session_id_and AND
1862
                    ce.active <> -1 AND 
1863
                    ce.c_id = $course_id
1864
                    $exercise_where
1865
                    $extra_where_conditions
1866
                ";
1867
1868
            // sql for hotpotatoes tests for teacher / tutor view
1869
            if ($get_count) {
1870
                $hpsql_select = "SELECT count(username)";
1871
            } else {
1872
                $hpsql_select = "SELECT
1873
                    $first_and_last_name ,
1874
                    username,
1875
                    official_code,
1876
                    tth.exe_name,
1877
                    tth.exe_result ,
1878
                    tth.exe_weighting,
1879
                    tth.exe_date";
1880
            }
1881
1882
            $hpsql = " $hpsql_select
1883
                FROM
1884
                    $TBL_TRACK_HOTPOTATOES tth,
1885
                    $TBL_USER user
1886
                    $sqlFromOption
1887
                WHERE
1888
                    user.user_id=tth.exe_user_id
1889
                    AND tth.c_id = $course_id
1890
                    $hotpotatoe_where
1891
                    $sqlWhereOption
1892
                    AND user.status NOT IN(".api_get_users_status_ignored_in_reports('string').")
1893
                ORDER BY
1894
                    tth.c_id ASC,
1895
                    tth.exe_date DESC";
1896
        }
1897
1898
        if (empty($sql)) {
1899
            return false;
1900
        }
1901
1902
        if ($get_count) {
1903
            $resx = Database::query($sql);
1904
            $rowx = Database::fetch_row($resx, 'ASSOC');
1905
1906
            return $rowx[0];
1907
        }
1908
1909
        $teacher_list = CourseManager::get_teacher_list_from_course_code(
1910
            $courseCode
1911
        );
1912
        $teacher_id_list = [];
1913
        if (!empty($teacher_list)) {
1914
            foreach ($teacher_list as $teacher) {
1915
                $teacher_id_list[] = $teacher['user_id'];
1916
            }
1917
        }
1918
1919
        $listInfo = [];
1920
        // Simple exercises
1921
        if (empty($hotpotatoe_where)) {
1922
            $column = !empty($column) ? Database::escape_string($column) : null;
1923
            $from = intval($from);
1924
            $number_of_items = intval($number_of_items);
1925
1926
            if (!empty($column)) {
1927
                $sql .= " ORDER BY $column $direction ";
1928
            }
1929
            $sql .= " LIMIT $from, $number_of_items";
1930
1931
            $results = [];
1932
            $resx = Database::query($sql);
1933
            while ($rowx = Database::fetch_array($resx, 'ASSOC')) {
1934
                $results[] = $rowx;
1935
            }
1936
1937
            $group_list = GroupManager::get_group_list(null, $courseInfo);
1938
            $clean_group_list = [];
1939
            if (!empty($group_list)) {
1940
                foreach ($group_list as $group) {
1941
                    $clean_group_list[$group['id']] = $group['name'];
1942
                }
1943
            }
1944
1945
            $lp_list_obj = new LearnpathList(api_get_user_id());
1946
            $lp_list = $lp_list_obj->get_flat_list();
1947
            $oldIds = array_column($lp_list, 'lp_old_id', 'iid');
1948
1949
            if (is_array($results)) {
1950
                $users_array_id = [];
1951
                $from_gradebook = false;
1952
                if (isset($_GET['gradebook']) && $_GET['gradebook'] == 'view') {
1953
                    $from_gradebook = true;
1954
                }
1955
                $sizeof = count($results);
1956
                $user_list_id = [];
1957
                $locked = api_resource_is_locked_by_gradebook(
1958
                    $exercise_id,
1959
                    LINK_EXERCISE
1960
                );
1961
1962
                $timeNow = strtotime(api_get_utc_datetime());
1963
                // Looping results
1964
                for ($i = 0; $i < $sizeof; $i++) {
1965
                    $revised = $results[$i]['revised'];
1966
                    if ($results[$i]['completion_status'] == 'incomplete') {
1967
                        // If the exercise was incomplete, we need to determine
1968
                        // if it is still into the time allowed, or if its
1969
                        // allowed time has expired and it can be closed
1970
                        // (it's "unclosed")
1971
                        $minutes = $results[$i]['expired_time'];
1972
                        if ($minutes == 0) {
1973
                            // There's no time limit, so obviously the attempt
1974
                            // can still be "ongoing", but the teacher should
1975
                            // be able to choose to close it, so mark it as
1976
                            // "unclosed" instead of "ongoing"
1977
                            $revised = 2;
1978
                        } else {
1979
                            $allowedSeconds = $minutes * 60;
1980
                            $timeAttemptStarted = strtotime($results[$i]['start_date']);
1981
                            $secondsSinceStart = $timeNow - $timeAttemptStarted;
1982
                            if ($secondsSinceStart > $allowedSeconds) {
1983
                                $revised = 2; // mark as "unclosed"
1984
                            } else {
1985
                                $revised = 3; // mark as "ongoing"
1986
                            }
1987
                        }
1988
                    }
1989
1990
                    if ($from_gradebook && ($is_allowedToEdit)) {
1991
                        if (in_array(
1992
                            $results[$i]['username'].$results[$i]['firstname'].$results[$i]['lastname'],
1993
                            $users_array_id
1994
                        )) {
1995
                            continue;
1996
                        }
1997
                        $users_array_id[] = $results[$i]['username'].$results[$i]['firstname'].$results[$i]['lastname'];
1998
                    }
1999
2000
                    $lp_obj = isset($results[$i]['orig_lp_id']) && isset($lp_list[$results[$i]['orig_lp_id']]) ? $lp_list[$results[$i]['orig_lp_id']] : null;
2001
                    if (empty($lp_obj)) {
2002
                        // Try to get the old id (id instead of iid)
2003
                        $lpNewId = isset($results[$i]['orig_lp_id']) && isset($oldIds[$results[$i]['orig_lp_id']]) ? $oldIds[$results[$i]['orig_lp_id']] : null;
2004
                        if ($lpNewId) {
2005
                            $lp_obj = isset($lp_list[$lpNewId]) ? $lp_list[$lpNewId] : null;
2006
                        }
2007
                    }
2008
                    $lp_name = null;
2009
                    if ($lp_obj) {
2010
                        $url = api_get_path(WEB_CODE_PATH).'lp/lp_controller.php?'.api_get_cidreq().'&action=view&lp_id='.$results[$i]['orig_lp_id'];
2011
                        $lp_name = Display::url(
2012
                            $lp_obj['lp_name'],
2013
                            $url,
2014
                            ['target' => '_blank']
2015
                        );
2016
                    }
2017
2018
                    // Add all groups by user
2019
                    $group_name_list = '';
2020
                    if ($is_empty_sql_inner_join_tbl_user) {
2021
                        $group_list = GroupManager::get_group_ids(
2022
                            api_get_course_int_id(),
2023
                            $results[$i]['user_id']
2024
                        );
2025
2026
                        foreach ($group_list as $id) {
2027
                            if (isset($clean_group_list[$id])) {
2028
                                $group_name_list .= $clean_group_list[$id].'<br/>';
2029
                            }
2030
                        }
2031
                        $results[$i]['group_name'] = $group_name_list;
2032
                    }
2033
2034
                    $results[$i]['exe_duration'] = !empty($results[$i]['exe_duration']) ? round($results[$i]['exe_duration'] / 60) : 0;
2035
2036
                    $user_list_id[] = $results[$i]['exe_user_id'];
2037
                    $id = $results[$i]['exe_id'];
2038
                    $dt = api_convert_and_format_date($results[$i]['exe_weighting']);
2039
2040
                    // we filter the results if we have the permission to
2041
                    $result_disabled = 0;
2042
                    if (isset($results[$i]['results_disabled'])) {
2043
                        $result_disabled = intval(
2044
                            $results[$i]['results_disabled']
2045
                        );
2046
                    }
2047
                    if ($result_disabled == 0) {
2048
                        $my_res = $results[$i]['exe_result'];
2049
                        $my_total = $results[$i]['exe_weighting'];
2050
2051
                        $results[$i]['start_date'] = api_get_local_time($results[$i]['start_date']);
2052
                        $results[$i]['exe_date'] = api_get_local_time($results[$i]['exe_date']);
2053
2054
                        if (!$results[$i]['propagate_neg'] && $my_res < 0) {
2055
                            $my_res = 0;
2056
                        }
2057
2058
                        $score = self::show_score($my_res, $my_total);
2059
2060
                        $actions = '<div class="pull-right">';
2061
                        if ($is_allowedToEdit) {
2062
                            if (isset($teacher_id_list)) {
2063
                                if (in_array(
2064
                                    $results[$i]['exe_user_id'],
2065
                                    $teacher_id_list
2066
                                )) {
2067
                                    $actions .= Display::return_icon(
2068
                                        'teacher.png',
2069
                                        get_lang('Teacher')
2070
                                    );
2071
                                }
2072
                            }
2073
                            $revisedLabel = '';
2074
                            switch ($revised) {
2075
                                case 0:
2076
                                    $actions .= "<a href='exercise_show.php?".api_get_cidreq()."&action=qualify&id=$id'>".
2077
                                        Display:: return_icon(
2078
                                            'quiz.png',
2079
                                            get_lang('Qualify')
2080
                                        );
2081
                                    $actions .= '</a>';
2082
                                    $revisedLabel = Display::label(
2083
                                        get_lang('NotValidated'),
2084
                                        'info'
2085
                                    );
2086
                                    break;
2087
                                case 1:
2088
                                    $actions .= "<a href='exercise_show.php?".api_get_cidreq()."&action=edit&id=$id'>".
2089
                                        Display:: return_icon(
2090
                                            'edit.png',
2091
                                            get_lang('Edit'),
2092
                                            [],
2093
                                            ICON_SIZE_SMALL
2094
                                        );
2095
                                    $actions .= '</a>';
2096
                                    $revisedLabel = Display::label(
2097
                                        get_lang('Validated'),
2098
                                        'success'
2099
                                    );
2100
                                    break;
2101
                                case 2: //finished but not marked as such
2102
                                    $actions .= '<a href="exercise_report.php?'
2103
                                        .api_get_cidreq()
2104
                                        .'&exerciseId='
2105
                                        .$exercise_id
2106
                                        .'&a=close&id='
2107
                                        .$id
2108
                                        .'">'.
2109
                                        Display:: return_icon(
2110
                                            'lock.png',
2111
                                            get_lang('MarkAttemptAsClosed'),
2112
                                            [],
2113
                                            ICON_SIZE_SMALL
2114
                                        );
2115
                                    $actions .= '</a>';
2116
                                    $revisedLabel = Display::label(
2117
                                        get_lang('Unclosed'),
2118
                                        'warning'
2119
                                    );
2120
                                    break;
2121
                                case 3: //still ongoing
2122
                                    $actions .= Display:: return_icon(
2123
                                        'clock.png',
2124
                                        get_lang('AttemptStillOngoingPleaseWait'),
2125
                                        [],
2126
                                        ICON_SIZE_SMALL
2127
                                    );
2128
                                    $actions .= '';
2129
                                    $revisedLabel = Display::label(
2130
                                        get_lang('Ongoing'),
2131
                                        'danger'
2132
                                    );
2133
                                    break;
2134
                            }
2135
2136
                            if ($filter == 2) {
2137
                                $actions .= ' <a href="exercise_history.php?'.api_get_cidreq().'&exe_id='.$id.'">'.
2138
                                    Display:: return_icon(
2139
                                        'history.png',
2140
                                        get_lang('ViewHistoryChange')
2141
                                    ).'</a>';
2142
                            }
2143
2144
                            // Admin can always delete the attempt
2145
                            if (($locked == false || api_is_platform_admin()) && !api_is_student_boss()) {
2146
                                $ip = Tracking::get_ip_from_user_event(
2147
                                    $results[$i]['exe_user_id'],
2148
                                    api_get_utc_datetime(),
2149
                                    false
2150
                                );
2151
                                $actions .= '<a href="http://www.whatsmyip.org/ip-geo-location/?ip='.$ip.'" target="_blank">'
2152
                                    .Display::return_icon('info.png', $ip)
2153
                                    .'</a>';
2154
2155
                                $recalculateUrl = api_get_path(WEB_CODE_PATH).'exercise/recalculate.php?'.
2156
                                    api_get_cidreq().'&'.
2157
                                    http_build_query([
2158
                                        'id' => $id,
2159
                                        'exercise' => $exercise_id,
2160
                                        'user' => $results[$i]['exe_user_id']
2161
                                    ]);
2162
                                $actions .= Display::url(
2163
                                    Display::return_icon('reload.png', get_lang('RecalculateResults')),
2164
                                    $recalculateUrl,
2165
                                    [
2166
                                        'data-exercise' => $exercise_id,
2167
                                        'data-user' => $results[$i]['exe_user_id'],
2168
                                        'data-id' => $id,
2169
                                        'class' => 'exercise-recalculate'
2170
                                    ]
2171
                                );
2172
2173
                                $filterByUser = isset($_GET['filter_by_user']) ? (int) $_GET['filter_by_user'] : 0;
2174
                                $delete_link = '<a href="exercise_report.php?'.api_get_cidreq().'&filter_by_user='.$filterByUser.'&filter='.$filter.'&exerciseId='.$exercise_id.'&delete=delete&did='.$id.'"
2175
                                onclick="javascript:if(!confirm(\'' . sprintf(
2176
                                    get_lang('DeleteAttempt'),
2177
                                    $results[$i]['username'],
2178
                                    $dt
2179
                                ).'\')) return false;">';
2180
                                $delete_link .=
2181
                                    Display:: return_icon(
2182
                                        'delete.png',
2183
                                        get_lang('Delete')
2184
                                    ).'</a>';
2185
                                $delete_link = utf8_encode($delete_link);
2186
2187
                                if (api_is_drh() && !api_is_platform_admin()) {
2188
                                    $delete_link = null;
2189
                                }
2190
                                if ($revised == 3) {
2191
                                    $delete_link = null;
2192
                                }
2193
                                $actions .= $delete_link;
2194
                            }
2195
                        } else {
2196
                            $attempt_url = api_get_path(WEB_CODE_PATH).'exercise/result.php?'.api_get_cidreq().'&id='.$results[$i]['exe_id'].'&id_session='.$sessionId;
2197
                            $attempt_link = Display::url(
2198
                                get_lang('Show'),
2199
                                $attempt_url,
2200
                                [
2201
                                    'class' => 'ajax btn btn-default',
2202
                                    'data-title' => get_lang('Show')
2203
                                ]
2204
                            );
2205
                            $actions .= $attempt_link;
2206
                        }
2207
                        $actions .= '</div>';
2208
2209
                        if (!empty($userExtraFieldsToAdd)) {
2210
                            foreach ($userExtraFieldsToAdd as $variable) {
2211
                                $extraFieldValue = new ExtraFieldValue('user');
2212
                                $values = $extraFieldValue->get_values_by_handler_and_field_variable(
2213
                                    $results[$i]['user_id'],
2214
                                    $variable
2215
                                );
2216
                                if (isset($values['value'])) {
2217
                                    $results[$i][$variable] = $values['value'];
2218
                                }
2219
                            }
2220
                        }
2221
2222
                        $exeId = $results[$i]['exe_id'];
2223
                        $results[$i]['id'] = $exeId;
2224
                        $category_list = [];
2225
                        if ($is_allowedToEdit) {
2226
                            $sessionName = '';
2227
                            $sessionStartAccessDate = '';
2228
                            if (!empty($results[$i]['session_id'])) {
2229
                                $sessionInfo = api_get_session_info($results[$i]['session_id']);
2230
                                if (!empty($sessionInfo)) {
2231
                                    $sessionName = $sessionInfo['name'];
2232
                                    $sessionStartAccessDate = api_get_local_time($sessionInfo['access_start_date']);
2233
                                }
2234
                            }
2235
2236
                            $objExercise = new Exercise($course_id);
2237
                            if ($showExerciseCategories) {
2238
                                // Getting attempt info
2239
                                $exercise_stat_info = $objExercise->get_stat_track_exercise_info_by_exe_id($exeId);
2240
                                if (!empty($exercise_stat_info['data_tracking'])) {
2241
                                    $question_list = explode(',', $exercise_stat_info['data_tracking']);
2242
                                    if (!empty($question_list)) {
2243
                                        foreach ($question_list as $questionId) {
2244
                                            $objQuestionTmp = Question::read($questionId, $course_id);
2245
                                            // We're inside *one* question. Go through each possible answer for this question
2246
                                            $result = $objExercise->manage_answer(
2247
                                                $exeId,
2248
                                                $questionId,
2249
                                                null,
2250
                                                'exercise_result',
2251
                                                false,
2252
                                                false,
2253
                                                true,
2254
                                                false,
2255
                                                $objExercise->selectPropagateNeg(),
2256
                                                null,
2257
                                                true
2258
                                            );
2259
2260
                                            $my_total_score = $result['score'];
2261
                                            $my_total_weight = $result['weight'];
2262
2263
                                            // Category report
2264
                                            $category_was_added_for_this_test = false;
2265
                                            if (isset($objQuestionTmp->category) && !empty($objQuestionTmp->category)) {
2266
                                                if (!isset($category_list[$objQuestionTmp->category]['score'])) {
2267
                                                    $category_list[$objQuestionTmp->category]['score'] = 0;
2268
                                                }
2269
                                                if (!isset($category_list[$objQuestionTmp->category]['total'])) {
2270
                                                    $category_list[$objQuestionTmp->category]['total'] = 0;
2271
                                                }
2272
                                                $category_list[$objQuestionTmp->category]['score'] += $my_total_score;
2273
                                                $category_list[$objQuestionTmp->category]['total'] += $my_total_weight;
2274
                                                $category_was_added_for_this_test = true;
2275
                                            }
2276
2277
                                            if (isset($objQuestionTmp->category_list) && !empty($objQuestionTmp->category_list)) {
2278
                                                foreach ($objQuestionTmp->category_list as $category_id) {
2279
                                                    $category_list[$category_id]['score'] += $my_total_score;
2280
                                                    $category_list[$category_id]['total'] += $my_total_weight;
2281
                                                    $category_was_added_for_this_test = true;
2282
                                                }
2283
                                            }
2284
2285
                                            // No category for this question!
2286
                                            if ($category_was_added_for_this_test == false) {
2287
                                                if (!isset($category_list['none']['score'])) {
2288
                                                    $category_list['none']['score'] = 0;
2289
                                                }
2290
                                                if (!isset($category_list['none']['total'])) {
2291
                                                    $category_list['none']['total'] = 0;
2292
                                                }
2293
2294
                                                $category_list['none']['score'] += $my_total_score;
2295
                                                $category_list['none']['total'] += $my_total_weight;
2296
                                            }
2297
                                        }
2298
                                    }
2299
                                }
2300
                            }
2301
2302
                            foreach ($category_list as $categoryId => $result) {
2303
                                $scoreToDisplay = self::show_score($result['score'], $result['total']);
2304
                                $results[$i]['category_'.$categoryId] = $scoreToDisplay;
2305
                                $results[$i]['category_'.$categoryId.'_score_percentage'] = self::show_score(
2306
                                    $result['score'],
2307
                                    $result['total'],
2308
                                    true,
2309
                                    true,
2310
                                    true, // $show_only_percentage = false
2311
                                    true // hide % sign
2312
                                );
2313
                                $results[$i]['category_'.$categoryId.'_only_score'] = $result['score'];
2314
                                $results[$i]['category_'.$categoryId.'_total'] = $result['total'];
2315
                            }
2316
                            $results[$i]['session'] = $sessionName;
2317
                            $results[$i]['session_access_start_date'] = $sessionStartAccessDate;
2318
                            $results[$i]['status'] = $revisedLabel;
2319
                            $results[$i]['score'] = $score;
2320
                            $results[$i]['score_percentage'] = self::show_score(
2321
                                $my_res,
2322
                                $my_total,
2323
                                true,
2324
                                true,
2325
                                true,
2326
                                true
2327
                            );
2328
                            $results[$i]['only_score'] = $my_res;
2329
                            $results[$i]['total'] = $my_total;
2330
                            $results[$i]['lp'] = $lp_name;
2331
                            $results[$i]['actions'] = $actions;
2332
                            $listInfo[] = $results[$i];
2333
                        } else {
2334
                            $results[$i]['status'] = $revisedLabel;
2335
                            $results[$i]['score'] = $score;
2336
                            $results[$i]['actions'] = $actions;
2337
                            $listInfo[] = $results[$i];
2338
                        }
2339
                    }
2340
                }
2341
            }
2342
        } else {
2343
            $hpresults = [];
2344
            $res = Database::query($hpsql);
2345
            if ($res !== false) {
2346
                $i = 0;
2347
                while ($resA = Database::fetch_array($res, 'NUM')) {
2348
                    for ($j = 0; $j < 6; $j++) {
2349
                        $hpresults[$i][$j] = $resA[$j];
2350
                    }
2351
                    $i++;
2352
                }
2353
            }
2354
2355
            // Print HotPotatoes test results.
2356
            if (is_array($hpresults)) {
2357
                for ($i = 0; $i < sizeof($hpresults); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function sizeof() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
2358
                    $hp_title = GetQuizName($hpresults[$i][3], $documentPath);
2359
                    if ($hp_title == '') {
2360
                        $hp_title = basename($hpresults[$i][3]);
2361
                    }
2362
2363
                    $hp_date = api_get_local_time(
2364
                        $hpresults[$i][6],
2365
                        null,
2366
                        date_default_timezone_get()
2367
                    );
2368
                    $hp_result = round(($hpresults[$i][4] / ($hpresults[$i][5] != 0 ? $hpresults[$i][5] : 1)) * 100, 2);
2369
                    $hp_result .= '% ('.$hpresults[$i][4].' / '.$hpresults[$i][5].')';
2370
2371
                    if ($is_allowedToEdit) {
2372
                        $listInfo[] = [
2373
                            $hpresults[$i][0],
2374
                            $hpresults[$i][1],
2375
                            $hpresults[$i][2],
2376
                            '',
2377
                            $hp_title,
2378
                            '-',
2379
                            $hp_date,
2380
                            $hp_result,
2381
                            '-'
2382
                        ];
2383
                    } else {
2384
                        $listInfo[] = [
2385
                            $hp_title,
2386
                            '-',
2387
                            $hp_date,
2388
                            $hp_result,
2389
                            '-'
2390
                        ];
2391
                    }
2392
                }
2393
            }
2394
        }
2395
2396
        return $listInfo;
2397
    }
2398
2399
    /**
2400
     * Converts the score with the exercise_max_note and exercise_min_score
2401
     * the platform settings + formats the results using the float_format function
2402
     *
2403
     * @param float $score
2404
     * @param float $weight
2405
     * @param bool $show_percentage show percentage or not
2406
     * @param bool $use_platform_settings use or not the platform settings
2407
     * @param bool $show_only_percentage
2408
     * @param bool $hidePercetangeSign hide "%" sign
2409
     * @return  string  an html with the score modified
2410
     */
2411
    public static function show_score(
2412
        $score,
2413
        $weight,
2414
        $show_percentage = true,
2415
        $use_platform_settings = true,
2416
        $show_only_percentage = false,
2417
        $hidePercetangeSign = false
2418
    ) {
2419
        if (is_null($score) && is_null($weight)) {
2420
            return '-';
2421
        }
2422
2423
        $maxNote = api_get_setting('exercise_max_score');
2424
        $minNote = api_get_setting('exercise_min_score');
2425
2426
        if ($use_platform_settings) {
2427
            if ($maxNote != '' && $minNote != '') {
2428
                if (!empty($weight) && intval($weight) != 0) {
2429
                    $score = $minNote + ($maxNote - $minNote) * $score / $weight;
2430
                } else {
2431
                    $score = $minNote;
2432
                }
2433
                $weight = $maxNote;
2434
            }
2435
        }
2436
        $percentage = (100 * $score) / ($weight != 0 ? $weight : 1);
2437
2438
        // Formats values
2439
        $percentage = float_format($percentage, 1);
2440
        $score = float_format($score, 1);
2441
        $weight = float_format($weight, 1);
2442
2443
        $html = '';
2444
        if ($show_percentage) {
2445
            $percentageSign = '%';
2446
            if ($hidePercetangeSign) {
2447
                $percentageSign = '';
2448
            }
2449
            $html = $percentage."$percentageSign  ($score / $weight)";
2450
            if ($show_only_percentage) {
2451
                $html = $percentage."$percentageSign ";
2452
            }
2453
        } else {
2454
            $html = $score.' / '.$weight;
2455
        }
2456
2457
        // Over write score
2458
        $scoreBasedInModel = self::convertScoreToModel($percentage);
2459
        if (!empty($scoreBasedInModel)) {
2460
            $html = $scoreBasedInModel;
2461
        }
2462
2463
        $html = Display::span($html, ['class' => 'score_exercise']);
2464
2465
        return $html;
2466
    }
2467
2468
    /**
2469
     * @param array $model
2470
     * @param float $percentage
2471
     * @return string
2472
     */
2473
    public static function getModelStyle($model, $percentage)
2474
    {
2475
        //$modelWithStyle = get_lang($model['name']);
2476
        $modelWithStyle = '<span class="'.$model['css_class'].'"> &nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;</span>';
2477
        //$modelWithStyle .= $percentage;
2478
2479
        return $modelWithStyle;
2480
    }
2481
2482
    /**
2483
     * @param float $percentage value between 0 and 100
2484
     * @return string
2485
     */
2486
    public static function convertScoreToModel($percentage)
2487
    {
2488
        $model = self::getCourseScoreModel();
2489
        if (!empty($model)) {
2490
            $scoreWithGrade = [];
2491
            foreach ($model['score_list'] as $item) {
2492
                if ($percentage >= $item['min'] && $percentage <= $item['max']) {
2493
                    $scoreWithGrade = $item;
2494
                    break;
2495
                }
2496
            }
2497
2498
            if (!empty($scoreWithGrade)) {
2499
                return self::getModelStyle($scoreWithGrade, $percentage);
2500
            }
2501
        }
2502
        return '';
2503
    }
2504
2505
    /**
2506
     * @return array
2507
     */
2508
    public static function getCourseScoreModel()
2509
    {
2510
        $modelList = self::getScoreModels();
2511
        if (empty($modelList)) {
2512
            return [];
2513
        }
2514
2515
        $courseInfo = api_get_course_info();
2516
        if (!empty($courseInfo)) {
2517
            $scoreModelId = api_get_course_setting('score_model_id');
2518
            if ($scoreModelId != -1) {
2519
                $modelIdList = array_column($modelList['models'], 'id');
2520
                if (in_array($scoreModelId, $modelIdList)) {
2521
                    foreach ($modelList['models'] as $item) {
2522
                        if ($item['id'] == $scoreModelId) {
2523
                            return $item;
2524
                        }
2525
                    }
2526
                }
2527
            }
2528
        }
2529
2530
        return [];
2531
    }
2532
2533
    /**
2534
     * @return array
2535
     */
2536
    public static function getScoreModels()
2537
    {
2538
        return api_get_configuration_value('score_grade_model');
2539
    }
2540
2541
    /**
2542
     * @param float $score
2543
     * @param float $weight
2544
     * @param string $pass_percentage
2545
     * @return bool
2546
     */
2547
    public static function isSuccessExerciseResult($score, $weight, $pass_percentage)
2548
    {
2549
        $percentage = float_format(
2550
            ($score / ($weight != 0 ? $weight : 1)) * 100,
2551
            1
2552
        );
2553
        if (isset($pass_percentage) && !empty($pass_percentage)) {
2554
            if ($percentage >= $pass_percentage) {
2555
                return true;
2556
            }
2557
        }
2558
        return false;
2559
    }
2560
2561
    /**
2562
     * @param FormValidator $form
2563
     * @param string $name
2564
     * @param $weight
2565
     * @param $selected
2566
     * @return bool
2567
     */
2568
    public static function addScoreModelInput(
2569
        FormValidator & $form,
2570
        $name,
2571
        $weight,
2572
        $selected
2573
    ) {
2574
        $model = self::getCourseScoreModel();
2575
        if (empty($model)) {
2576
            return false;
2577
        }
2578
2579
        /** @var HTML_QuickForm_select $element */
2580
        $element = $form->createElement(
2581
            'select',
2582
            $name,
2583
            get_lang('Qualification'),
2584
            [],
2585
            ['class' => 'exercise_mark_select']
2586
        );
2587
2588
        foreach ($model['score_list'] as $item) {
2589
            $i = api_number_format($item['score_to_qualify'] / 100 * $weight, 2);
2590
            $label = ExerciseLib::getModelStyle($item, $i);
2591
            $attributes = [
2592
                'class' => $item['css_class']
2593
            ];
2594
            if ($selected == $i) {
2595
                $attributes['selected'] = 'selected';
2596
            }
2597
            $element->addOption($label, $i, $attributes);
2598
        }
2599
        $form->addElement($element);
2600
    }
2601
2602
    /**
2603
     * @return string
2604
     */
2605
    public static function getJsCode()
2606
    {
2607
        // Filling the scores with the right colors.
2608
        $models = ExerciseLib::getCourseScoreModel();
2609
        $cssListToString = '';
2610
        if (!empty($models)) {
2611
            $cssList = array_column($models['score_list'], 'css_class');
2612
            $cssListToString = implode(' ', $cssList);
2613
        }
2614
2615
        if (empty($cssListToString)) {
2616
            return '';
2617
        }
2618
        $js = <<<EOT
2619
        
2620
        function updateSelect(element) {
2621
            var spanTag = element.parent().find('span.filter-option');
2622
            var value = element.val();
2623
            var selectId = element.attr('id');
2624
            var optionClass = $('#' + selectId + ' option[value="'+value+'"]').attr('class');
2625
            spanTag.removeClass('$cssListToString');
2626
            spanTag.addClass(optionClass);
2627
        }
2628
        
2629
        $(document).ready( function() {
2630
            // Loading values
2631
            $('.exercise_mark_select').on('loaded.bs.select', function() {
2632
                updateSelect($(this));
2633
            });
2634
            // On change
2635
            $('.exercise_mark_select').on('changed.bs.select', function() {
2636
                updateSelect($(this));
2637
            });
2638
        });
2639
EOT;
2640
2641
        return $js;
2642
    }
2643
2644
    /**
2645
     * @param float $score
2646
     * @param float $weight
2647
     * @param string $pass_percentage
2648
     * @return string
2649
     */
2650
    public static function showSuccessMessage($score, $weight, $pass_percentage)
2651
    {
2652
        $res = '';
2653
        if (self::isPassPercentageEnabled($pass_percentage)) {
2654
            $isSuccess = self::isSuccessExerciseResult(
2655
                $score,
2656
                $weight,
2657
                $pass_percentage
2658
            );
2659
2660
            if ($isSuccess) {
2661
                $html = get_lang('CongratulationsYouPassedTheTest');
2662
                $icon = Display::return_icon(
2663
                    'completed.png',
2664
                    get_lang('Correct'),
2665
                    [],
2666
                    ICON_SIZE_MEDIUM
2667
                );
2668
            } else {
2669
                //$html .= Display::return_message(get_lang('YouDidNotReachTheMinimumScore'), 'warning');
2670
                $html = get_lang('YouDidNotReachTheMinimumScore');
2671
                $icon = Display::return_icon(
2672
                    'warning.png',
2673
                    get_lang('Wrong'),
2674
                    [],
2675
                    ICON_SIZE_MEDIUM
2676
                );
2677
            }
2678
            $html = Display::tag('h4', $html);
2679
            $html .= Display::tag(
2680
                'h5',
2681
                $icon,
2682
                ['style' => 'width:40px; padding:2px 10px 0px 0px']
2683
            );
2684
            $res = $html;
2685
        }
2686
        return $res;
2687
    }
2688
2689
    /**
2690
     * Return true if pass_pourcentage activated (we use the pass pourcentage feature
2691
     * return false if pass_percentage = 0 (we don't use the pass pourcentage feature
2692
     * @param $value
2693
     * @return boolean
2694
     * In this version, pass_percentage and show_success_message are disabled if
2695
     * pass_percentage is set to 0
2696
     */
2697
    public static function isPassPercentageEnabled($value)
2698
    {
2699
        return $value > 0;
2700
    }
2701
2702
    /**
2703
     * Converts a numeric value in a percentage example 0.66666 to 66.67 %
2704
     * @param $value
2705
     * @return float Converted number
2706
     */
2707
    public static function convert_to_percentage($value)
2708
    {
2709
        $return = '-';
2710
        if ($value != '') {
2711
            $return = float_format($value * 100, 1).' %';
2712
        }
2713
        return $return;
2714
    }
2715
2716
    /**
2717
     * Converts a score/weight values to the platform scale
2718
     * @param   float $score
2719
     * @param   float $weight
2720
     * @deprecated seem not to be used
2721
     * @return  float   the score rounded converted to the new range
2722
     */
2723
    public static function convert_score($score, $weight)
2724
    {
2725
        $maxNote = api_get_setting('exercise_max_score');
2726
        $minNote = api_get_setting('exercise_min_score');
2727
2728
        if ($score != '' && $weight != '') {
2729
            if ($maxNote != '' && $minNote != '') {
2730
                if (!empty($weight)) {
2731
                    $score = $minNote + ($maxNote - $minNote) * $score / $weight;
2732
                } else {
2733
                    $score = $minNote;
2734
                }
2735
            }
2736
        }
2737
        $score_rounded = float_format($score, 1);
2738
2739
        return $score_rounded;
2740
    }
2741
2742
    /**
2743
     * Getting all active exercises from a course from a session
2744
     * (if a session_id is provided we will show all the exercises in the course +
2745
     * all exercises in the session)
2746
     * @param   array $course_info
2747
     * @param   int $session_id
2748
     * @param   boolean $check_publication_dates
2749
     * @param   string $search Search exercise name
2750
     * @param   boolean $search_all_sessions Search exercises in all sessions
2751
     * @param   int 0 = only inactive exercises
2752
     *                  1 = only active exercises,
2753
     *                  2 = all exercises
2754
     *                  3 = active <> -1
2755
     * @return  array   array with exercise data
2756
     */
0 ignored issues
show
Documentation Bug introduced by
The doc comment 0 at position 0 could not be parsed: Unknown type name '0' at position 0 in 0.
Loading history...
2757
    public static function get_all_exercises(
2758
        $course_info = null,
2759
        $session_id = 0,
2760
        $check_publication_dates = false,
2761
        $search = '',
2762
        $search_all_sessions = false,
2763
        $active = 2
2764
    ) {
2765
        $course_id = api_get_course_int_id();
2766
2767
        if (!empty($course_info) && !empty($course_info['real_id'])) {
2768
            $course_id = $course_info['real_id'];
2769
        }
2770
2771
        if ($session_id == -1) {
2772
            $session_id = 0;
2773
        }
2774
2775
        $now = api_get_utc_datetime();
2776
        $time_conditions = '';
2777
2778
        if ($check_publication_dates) {
2779
            //start and end are set
2780
            $time_conditions = " AND ((start_time <> '' AND start_time < '$now' AND end_time <> '' AND end_time > '$now' )  OR ";
2781
            // only start is set
2782
            $time_conditions .= " (start_time <> '' AND start_time < '$now' AND end_time is NULL) OR ";
2783
            // only end is set
2784
            $time_conditions .= " (start_time IS NULL AND end_time <> '' AND end_time > '$now') OR ";
2785
            // nothing is set
2786
            $time_conditions .= " (start_time IS NULL AND end_time IS NULL))  ";
2787
        }
2788
2789
        $needle_where = !empty($search) ? " AND title LIKE '?' " : '';
2790
        $needle = !empty($search) ? "%".$search."%" : '';
2791
2792
        // Show courses by active status
2793
        $active_sql = '';
2794
        if ($active == 3) {
2795
            $active_sql = ' active <> -1 AND';
2796
        } else {
2797
            if ($active != 2) {
2798
                $active_sql = sprintf(' active = %d AND', $active);
2799
            }
2800
        }
2801
2802
        if ($search_all_sessions == true) {
2803
            $conditions = [
2804
                'where' => [
2805
                    $active_sql.' c_id = ? '.$needle_where.$time_conditions => [
2806
                        $course_id,
2807
                        $needle
2808
                    ]
2809
                ],
2810
                'order' => 'title'
2811
            ];
2812
        } else {
2813
            if (empty($session_id)) {
2814
                $conditions = [
2815
                    'where' => [
2816
                        $active_sql.' (session_id = 0 OR session_id IS NULL) AND c_id = ? '.$needle_where.$time_conditions => [
2817
                            $course_id,
2818
                            $needle
2819
                        ]
2820
                    ],
2821
                    'order' => 'title'
2822
                ];
2823
            } else {
2824
                $conditions = [
2825
                    'where' => [
2826
                        $active_sql.' (session_id = 0 OR session_id = ? ) AND c_id = ? '.$needle_where.$time_conditions => [
2827
                            $session_id,
2828
                            $course_id,
2829
                            $needle
2830
                        ]
2831
                    ],
2832
                    'order' => 'title'
2833
                ];
2834
            }
2835
        }
2836
2837
        $table = Database::get_course_table(TABLE_QUIZ_TEST);
2838
        return Database::select('*', $table, $conditions);
2839
    }
2840
2841
    /**
2842
     * Get exercise information by id
2843
     * @param int $exerciseId Exercise Id
2844
     * @param int $courseId The course ID (necessary as c_quiz.id is not unique)
2845
     * @return array Exercise info
2846
     */
2847
    public static function get_exercise_by_id($exerciseId = 0, $courseId = 0)
2848
    {
2849
        $table = Database::get_course_table(TABLE_QUIZ_TEST);
2850
        if (empty($courseId)) {
2851
            $courseId = api_get_course_int_id();
2852
        } else {
2853
            $courseId = intval($courseId);
2854
        }
2855
        $conditions = [
2856
            'where' => [
2857
                'id = ?' => [$exerciseId],
2858
                ' AND c_id = ? ' => $courseId
2859
            ]
2860
        ];
2861
2862
        return Database::select('*', $table, $conditions);
2863
    }
2864
2865
    /**
2866
     * Getting all exercises (active only or all)
2867
     * from a course from a session
2868
     * (if a session_id is provided we will show all the exercises in the
2869
     * course + all exercises in the session)
2870
     * @param   array   course data
2871
     * @param   int     session id
2872
     * @param    int        course c_id
2873
     * @param   boolean $only_active_exercises
2874
     * @return  array   array with exercise data
2875
     * modified by Hubert Borderiou
2876
     */
2877
    public static function get_all_exercises_for_course_id(
2878
        $course_info = null,
2879
        $session_id = 0,
2880
        $course_id = 0,
2881
        $only_active_exercises = true
2882
    ) {
2883
        $TBL_EXERCISES = Database::get_course_table(TABLE_QUIZ_TEST);
2884
2885
        if ($only_active_exercises) {
2886
            // Only active exercises.
2887
            $sql_active_exercises = "active = 1 AND ";
2888
        } else {
2889
            // Not only active means visible and invisible NOT deleted (-2)
2890
            $sql_active_exercises = "active IN (1, 0) AND ";
2891
        }
2892
2893
        if ($session_id == -1) {
2894
            $session_id = 0;
2895
        }
2896
2897
        $params = [
2898
            $session_id,
2899
            $course_id
2900
        ];
2901
2902
        if ($session_id == 0) {
2903
            $conditions = [
2904
                'where' => ["$sql_active_exercises session_id = ? AND c_id = ?" => $params],
2905
                'order' => 'title'
2906
            ];
2907
        } else {
2908
            // All exercises
2909
            $conditions = [
2910
                'where' => ["$sql_active_exercises (session_id = 0 OR session_id = ? ) AND c_id=?" => $params],
2911
                'order' => 'title'
2912
            ];
2913
        }
2914
2915
        return Database::select('*', $TBL_EXERCISES, $conditions);
2916
    }
2917
2918
    /**
2919
     * Gets the position of the score based in a given score (result/weight)
2920
     * and the exe_id based in the user list
2921
     * (NO Exercises in LPs )
2922
     * @param   float $my_score user score to be compared *attention*
2923
     * $my_score = score/weight and not just the score
2924
     * @param   int $my_exe_id exe id of the exercise
2925
     * (this is necessary because if 2 students have the same score the one
2926
     * with the minor exe_id will have a best position, just to be fair and FIFO)
2927
     * @param   int $exercise_id
2928
     * @param   string $course_code
2929
     * @param   int $session_id
2930
     * @param   array $user_list
2931
     * @param   bool $return_string
2932
     *
2933
     * @return  int     the position of the user between his friends in a course
2934
     * (or course within a session)
2935
     */
2936
    public static function get_exercise_result_ranking(
2937
        $my_score,
2938
        $my_exe_id,
2939
        $exercise_id,
2940
        $course_code,
2941
        $session_id = 0,
2942
        $user_list = [],
2943
        $return_string = true
2944
    ) {
2945
        //No score given we return
2946
        if (is_null($my_score)) {
2947
            return '-';
2948
        }
2949
        if (empty($user_list)) {
2950
            return '-';
2951
        }
2952
2953
        $best_attempts = [];
2954
        foreach ($user_list as $user_data) {
2955
            $user_id = $user_data['user_id'];
2956
            $best_attempts[$user_id] = self::get_best_attempt_by_user(
2957
                $user_id,
2958
                $exercise_id,
2959
                $course_code,
2960
                $session_id
2961
            );
2962
        }
2963
2964
        if (empty($best_attempts)) {
2965
            return 1;
2966
        } else {
2967
            $position = 1;
2968
            $my_ranking = [];
2969
            foreach ($best_attempts as $user_id => $result) {
2970
                if (!empty($result['exe_weighting']) && intval($result['exe_weighting']) != 0) {
2971
                    $my_ranking[$user_id] = $result['exe_result'] / $result['exe_weighting'];
2972
                } else {
2973
                    $my_ranking[$user_id] = 0;
2974
                }
2975
            }
2976
            //if (!empty($my_ranking)) {
2977
            asort($my_ranking);
2978
            $position = count($my_ranking);
2979
            if (!empty($my_ranking)) {
2980
                foreach ($my_ranking as $user_id => $ranking) {
2981
                    if ($my_score >= $ranking) {
2982
                        if ($my_score == $ranking && isset($best_attempts[$user_id]['exe_id'])) {
2983
                            $exe_id = $best_attempts[$user_id]['exe_id'];
2984
                            if ($my_exe_id < $exe_id) {
2985
                                $position--;
2986
                            }
2987
                        } else {
2988
                            $position--;
2989
                        }
2990
                    }
2991
                }
2992
            }
2993
            //}
2994
            $return_value = [
2995
                'position' => $position,
2996
                'count' => count($my_ranking)
2997
            ];
2998
2999
            if ($return_string) {
3000
                if (!empty($position) && !empty($my_ranking)) {
3001
                    $return_value = $position.'/'.count($my_ranking);
3002
                } else {
3003
                    $return_value = '-';
3004
                }
3005
            }
3006
            return $return_value;
3007
        }
3008
    }
3009
3010
    /**
3011
     * Gets the position of the score based in a given score (result/weight) and the exe_id based in all attempts
3012
     * (NO Exercises in LPs ) old functionality by attempt
3013
     * @param   float   user score to be compared attention => score/weight
3014
     * @param   int     exe id of the exercise
3015
     * (this is necessary because if 2 students have the same score the one
3016
     * with the minor exe_id will have a best position, just to be fair and FIFO)
3017
     * @param   int     exercise id
3018
     * @param   string  course code
3019
     * @param   int     session id
3020
     * @param bool $return_string
3021
     * @return  int     the position of the user between his friends in a course (or course within a session)
3022
     */
3023
    public static function get_exercise_result_ranking_by_attempt(
3024
        $my_score,
3025
        $my_exe_id,
3026
        $exercise_id,
3027
        $courseId,
3028
        $session_id = 0,
3029
        $return_string = true
3030
    ) {
3031
        if (empty($session_id)) {
3032
            $session_id = 0;
3033
        }
3034
        if (is_null($my_score)) {
3035
            return '-';
3036
        }
3037
        $user_results = Event::get_all_exercise_results(
3038
            $exercise_id,
3039
            $courseId,
3040
            $session_id,
3041
            false
3042
        );
3043
        $position_data = [];
3044
        if (empty($user_results)) {
3045
            return 1;
3046
        } else {
3047
            $position = 1;
3048
            $my_ranking = [];
3049
            foreach ($user_results as $result) {
3050
                if (!empty($result['exe_weighting']) && intval($result['exe_weighting']) != 0) {
3051
                    $my_ranking[$result['exe_id']] = $result['exe_result'] / $result['exe_weighting'];
3052
                } else {
3053
                    $my_ranking[$result['exe_id']] = 0;
3054
                }
3055
            }
3056
            asort($my_ranking);
3057
            $position = count($my_ranking);
3058
            if (!empty($my_ranking)) {
3059
                foreach ($my_ranking as $exe_id => $ranking) {
3060
                    if ($my_score >= $ranking) {
3061
                        if ($my_score == $ranking) {
3062
                            if ($my_exe_id < $exe_id) {
3063
                                $position--;
3064
                            }
3065
                        } else {
3066
                            $position--;
3067
                        }
3068
                    }
3069
                }
3070
            }
3071
            $return_value = [
3072
                'position' => $position,
3073
                'count' => count($my_ranking)
3074
            ];
3075
3076
            if ($return_string) {
3077
                if (!empty($position) && !empty($my_ranking)) {
3078
                    return $position.'/'.count($my_ranking);
3079
                }
3080
            }
3081
            return $return_value;
3082
        }
3083
    }
3084
3085
    /**
3086
     * Get the best attempt in a exercise (NO Exercises in LPs )
3087
     * @param int $exercise_id
3088
     * @param int $courseId
3089
     * @param int $session_id
3090
     *
3091
     * @return array
3092
     */
3093
    public static function get_best_attempt_in_course($exercise_id, $courseId, $session_id)
3094
    {
3095
        $user_results = Event::get_all_exercise_results(
3096
            $exercise_id,
3097
            $courseId,
3098
            $session_id,
3099
            false
3100
        );
3101
3102
        $best_score_data = [];
3103
        $best_score = 0;
3104
        if (!empty($user_results)) {
3105
            foreach ($user_results as $result) {
3106
                if (!empty($result['exe_weighting']) &&
3107
                    intval($result['exe_weighting']) != 0
3108
                ) {
3109
                    $score = $result['exe_result'] / $result['exe_weighting'];
3110
                    if ($score >= $best_score) {
3111
                        $best_score = $score;
3112
                        $best_score_data = $result;
3113
                    }
3114
                }
3115
            }
3116
        }
3117
3118
        return $best_score_data;
3119
    }
3120
3121
    /**
3122
     * Get the best score in a exercise (NO Exercises in LPs )
3123
     * @param int $user_id
3124
     * @param int $exercise_id
3125
     * @param int $courseId
3126
     * @param int $session_id
3127
     *
3128
     * @return array
3129
     */
3130
    public static function get_best_attempt_by_user(
3131
        $user_id,
3132
        $exercise_id,
3133
        $courseId,
3134
        $session_id
3135
    ) {
3136
        $user_results = Event::get_all_exercise_results(
3137
            $exercise_id,
3138
            $courseId,
3139
            $session_id,
3140
            false,
3141
            $user_id
3142
        );
3143
        $best_score_data = [];
3144
        $best_score = 0;
3145
        if (!empty($user_results)) {
3146
            foreach ($user_results as $result) {
3147
                if (!empty($result['exe_weighting']) && intval($result['exe_weighting']) != 0) {
3148
                    $score = $result['exe_result'] / $result['exe_weighting'];
3149
                    if ($score >= $best_score) {
3150
                        $best_score = $score;
3151
                        $best_score_data = $result;
3152
                    }
3153
                }
3154
            }
3155
        }
3156
3157
        return $best_score_data;
3158
    }
3159
3160
    /**
3161
     * Get average score (NO Exercises in LPs )
3162
     * @param    int    exercise id
3163
     * @param    int $courseId
3164
     * @param    int    session id
3165
     * @return    float    Average score
3166
     */
3167
    public static function get_average_score($exercise_id, $courseId, $session_id)
3168
    {
3169
        $user_results = Event::get_all_exercise_results(
3170
            $exercise_id,
3171
            $courseId,
3172
            $session_id
3173
        );
3174
        $avg_score = 0;
3175
        if (!empty($user_results)) {
3176
            foreach ($user_results as $result) {
3177
                if (!empty($result['exe_weighting']) && intval($result['exe_weighting']) != 0) {
3178
                    $score = $result['exe_result'] / $result['exe_weighting'];
3179
                    $avg_score += $score;
3180
                }
3181
            }
3182
            $avg_score = float_format($avg_score / count($user_results), 1);
3183
        }
3184
3185
        return $avg_score;
3186
    }
3187
3188
    /**
3189
     * Get average score by score (NO Exercises in LPs )
3190
     * @param    int    $courseId
3191
     * @param    int    session id
3192
     * @return    float    Average score
3193
     */
3194
    public static function get_average_score_by_course($courseId, $session_id)
3195
    {
3196
        $user_results = Event::get_all_exercise_results_by_course(
3197
            $courseId,
3198
            $session_id,
3199
            false
3200
        );
3201
        $avg_score = 0;
3202
        if (!empty($user_results)) {
3203
            foreach ($user_results as $result) {
3204
                if (!empty($result['exe_weighting']) && intval(
3205
                        $result['exe_weighting']
3206
                    ) != 0
3207
                ) {
3208
                    $score = $result['exe_result'] / $result['exe_weighting'];
3209
                    $avg_score += $score;
3210
                }
3211
            }
3212
            // We assume that all exe_weighting
3213
            $avg_score = $avg_score / count($user_results);
3214
        }
3215
3216
        return $avg_score;
3217
    }
3218
3219
    /**
3220
     * @param int $user_id
3221
     * @param int $courseId
3222
     * @param int $session_id
3223
     *
3224
     * @return float|int
3225
     */
3226
    public static function get_average_score_by_course_by_user(
3227
        $user_id,
3228
        $courseId,
3229
        $session_id
3230
    ) {
3231
        $user_results = Event::get_all_exercise_results_by_user(
3232
            $user_id,
3233
            $courseId,
3234
            $session_id
3235
        );
3236
        $avg_score = 0;
3237
        if (!empty($user_results)) {
3238
            foreach ($user_results as $result) {
3239
                if (!empty($result['exe_weighting']) && intval($result['exe_weighting']) != 0) {
3240
                    $score = $result['exe_result'] / $result['exe_weighting'];
3241
                    $avg_score += $score;
3242
                }
3243
            }
3244
            // We assume that all exe_weighting
3245
            $avg_score = ($avg_score / count($user_results));
3246
        }
3247
3248
        return $avg_score;
3249
    }
3250
3251
    /**
3252
     * Get average score by score (NO Exercises in LPs )
3253
     * @param    int        exercise id
3254
     * @param    int $courseId
3255
     * @param    int        session id
3256
     * @return    float    Best average score
3257
     */
3258
    public static function get_best_average_score_by_exercise(
3259
        $exercise_id,
3260
        $courseId,
3261
        $session_id,
3262
        $user_count
3263
    ) {
3264
        $user_results = Event::get_best_exercise_results_by_user(
3265
            $exercise_id,
3266
            $courseId,
3267
            $session_id
3268
        );
3269
        $avg_score = 0;
3270
        if (!empty($user_results)) {
3271
            foreach ($user_results as $result) {
3272
                if (!empty($result['exe_weighting']) && intval($result['exe_weighting']) != 0) {
3273
                    $score = $result['exe_result'] / $result['exe_weighting'];
3274
                    $avg_score += $score;
3275
                }
3276
            }
3277
            //We asumme that all exe_weighting
3278
            //$avg_score = show_score( $avg_score / count($user_results) , $result['exe_weighting']);
3279
            //$avg_score = ($avg_score / count($user_results));
3280
            if (!empty($user_count)) {
3281
                $avg_score = float_format($avg_score / $user_count, 1) * 100;
3282
            } else {
3283
                $avg_score = 0;
3284
            }
3285
        }
3286
3287
        return $avg_score;
3288
    }
3289
3290
    /**
3291
     * Get average score by score (NO Exercises in LPs )
3292
     * @param    int        exercise id
3293
     * @param    int $courseId
3294
     * @param    int        session id
3295
     * @return    float    Best average score
3296
     */
3297
    public static function getBestScoreByExercise(
3298
        $exercise_id,
3299
        $courseId,
3300
        $session_id
3301
    ) {
3302
        $user_results = Event::get_best_exercise_results_by_user(
3303
            $exercise_id,
3304
            $courseId,
3305
            $session_id
3306
        );
3307
        $avg_score = 0;
3308
        if (!empty($user_results)) {
3309
            foreach ($user_results as $result) {
3310
                if (!empty($result['exe_weighting']) && intval($result['exe_weighting']) != 0) {
3311
                    $score = $result['exe_result'] / $result['exe_weighting'];
3312
                    $avg_score += $score;
3313
                }
3314
            }
3315
        }
3316
3317
        return $avg_score;
3318
    }
3319
3320
    /**
3321
     * @param string $course_code
3322
     * @param int $session_id
3323
     *
3324
     * @return array
3325
     */
3326
    public static function get_exercises_to_be_taken($course_code, $session_id)
3327
    {
3328
        $course_info = api_get_course_info($course_code);
3329
        $exercises = self::get_all_exercises($course_info, $session_id);
3330
        $result = [];
3331
        $now = time() + 15 * 24 * 60 * 60;
3332
        foreach ($exercises as $exercise_item) {
3333
            if (isset($exercise_item['end_time']) &&
3334
                !empty($exercise_item['end_time']) &&
3335
                api_strtotime($exercise_item['end_time'], 'UTC') < $now
3336
            ) {
3337
                $result[] = $exercise_item;
3338
            }
3339
        }
3340
        return $result;
3341
    }
3342
3343
    /**
3344
     * Get student results (only in completed exercises) stats by question
3345
     * @param    int $question_id
3346
     * @param    int $exercise_id
3347
     * @param    string $course_code
3348
     * @param    int $session_id
3349
     * @return array
3350
     *
3351
     **/
3352
    public static function get_student_stats_by_question(
3353
        $question_id,
3354
        $exercise_id,
3355
        $course_code,
3356
        $session_id
3357
    ) {
3358
        $track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3359
        $track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
3360
3361
        $question_id = intval($question_id);
3362
        $exercise_id = intval($exercise_id);
3363
        $course_code = Database::escape_string($course_code);
3364
        $session_id = intval($session_id);
3365
        $courseId = api_get_course_int_id($course_code);
3366
3367
        $sql = "SELECT MAX(marks) as max, MIN(marks) as min, AVG(marks) as average
3368
    		FROM $track_exercises e
3369
    		INNER JOIN $track_attempt a
3370
    		ON (
3371
    		    a.exe_id = e.exe_id AND
3372
    		    e.c_id = a.c_id AND
3373
    		    e.session_id  = a.session_id
3374
            )
3375
    		WHERE
3376
    		    exe_exo_id 	= $exercise_id AND
3377
                a.c_id = $courseId AND
3378
                e.session_id = $session_id AND
3379
                question_id = $question_id AND
3380
                status = ''
3381
            LIMIT 1";
3382
        $result = Database::query($sql);
3383
        $return = [];
3384
        if ($result) {
3385
            $return = Database::fetch_array($result, 'ASSOC');
3386
        }
3387
3388
        return $return;
3389
    }
3390
3391
    /**
3392
     * Get the correct answer count for a fill blanks question
3393
     *
3394
     * @param int $question_id
3395
     * @param int $exercise_id
3396
     * @return int
3397
     */
3398
    public static function getNumberStudentsFillBlanksAnswerCount(
3399
        $question_id,
3400
        $exercise_id
3401
    ) {
3402
        $listStudentsId = [];
3403
        $listAllStudentInfo = CourseManager::get_student_list_from_course_code(
3404
            api_get_course_id(),
3405
            true
3406
        );
3407
        foreach ($listAllStudentInfo as $i => $listStudentInfo) {
3408
            $listStudentsId[] = $listStudentInfo['user_id'];
3409
        }
3410
3411
        $listFillTheBlankResult = FillBlanks::getFillTheBlankTabResult(
3412
            $exercise_id,
3413
            $question_id,
3414
            $listStudentsId,
3415
            '1970-01-01',
3416
            '3000-01-01'
3417
        );
3418
3419
        $arrayCount = [];
3420
3421
        foreach ($listFillTheBlankResult as $resultCount) {
3422
            foreach ($resultCount as $index => $count) {
3423
                //this is only for declare the array index per answer
3424
                $arrayCount[$index] = 0;
3425
            }
3426
        }
3427
3428
        foreach ($listFillTheBlankResult as $resultCount) {
3429
            foreach ($resultCount as $index => $count) {
3430
                $count = ($count === 0) ? 1 : 0;
3431
                $arrayCount[$index] += $count;
3432
            }
3433
        }
3434
3435
        return $arrayCount;
3436
    }
3437
3438
    /**
3439
     * @param int $question_id
3440
     * @param int $exercise_id
3441
     * @param string $course_code
3442
     * @param int $session_id
3443
     * @param string $questionType
3444
     * @return int
3445
     */
3446
    public static function get_number_students_question_with_answer_count(
3447
        $question_id,
3448
        $exercise_id,
3449
        $course_code,
3450
        $session_id,
3451
        $questionType = ''
3452
    ) {
3453
        $track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3454
        $track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
3455
        $courseUser = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3456
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
3457
        $courseUserSession = Database::get_main_table(
3458
            TABLE_MAIN_SESSION_COURSE_USER
3459
        );
3460
3461
        $question_id = intval($question_id);
3462
        $exercise_id = intval($exercise_id);
3463
        $courseId = api_get_course_int_id($course_code);
3464
        $session_id = intval($session_id);
3465
3466
        if ($questionType == FILL_IN_BLANKS) {
3467
            $listStudentsId = [];
3468
            $listAllStudentInfo = CourseManager::get_student_list_from_course_code(
3469
                api_get_course_id(),
3470
                true
3471
            );
3472
            foreach ($listAllStudentInfo as $i => $listStudentInfo) {
3473
                $listStudentsId[] = $listStudentInfo['user_id'];
3474
            }
3475
3476
            $listFillTheBlankResult = FillBlanks::getFillTheBlankTabResult(
3477
                $exercise_id,
3478
                $question_id,
3479
                $listStudentsId,
3480
                '1970-01-01',
3481
                '3000-01-01'
3482
            );
3483
3484
            return FillBlanks::getNbResultFillBlankAll($listFillTheBlankResult);
3485
        }
3486
3487
        if (empty($session_id)) {
3488
            $courseCondition = "
3489
            INNER JOIN $courseUser cu
3490
            ON cu.c_id = c.id AND cu.user_id  = exe_user_id";
3491
            $courseConditionWhere = " AND relation_type <> 2 AND cu.status = ".STUDENT;
3492
        } else {
3493
            $courseCondition = "
3494
            INNER JOIN $courseUserSession cu
3495
            ON cu.c_id = c.id AND cu.user_id = exe_user_id";
3496
            $courseConditionWhere = " AND cu.status = 0 ";
3497
        }
3498
3499
        $sql = "SELECT DISTINCT exe_user_id
3500
    		FROM $track_exercises e
3501
    		INNER JOIN $track_attempt a
3502
    		ON (
3503
    		    a.exe_id = e.exe_id AND
3504
    		    e.c_id = a.c_id AND
3505
    		    e.session_id  = a.session_id
3506
            )
3507
            INNER JOIN $courseTable c
3508
            ON (c.id = a.c_id)
3509
    		$courseCondition
3510
    		WHERE
3511
    		    exe_exo_id = $exercise_id AND
3512
                a.c_id = $courseId AND
3513
                e.session_id = $session_id AND
3514
                question_id = $question_id AND
3515
                answer <> '0' AND
3516
                e.status = ''
3517
                $courseConditionWhere
3518
            ";
3519
        $result = Database::query($sql);
3520
        $return = 0;
3521
        if ($result) {
3522
            $return = Database::num_rows($result);
3523
        }
3524
        return $return;
3525
    }
3526
3527
    /**
3528
     * @param int $answer_id
3529
     * @param int $question_id
3530
     * @param int $exercise_id
3531
     * @param string $course_code
3532
     * @param int $session_id
3533
     *
3534
     * @return int
3535
     */
3536
    public static function get_number_students_answer_hotspot_count(
3537
        $answer_id,
3538
        $question_id,
3539
        $exercise_id,
3540
        $course_code,
3541
        $session_id
3542
    ) {
3543
        $track_exercises = Database::get_main_table(
3544
            TABLE_STATISTIC_TRACK_E_EXERCISES
3545
        );
3546
        $track_hotspot = Database::get_main_table(
3547
            TABLE_STATISTIC_TRACK_E_HOTSPOT
3548
        );
3549
        $courseUser = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3550
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
3551
3552
        $courseUserSession = Database::get_main_table(
3553
            TABLE_MAIN_SESSION_COURSE_USER
3554
        );
3555
3556
        $question_id = intval($question_id);
3557
        $answer_id = intval($answer_id);
3558
        $exercise_id = intval($exercise_id);
3559
        $course_code = Database::escape_string($course_code);
3560
        $session_id = intval($session_id);
3561
3562
        if (empty($session_id)) {
3563
            $courseCondition = "
3564
            INNER JOIN $courseUser cu
3565
            ON cu.c_id = c.id AND cu.user_id  = exe_user_id";
3566
            $courseConditionWhere = " AND relation_type <> 2 AND cu.status = ".STUDENT;
3567
        } else {
3568
            $courseCondition = "
3569
            INNER JOIN $courseUserSession cu
3570
            ON cu.c_id = c.id AND cu.user_id = exe_user_id";
3571
            $courseConditionWhere = " AND cu.status = 0 ";
3572
        }
3573
3574
        $sql = "SELECT DISTINCT exe_user_id
3575
    		FROM $track_exercises e
3576
    		INNER JOIN $track_hotspot a
3577
    		ON (a.hotspot_exe_id = e.exe_id)
3578
    		INNER JOIN $courseTable c
3579
    		ON (hotspot_course_code = c.code)
3580
    		$courseCondition
3581
    		WHERE
3582
    		    exe_exo_id              = $exercise_id AND
3583
                a.hotspot_course_code 	= '$course_code' AND
3584
                e.session_id            = $session_id AND
3585
                hotspot_answer_id       = $answer_id AND
3586
                hotspot_question_id     = $question_id AND
3587
                hotspot_correct         =  1 AND
3588
                e.status                = ''
3589
                $courseConditionWhere
3590
            ";
3591
3592
        $result = Database::query($sql);
3593
        $return = 0;
3594
        if ($result) {
3595
            $return = Database::num_rows($result);
3596
        }
3597
        return $return;
3598
    }
3599
3600
    /**
3601
     * @param int $answer_id
3602
     * @param int $question_id
3603
     * @param int $exercise_id
3604
     * @param string $course_code
3605
     * @param int $session_id
3606
     * @param string $question_type
3607
     * @param string $correct_answer
3608
     * @param string $current_answer
3609
     * @return int
3610
     */
3611
    public static function get_number_students_answer_count(
3612
        $answer_id,
3613
        $question_id,
3614
        $exercise_id,
3615
        $course_code,
3616
        $session_id,
3617
        $question_type = null,
3618
        $correct_answer = null,
3619
        $current_answer = null
3620
    ) {
3621
        $track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3622
        $track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
3623
        $courseTable = Database::get_main_table(TABLE_MAIN_COURSE);
3624
        $courseUser = Database::get_main_table(TABLE_MAIN_COURSE_USER);
3625
        $courseUserSession = Database::get_main_table(
3626
            TABLE_MAIN_SESSION_COURSE_USER
3627
        );
3628
3629
        $question_id = intval($question_id);
3630
        $answer_id = intval($answer_id);
3631
        $exercise_id = intval($exercise_id);
3632
        $courseId = api_get_course_int_id($course_code);
3633
        $course_code = Database::escape_string($course_code);
3634
        $session_id = intval($session_id);
3635
3636
        switch ($question_type) {
3637
            case FILL_IN_BLANKS:
3638
                $answer_condition = "";
3639
                $select_condition = " e.exe_id, answer ";
3640
                break;
3641
            case MATCHING:
3642
            case MATCHING_DRAGGABLE:
3643
            default:
3644
                $answer_condition = " answer = $answer_id AND ";
3645
                $select_condition = " DISTINCT exe_user_id ";
3646
        }
3647
3648
        if (empty($session_id)) {
3649
            $courseCondition = "
3650
            INNER JOIN $courseUser cu
3651
            ON cu.c_id = c.id AND cu.user_id  = exe_user_id";
3652
            $courseConditionWhere = " AND relation_type <> 2 AND cu.status = ".STUDENT;
3653
        } else {
3654
            $courseCondition = "
3655
            INNER JOIN $courseUserSession cu
3656
            ON cu.c_id = a.c_id AND cu.user_id = exe_user_id";
3657
            $courseConditionWhere = " AND cu.status = 0 ";
3658
        }
3659
3660
        $sql = "SELECT $select_condition
3661
    		FROM $track_exercises e
3662
    		INNER JOIN $track_attempt a
3663
    		ON (
3664
    		    a.exe_id = e.exe_id AND
3665
    		    e.c_id = a.c_id AND
3666
    		    e.session_id  = a.session_id
3667
            )
3668
            INNER JOIN $courseTable c
3669
            ON c.id = a.c_id
3670
    		$courseCondition
3671
    		WHERE
3672
    		    exe_exo_id = $exercise_id AND
3673
                a.c_id = $courseId AND
3674
                e.session_id = $session_id AND
3675
                $answer_condition
3676
                question_id = $question_id AND
3677
                e.status = ''
3678
                $courseConditionWhere
3679
            ";
3680
        $result = Database::query($sql);
3681
        $return = 0;
3682
        if ($result) {
3683
            $good_answers = 0;
3684
            switch ($question_type) {
3685
                case FILL_IN_BLANKS:
3686
                    while ($row = Database::fetch_array($result, 'ASSOC')) {
3687
                        $fill_blank = self::check_fill_in_blanks(
3688
                            $correct_answer,
3689
                            $row['answer'],
3690
                            $current_answer
3691
                        );
3692
                        if (isset($fill_blank[$current_answer]) && $fill_blank[$current_answer] == 1) {
3693
                            $good_answers++;
3694
                        }
3695
                    }
3696
                    return $good_answers;
3697
                    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...
3698
                case MATCHING:
3699
                case MATCHING_DRAGGABLE:
3700
                default:
3701
                    $return = Database::num_rows($result);
3702
            }
3703
        }
3704
3705
        return $return;
3706
    }
3707
3708
    /**
3709
     * @param array $answer
3710
     * @param string $user_answer
3711
     * @return array
3712
     */
3713
    public static function check_fill_in_blanks($answer, $user_answer, $current_answer)
3714
    {
3715
        // the question is encoded like this
3716
        // [A] B [C] D [E] F::10,10,10@1
3717
        // number 1 before the "@" means that is a switchable fill in blank question
3718
        // [A] B [C] D [E] F::10,10,10@ or  [A] B [C] D [E] F::10,10,10
3719
        // means that is a normal fill blank question
3720
        // first we explode the "::"
3721
        $pre_array = explode('::', $answer);
3722
        // is switchable fill blank or not
3723
        $last = count($pre_array) - 1;
3724
        $is_set_switchable = explode('@', $pre_array[$last]);
3725
        $switchable_answer_set = false;
3726
        if (isset($is_set_switchable[1]) && $is_set_switchable[1] == 1) {
3727
            $switchable_answer_set = true;
3728
        }
3729
        $answer = '';
3730
        for ($k = 0; $k < $last; $k++) {
3731
            $answer .= $pre_array[$k];
3732
        }
3733
        // splits weightings that are joined with a comma
3734
        $answerWeighting = explode(',', $is_set_switchable[0]);
3735
3736
        // we save the answer because it will be modified
3737
        //$temp = $answer;
3738
        $temp = $answer;
3739
3740
        $answer = '';
3741
        $j = 0;
3742
        //initialise answer tags
3743
        $user_tags = $correct_tags = $real_text = [];
3744
        // the loop will stop at the end of the text
3745
        while (1) {
3746
            // quits the loop if there are no more blanks (detect '[')
3747
            if (($pos = api_strpos($temp, '[')) === false) {
3748
                // adds the end of the text
3749
                $answer = $temp;
3750
                $real_text[] = $answer;
3751
                break; //no more "blanks", quit the loop
3752
            }
3753
            // adds the piece of text that is before the blank
3754
            //and ends with '[' into a general storage array
3755
            $real_text[] = api_substr($temp, 0, $pos + 1);
3756
            $answer .= api_substr($temp, 0, $pos + 1);
3757
            //take the string remaining (after the last "[" we found)
3758
            $temp = api_substr($temp, $pos + 1);
3759
            // quit the loop if there are no more blanks, and update $pos to the position of next ']'
3760
            if (($pos = api_strpos($temp, ']')) === false) {
3761
                // adds the end of the text
3762
                $answer .= $temp;
3763
                break;
3764
            }
3765
3766
            $str = $user_answer;
3767
3768
            preg_match_all('#\[([^[]*)\]#', $str, $arr);
3769
            $str = str_replace('\r\n', '', $str);
3770
            $choices = $arr[1];
3771
            $choice = [];
3772
            $check = false;
3773
            $i = 0;
3774
            foreach ($choices as $item) {
3775
                if ($current_answer === $item) {
3776
                    $check = true;
3777
                }
3778
                if ($check) {
3779
                    $choice[] = $item;
3780
                    $i++;
3781
                }
3782
                if ($i == 3) {
3783
                    break;
3784
                }
3785
            }
3786
            $tmp = api_strrpos($choice[$j], ' / ');
3787
3788
            if ($tmp !== false) {
3789
                $choice[$j] = api_substr($choice[$j], 0, $tmp);
3790
            }
3791
3792
            $choice[$j] = trim($choice[$j]);
3793
3794
            //Needed to let characters ' and " to work as part of an answer
3795
            $choice[$j] = stripslashes($choice[$j]);
3796
3797
            $user_tags[] = api_strtolower($choice[$j]);
3798
            //put the contents of the [] answer tag into correct_tags[]
3799
            $correct_tags[] = api_strtolower(api_substr($temp, 0, $pos));
3800
            $j++;
3801
            $temp = api_substr($temp, $pos + 1);
3802
        }
3803
3804
        $answer = '';
3805
        $real_correct_tags = $correct_tags;
3806
        $chosen_list = [];
3807
        $good_answer = [];
3808
3809
        for ($i = 0; $i < count($real_correct_tags); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
3810
            if (!$switchable_answer_set) {
3811
                //needed to parse ' and " characters
3812
                $user_tags[$i] = stripslashes($user_tags[$i]);
3813
                if ($correct_tags[$i] == $user_tags[$i]) {
3814
                    $good_answer[$correct_tags[$i]] = 1;
3815
                } elseif (!empty($user_tags[$i])) {
3816
                    $good_answer[$correct_tags[$i]] = 0;
3817
                } else {
3818
                    $good_answer[$correct_tags[$i]] = 0;
3819
                }
3820
            } else {
3821
                // switchable fill in the blanks
3822
                if (in_array($user_tags[$i], $correct_tags)) {
3823
                    $correct_tags = array_diff($correct_tags, $chosen_list);
3824
                    $good_answer[$correct_tags[$i]] = 1;
3825
                } elseif (!empty($user_tags[$i])) {
3826
                    $good_answer[$correct_tags[$i]] = 0;
3827
                } else {
3828
                    $good_answer[$correct_tags[$i]] = 0;
3829
                }
3830
            }
3831
            // adds the correct word, followed by ] to close the blank
3832
            $answer .= ' / <font color="green"><b>'.$real_correct_tags[$i].'</b></font>]';
3833
            if (isset($real_text[$i + 1])) {
3834
                $answer .= $real_text[$i + 1];
3835
            }
3836
        }
3837
3838
        return $good_answer;
3839
    }
3840
3841
    /**
3842
     * @param int $exercise_id
3843
     * @param string $course_code
3844
     * @param int $session_id
3845
     * @return int
3846
     */
3847
    public static function get_number_students_finish_exercise(
3848
        $exercise_id,
3849
        $course_code,
3850
        $session_id
3851
    ) {
3852
        $track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
3853
        $track_attempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
3854
3855
        $exercise_id = intval($exercise_id);
3856
        $course_code = Database::escape_string($course_code);
3857
        $session_id = intval($session_id);
3858
3859
        $sql = "SELECT DISTINCT exe_user_id
3860
                FROM $track_exercises e
3861
                INNER JOIN $track_attempt a
3862
                ON (a.exe_id = e.exe_id)
3863
                WHERE
3864
                    exe_exo_id 	 = $exercise_id AND
3865
                    course_code  = '$course_code' AND
3866
                    e.session_id = $session_id AND
3867
                    status = ''";
3868
        $result = Database::query($sql);
3869
        $return = 0;
3870
        if ($result) {
3871
            $return = Database::num_rows($result);
3872
        }
3873
        return $return;
3874
    }
3875
3876
    /**
3877
     * @param string $in_name is the name and the id of the <select>
3878
     * @param string $in_default default value for option
3879
     * @param string $in_onchange
3880
     * @return string the html code of the <select>
3881
     */
3882
    public static function displayGroupMenu($in_name, $in_default, $in_onchange = "")
3883
    {
3884
        // check the default value of option
3885
        $tabSelected = [$in_default => " selected='selected' "];
3886
        $res = "";
3887
        $res .= "<select name='$in_name' id='$in_name' onchange='".$in_onchange."' >";
3888
        $res .= "<option value='-1'".$tabSelected["-1"].">-- ".get_lang(
3889
                'AllGroups'
3890
            )." --</option>";
3891
        $res .= "<option value='0'".$tabSelected["0"].">- ".get_lang(
3892
                'NotInAGroup'
3893
            )." -</option>";
3894
        $tabGroups = GroupManager::get_group_list();
3895
        $currentCatId = 0;
3896
        for ($i = 0; $i < count($tabGroups); $i++) {
0 ignored issues
show
Performance Best Practice introduced by
It seems like you are calling the size function count() as part of the test condition. You might want to compute the size beforehand, and not on each iteration.

If the size of the collection does not change during the iteration, it is generally a good practice to compute it beforehand, and not on each iteration:

for ($i=0; $i<count($array); $i++) { // calls count() on each iteration
}

// Better
for ($i=0, $c=count($array); $i<$c; $i++) { // calls count() just once
}
Loading history...
3897
            $tabCategory = GroupManager::get_category_from_group(
3898
                $tabGroups[$i]['iid']
3899
            );
3900
            if ($tabCategory["id"] != $currentCatId) {
3901
                $res .= "<option value='-1' disabled='disabled'>".$tabCategory["title"]."</option>";
3902
                $currentCatId = $tabCategory["id"];
3903
            }
3904
            $res .= "<option ".$tabSelected[$tabGroups[$i]["id"]]."style='margin-left:40px' value='".$tabGroups[$i]["id"]."'>".
3905
                    $tabGroups[$i]["name"]."</option>";
3906
        }
3907
        $res .= "</select>";
3908
        return $res;
3909
    }
3910
3911
    /**
3912
     * @param int $exe_id
3913
     */
3914
    public static function create_chat_exercise_session($exe_id)
3915
    {
3916
        if (!isset($_SESSION['current_exercises'])) {
3917
            $_SESSION['current_exercises'] = [];
3918
        }
3919
        $_SESSION['current_exercises'][$exe_id] = true;
3920
    }
3921
3922
    /**
3923
     * @param int $exe_id
3924
     */
3925
    public static function delete_chat_exercise_session($exe_id)
3926
    {
3927
        if (isset($_SESSION['current_exercises'])) {
3928
            $_SESSION['current_exercises'][$exe_id] = false;
3929
        }
3930
    }
3931
3932
    /**
3933
     * Display the exercise results
3934
     * @param Exercise $objExercise
3935
     * @param int $exeId
3936
     * @param bool $save_user_result save users results (true) or just show the results (false)
3937
     * @param string $remainingMessage
3938
     */
3939
    public static function displayQuestionListByAttempt(
3940
        $objExercise,
3941
        $exeId,
3942
        $save_user_result = false,
3943
        $remainingMessage = ''
3944
    ) {
3945
        $origin = api_get_origin();
3946
3947
        // Getting attempt info
3948
        $exercise_stat_info = $objExercise->get_stat_track_exercise_info_by_exe_id($exeId);
3949
3950
        // Getting question list
3951
        $question_list = [];
3952
        if (!empty($exercise_stat_info['data_tracking'])) {
3953
            $question_list = explode(',', $exercise_stat_info['data_tracking']);
3954
        } else {
3955
            // Try getting the question list only if save result is off
3956
            if ($save_user_result == false) {
3957
                $question_list = $objExercise->get_validated_question_list();
3958
            }
3959
            if ($objExercise->selectFeedbackType() == EXERCISE_FEEDBACK_TYPE_DIRECT) {
3960
                $question_list = $objExercise->get_validated_question_list();
3961
            }
3962
        }
3963
3964
        $counter = 1;
3965
        $total_score = $total_weight = 0;
3966
        $exercise_content = null;
3967
3968
        // Hide results
3969
        $show_results = false;
3970
        $show_only_score = false;
3971
3972
        if ($objExercise->results_disabled == RESULT_DISABLE_SHOW_SCORE_AND_EXPECTED_ANSWERS) {
3973
            $show_results = true;
3974
        }
3975
3976
        if (in_array(
3977
            $objExercise->results_disabled,
3978
            [
3979
                RESULT_DISABLE_SHOW_SCORE_ONLY,
3980
                RESULT_DISABLE_SHOW_FINAL_SCORE_ONLY_WITH_CATEGORIES
3981
            ]
3982
        )
3983
        ) {
3984
            $show_only_score = true;
3985
        }
3986
3987
        // Not display expected answer, but score, and feedback
3988
        $show_all_but_expected_answer = false;
3989
        if ($objExercise->results_disabled == RESULT_DISABLE_SHOW_SCORE_ONLY &&
3990
            $objExercise->feedback_type == EXERCISE_FEEDBACK_TYPE_END
3991
        ) {
3992
            $show_all_but_expected_answer = true;
3993
            $show_results = true;
3994
            $show_only_score = false;
3995
        }
3996
3997
        $showTotalScoreAndUserChoicesInLastAttempt = true;
3998
3999
        if ($objExercise->results_disabled == RESULT_DISABLE_SHOW_SCORE_ATTEMPT_SHOW_ANSWERS_LAST_ATTEMPT) {
4000
            $show_only_score = true;
4001
            $show_results = true;
4002
            if ($objExercise->attempts > 0) {
4003
                $attempts = Event::getExerciseResultsByUser(
4004
                    api_get_user_id(),
4005
                    $objExercise->id,
4006
                    api_get_course_int_id(),
4007
                    api_get_session_id(),
4008
                    $exercise_stat_info['orig_lp_id'],
4009
                    $exercise_stat_info['orig_lp_item_id'],
4010
                    'desc'
4011
                );
4012
4013
                if ($attempts) {
4014
                    $numberAttempts = count($attempts);
4015
                } else {
4016
                    $numberAttempts = 0;
4017
                }
4018
4019
                if ($save_user_result) {
4020
                    $numberAttempts++;
4021
                }
4022
                if ($numberAttempts >= $objExercise->attempts) {
4023
                    $show_results = true;
4024
                    $show_only_score = false;
4025
                    $showTotalScoreAndUserChoicesInLastAttempt = true;
4026
                } else {
4027
                    $showTotalScoreAndUserChoicesInLastAttempt = false;
4028
                }
4029
            }
4030
        }
4031
4032
        if ($show_results || $show_only_score) {
4033
            if (isset($exercise_stat_info['exe_user_id'])) {
4034
                $user_info = api_get_user_info($exercise_stat_info['exe_user_id']);
4035
                if ($user_info) {
4036
                    // Shows exercise header
4037
                    echo $objExercise->show_exercise_result_header(
4038
                        $user_info,
4039
                        api_convert_and_format_date(
4040
                            $exercise_stat_info['start_date'],
4041
                            DATE_TIME_FORMAT_LONG
4042
                        ),
4043
                        $exercise_stat_info['duration'],
4044
                        $exercise_stat_info['user_ip']
4045
                    );
4046
                }
4047
            }
4048
        }
4049
4050
        // Display text when test is finished #4074 and for LP #4227
4051
        $end_of_message = $objExercise->selectTextWhenFinished();
4052
        if (!empty($end_of_message)) {
4053
            echo Display::return_message($end_of_message, 'normal', false);
4054
            echo "<div class='clear'>&nbsp;</div>";
4055
        }
4056
4057
        $question_list_answers = [];
4058
        $media_list = [];
4059
        $category_list = [];
4060
        $loadChoiceFromSession = false;
4061
        $fromDatabase = true;
4062
        $exerciseResult = null;
4063
        $exerciseResultCoordinates = null;
4064
        $delineationResults = null;
4065
4066
        if ($objExercise->selectFeedbackType() == EXERCISE_FEEDBACK_TYPE_DIRECT) {
4067
            $loadChoiceFromSession = true;
4068
            $fromDatabase = false;
4069
            $exerciseResult = Session::read('exerciseResult');
4070
            $exerciseResultCoordinates = Session::read('exerciseResultCoordinates');
4071
            $delineationResults = Session::read('hotspot_delineation_result');
4072
            $delineationResults = isset($delineationResults[$objExercise->id]) ? $delineationResults[$objExercise->id] : null;
4073
        }
4074
4075
        $countPendingQuestions = 0;
4076
        // Loop over all question to show results for each of them, one by one
4077
        if (!empty($question_list)) {
4078
            foreach ($question_list as $questionId) {
4079
                // Creates a temporary Question object
4080
                $objQuestionTmp = Question::read($questionId);
4081
                // This variable came from exercise_submit_modal.php
4082
                ob_start();
4083
                $choice = null;
4084
                $delineationChoice = null;
4085
                if ($loadChoiceFromSession) {
4086
                    $choice = isset($exerciseResult[$questionId]) ? $exerciseResult[$questionId] : null;
4087
                    $delineationChoice = isset($delineationResults[$questionId]) ? $delineationResults[$questionId] : null;
4088
                }
4089
4090
                // We're inside *one* question. Go through each possible answer for this question
4091
                $result = $objExercise->manage_answer(
4092
                    $exeId,
4093
                    $questionId,
4094
                    $choice,
4095
                    'exercise_result',
4096
                    $exerciseResultCoordinates,
4097
                    $save_user_result,
4098
                    $fromDatabase,
4099
                    $show_results,
4100
                    $objExercise->selectPropagateNeg(),
4101
                    $delineationChoice,
4102
                    $showTotalScoreAndUserChoicesInLastAttempt
4103
                );
4104
4105
                if (empty($result)) {
4106
                    continue;
4107
                }
4108
4109
                $total_score += $result['score'];
4110
                $total_weight += $result['weight'];
4111
4112
                $question_list_answers[] = [
4113
                    'question' => $result['open_question'],
4114
                    'answer' => $result['open_answer'],
4115
                    'answer_type' => $result['answer_type']
4116
                ];
4117
4118
                $my_total_score = $result['score'];
4119
                $my_total_weight = $result['weight'];
4120
4121
                // Category report
4122
                $category_was_added_for_this_test = false;
4123
                if (isset($objQuestionTmp->category) && !empty($objQuestionTmp->category)) {
4124
                    if (!isset($category_list[$objQuestionTmp->category]['score'])) {
4125
                        $category_list[$objQuestionTmp->category]['score'] = 0;
4126
                    }
4127
                    if (!isset($category_list[$objQuestionTmp->category]['total'])) {
4128
                        $category_list[$objQuestionTmp->category]['total'] = 0;
4129
                    }
4130
                    $category_list[$objQuestionTmp->category]['score'] += $my_total_score;
4131
                    $category_list[$objQuestionTmp->category]['total'] += $my_total_weight;
4132
                    $category_was_added_for_this_test = true;
4133
                }
4134
4135
                if (isset($objQuestionTmp->category_list) && !empty($objQuestionTmp->category_list)) {
4136
                    foreach ($objQuestionTmp->category_list as $category_id) {
4137
                        $category_list[$category_id]['score'] += $my_total_score;
4138
                        $category_list[$category_id]['total'] += $my_total_weight;
4139
                        $category_was_added_for_this_test = true;
4140
                    }
4141
                }
4142
4143
                // No category for this question!
4144
                if ($category_was_added_for_this_test == false) {
4145
                    if (!isset($category_list['none']['score'])) {
4146
                        $category_list['none']['score'] = 0;
4147
                    }
4148
                    if (!isset($category_list['none']['total'])) {
4149
                        $category_list['none']['total'] = 0;
4150
                    }
4151
4152
                    $category_list['none']['score'] += $my_total_score;
4153
                    $category_list['none']['total'] += $my_total_weight;
4154
                }
4155
4156
                if ($objExercise->selectPropagateNeg() == 0 && $my_total_score < 0) {
4157
                    $my_total_score = 0;
4158
                }
4159
4160
                $comnt = null;
4161
                if ($show_results) {
4162
                    $comnt = Event::get_comments($exeId, $questionId);
4163
4164
                    $teacherAudio = ExerciseLib::getOralFeedbackAudio(
4165
                        $exeId,
4166
                        $questionId,
4167
                        api_get_user_id()
4168
                    );
4169
4170
                    if (!empty($comnt) || $teacherAudio) {
4171
                        echo '<b>'.get_lang('Feedback').'</b>';
4172
                    }
4173
4174
                    if (!empty($comnt)) {
4175
                        echo ExerciseLib::getFeedbackText($comnt);
4176
                    }
4177
4178
                    if ($teacherAudio) {
4179
                        echo $teacherAudio;
4180
                    }
4181
                }
4182
4183
                $score = [];
4184
                if ($show_results) {
4185
                    $score = [
4186
                        'result' => self::show_score(
4187
                            $my_total_score,
4188
                            $my_total_weight,
4189
                            false,
4190
                            true
4191
                        ),
4192
                        'pass' => $my_total_score >= $my_total_weight ? true : false,
4193
                        'score' => $my_total_score,
4194
                        'weight' => $my_total_weight,
4195
                        'comments' => $comnt
4196
                    ];
4197
                }
4198
4199
                if (in_array($objQuestionTmp->type, [FREE_ANSWER, ORAL_EXPRESSION, ANNOTATION])) {
4200
                    $check = $objQuestionTmp->isQuestionWaitingReview($score);
4201
                    if ($check === false) {
4202
                        $countPendingQuestions++;
4203
                    }
4204
                }
4205
4206
                $contents = ob_get_clean();
4207
                $question_content = '';
4208
                if ($show_results) {
4209
                    $question_content = '<div class="question_row_answer">';
4210
                    // Shows question title an description
4211
                    $question_content .= $objQuestionTmp->return_header(
4212
                        $objExercise,
4213
                        $counter,
4214
                        $score
4215
                    );
4216
                }
4217
                $counter++;
4218
                $question_content .= $contents;
4219
                if ($show_results) {
4220
                    $question_content .= '</div>';
4221
                }
4222
                if (!$show_only_score) {
4223
                    $exercise_content .= Display::div(
4224
                        Display::panel($question_content),
4225
                        ['class' => 'question-panel']
4226
                    );
4227
                }
4228
            } // end foreach() block that loops over all questions
4229
        }
4230
4231
        $total_score_text = null;
4232
        if ($show_results || $show_only_score) {
4233
            $total_score_text .= '<div class="question_row_score">';
4234
            $total_score_text .= self::getTotalScoreRibbon(
4235
                $objExercise,
4236
                $total_score,
4237
                $total_weight,
4238
                true,
4239
                $countPendingQuestions
4240
            );
4241
            $total_score_text .= '</div>';
4242
        }
4243
4244
        if (!empty($category_list) && ($show_results || $show_only_score)) {
4245
            // Adding total
4246
            $category_list['total'] = [
4247
                'score' => $total_score,
4248
                'total' => $total_weight
4249
            ];
4250
            echo TestCategory::get_stats_table_by_attempt(
4251
                $objExercise->id,
4252
                $category_list
4253
            );
4254
        }
4255
4256
        if ($show_all_but_expected_answer) {
4257
            $exercise_content .= "<div class='normal-message'>".get_lang('ExerciseWithFeedbackWithoutCorrectionComment')."</div>";
4258
        }
4259
4260
        // Remove audio auto play from questions on results page - refs BT#7939
4261
        $exercise_content = preg_replace(
4262
            ['/autoplay[\=\".+\"]+/', '/autostart[\=\".+\"]+/'],
4263
            '',
4264
            $exercise_content
4265
        );
4266
4267
        echo $total_score_text;
4268
        echo $exercise_content;
4269
4270
        if (!$show_only_score) {
4271
            echo $total_score_text;
4272
        }
4273
4274
        if (!empty($remainingMessage)) {
4275
            echo Display::return_message($remainingMessage, 'normal', false);
4276
        }
4277
4278
        if ($save_user_result) {
4279
            // Tracking of results
4280
            if ($exercise_stat_info) {
4281
                $learnpath_id = $exercise_stat_info['orig_lp_id'];
4282
                $learnpath_item_id = $exercise_stat_info['orig_lp_item_id'];
4283
                $learnpath_item_view_id = $exercise_stat_info['orig_lp_item_view_id'];
4284
4285
                if (api_is_allowed_to_session_edit()) {
4286
                    Event::updateEventExercise(
4287
                        $exercise_stat_info['exe_id'],
4288
                        $objExercise->selectId(),
4289
                        $total_score,
4290
                        $total_weight,
4291
                        api_get_session_id(),
4292
                        $learnpath_id,
4293
                        $learnpath_item_id,
4294
                        $learnpath_item_view_id,
4295
                        $exercise_stat_info['exe_duration'],
4296
                        $question_list,
4297
                        '',
4298
                        []
4299
                    );
4300
                }
4301
            }
4302
4303
            // Send notification at the end
4304
            if (!api_is_allowed_to_edit(null, true) &&
4305
                !api_is_excluded_user_type()
4306
            ) {
4307
                $objExercise->send_mail_notification_for_exam(
4308
                    'end',
4309
                    $question_list_answers,
4310
                    $origin,
4311
                    $exeId,
4312
                    $total_score,
4313
                    $total_weight
4314
                );
4315
            }
4316
        }
4317
    }
4318
4319
    /**
4320
     * @param string $class
4321
     * @param string $scoreLabel
4322
     * @param string $result
4323
     *
4324
     * @return string
4325
     */
4326
    public static function getQuestionRibbon($class, $scoreLabel, $result)
4327
    {
4328
        return '<div class="ribbon">
4329
                    <div class="rib rib-'.$class.'">
4330
                        <h3>'.$scoreLabel.'</h3>
4331
                    </div>
4332
                    <h4>'.get_lang('Score').': '.$result.'</h4>
4333
                </div>'
4334
        ;
4335
    }
4336
4337
    /**
4338
     * @param Exercise $objExercise
4339
     * @param float $score
4340
     * @param float $weight
4341
     * @param bool $checkPassPercentage
4342
     * @param int $countPendingQuestions
4343
     * @return string
4344
     */
4345
    public static function getTotalScoreRibbon(
4346
        $objExercise,
4347
        $score,
4348
        $weight,
4349
        $checkPassPercentage = false,
4350
        $countPendingQuestions = 0
4351
    ) {
4352
        $passPercentage = $objExercise->selectPassPercentage();
4353
        $ribbon = '<div class="title-score">';
4354
        if ($checkPassPercentage) {
4355
            $isSuccess = self::isSuccessExerciseResult(
4356
                $score,
4357
                $weight,
4358
                $passPercentage
4359
            );
4360
            // Color the final test score if pass_percentage activated
4361
            $class = '';
4362
            if (self::isPassPercentageEnabled($passPercentage)) {
4363
                if ($isSuccess) {
4364
                    $class = ' ribbon-total-success';
4365
                } else {
4366
                    $class = ' ribbon-total-error';
4367
                }
4368
            }
4369
            $ribbon .= '<div class="total '.$class.'">';
4370
        } else {
4371
            $ribbon .= '<div class="total">';
4372
        }
4373
        $ribbon .= '<h3>'.get_lang('YourTotalScore').":&nbsp;";
4374
        $ribbon .= self::show_score($score, $weight, false, true);
4375
        $ribbon .= '</h3>';
4376
        $ribbon .= '</div>';
4377
        if ($checkPassPercentage) {
4378
            $ribbon .= self::showSuccessMessage(
4379
                $score,
4380
                $weight,
4381
                $passPercentage
4382
            );
4383
        }
4384
        $ribbon .= '</div>';
4385
4386
        if (!empty($countPendingQuestions)) {
4387
            $ribbon .= '<br />';
4388
            $ribbon .= Display::return_message(
4389
                sprintf(
4390
                    get_lang('TempScoreXQuestionsNotCorrectedYet'),
4391
                    $countPendingQuestions
4392
                ),
4393
                'warning'
4394
            );
4395
        }
4396
4397
        return $ribbon;
4398
    }
4399
4400
    /**
4401
     * @param int $countLetter
4402
     * @return mixed
4403
     */
4404
    public static function detectInputAppropriateClass($countLetter)
4405
    {
4406
        $limits = [
4407
            0 => 'input-mini',
4408
            10 => 'input-mini',
4409
            15 => 'input-medium',
4410
            20 => 'input-xlarge',
4411
            40 => 'input-xlarge',
4412
            60 => 'input-xxlarge',
4413
            100 => 'input-xxlarge',
4414
            200 => 'input-xxlarge',
4415
        ];
4416
4417
        foreach ($limits as $size => $item) {
4418
            if ($countLetter <= $size) {
4419
                return $item;
4420
            }
4421
        }
4422
        return $limits[0];
4423
    }
4424
4425
    /**
4426
     * @param int $senderId
4427
     * @param array $course_info
4428
     * @param string $test
4429
     * @param string $url
4430
     *
4431
     * @return string
4432
     */
4433
    public static function getEmailNotification($senderId, $course_info, $test, $url)
4434
    {
4435
        $teacher_info = api_get_user_info($senderId);
4436
        $from_name = api_get_person_name(
4437
            $teacher_info['firstname'],
4438
            $teacher_info['lastname'],
4439
            null,
4440
            PERSON_NAME_EMAIL_ADDRESS
4441
        );
4442
4443
        $message = '<p>'.get_lang('DearStudentEmailIntroduction').'</p><p>'.get_lang('AttemptVCC');
4444
        $message .= '<h3>'.get_lang('CourseName').'</h3><p>'.Security::remove_XSS($course_info['name']).'';
4445
        $message .= '<h3>'.get_lang('Exercise').'</h3><p>'.Security::remove_XSS($test);
4446
        $message .= '<p>'.get_lang('ClickLinkToViewComment').' <br /><a href="#url#">#url#</a><br />';
4447
        $message .= '<p>'.get_lang('Regards').'</p>';
4448
        $message .= $from_name;
4449
        $message = str_replace("#test#", Security::remove_XSS($test), $message);
4450
        $message = str_replace("#url#", $url, $message);
4451
4452
        return $message;
4453
    }
4454
4455
    /**
4456
     * @return string
4457
     */
4458
    public static function getNotCorrectedYetText()
4459
    {
4460
        return Display::return_message(get_lang('notCorrectedYet'), 'warning');
4461
    }
4462
4463
    /**
4464
     * @param string $message
4465
     * @return string
4466
     */
4467
    public static function getFeedbackText($message)
4468
    {
4469
        // Old style
4470
        //return '<div id="question_feedback">'.$message.'</div>';
4471
        return Display::return_message($message, 'warning', false);
4472
    }
4473
4474
    /**
4475
     * Get the recorder audio component for save a teacher audio feedback
4476
     * @param int $attemptId
4477
     * @param int $questionId
4478
     * @param int $userId
4479
     * @return string
4480
     */
4481
    public static function getOralFeedbackForm($attemptId, $questionId, $userId)
4482
    {
4483
        $view = new Template('', false, false, false, false, false, false);
4484
        $view->assign('user_id', $userId);
4485
        $view->assign('question_id', $questionId);
4486
        $view->assign('directory', "/../exercises/teacher_audio/$attemptId/");
4487
        $view->assign('file_name', "{$questionId}_{$userId}");
4488
        $template = $view->get_template('exercise/oral_expression.tpl');
4489
4490
        return $view->fetch($template);
4491
    }
4492
4493
    /**
4494
     * Get the audio componen for a teacher audio feedback
4495
     * @param int $attemptId
4496
     * @param int $questionId
4497
     * @param int $userId
4498
     * @return string
4499
     */
4500
    public static function getOralFeedbackAudio($attemptId, $questionId, $userId)
4501
    {
4502
        $courseInfo = api_get_course_info();
4503
        $sessionId = api_get_session_id();
4504
        $groupId = api_get_group_id();
4505
        $sysCourseDir = api_get_path(SYS_COURSE_PATH).$courseInfo['path'];
4506
        $webCourseDir = api_get_path(WEB_COURSE_PATH).$courseInfo['path'];
4507
        $fileName = "{$questionId}_{$userId}".DocumentManager::getDocumentSuffix($courseInfo, $sessionId, $groupId);
4508
        $filePath = null;
4509
4510
        $relFilePath = "/exercises/teacher_audio/$attemptId/$fileName";
4511
4512
        if (file_exists($sysCourseDir.$relFilePath.'.ogg')) {
4513
            $filePath = $webCourseDir.$relFilePath.'.ogg';
4514
        } elseif (file_exists($sysCourseDir.$relFilePath.'.wav.wav')) {
4515
            $filePath = $webCourseDir.$relFilePath.'.wav.wav';
4516
        } elseif (file_exists($sysCourseDir.$relFilePath.'.wav')) {
4517
            $filePath = $webCourseDir.$relFilePath.'.wav';
4518
        }
4519
4520
        if (!$filePath) {
4521
            return '';
4522
        }
4523
4524
        return Display::tag(
4525
            'audio',
4526
            null,
4527
            ['src' => $filePath]
4528
        );
4529
    }
4530
4531
    /**
4532
     * @return array
4533
     */
4534
    public static function getNotificationSettings()
4535
    {
4536
        $emailAlerts = [
4537
            2 => get_lang('SendEmailToTeacherWhenStudentStartQuiz'),
4538
            1 => get_lang('SendEmailToTeacherWhenStudentEndQuiz'), // default
4539
            3 => get_lang('SendEmailToTeacherWhenStudentEndQuizOnlyIfOpenQuestion'),
4540
            4 => get_lang('SendEmailToTeacherWhenStudentEndQuizOnlyIfOralQuestion')
4541
        ];
4542
4543
        return $emailAlerts;
4544
    }
4545
}
4546