displayDegreeChartChildren()   F
last analyzed

Complexity

Conditions 20
Paths 15360

Size

Total Lines 169
Code Lines 110

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 20
eloc 110
nc 15360
nop 9
dl 0
loc 169
rs 0
c 0
b 0
f 0

How to fix   Long Method    Complexity    Many Parameters   

Long Method

Small methods make your code easier to understand, in particular if combined with a good name. Besides, if your method is small, finding a good name is usually much easier.

For example, if you find yourself adding comments to a method's body, this is usually a good sign to extract the commented part to a new method, and use the comment as a starting point when coming up with a good name for this new method.

Commonly applied refactorings include:

Many Parameters

Methods with many parameters are not only hard to understand, but their parameters also often become inconsistent when you need more, or different data.

There are several approaches to avoid long parameter lists:

1
<?php
2
3
/* For licensing terms, see /license.txt */
4
5
use Chamilo\CoreBundle\Framework\Container;
6
use Chamilo\CourseBundle\Entity\CQuizQuestion;
7
use ChamiloSession as Session;
8
9
/**
10
 * Class MultipleAnswerTrueFalseDegreeCertainty
11
 * This class allows to instantiate an object of type MULTIPLE_ANSWER
12
 * (MULTIPLE CHOICE, MULTIPLE ANSWER), extending the class question.
13
 */
14
class MultipleAnswerTrueFalseDegreeCertainty extends Question
15
{
16
    public const LEVEL_DARKGREEN = 1;
17
    public const LEVEL_LIGHTGREEN = 2;
18
    public const LEVEL_WHITE = 3;
19
    public const LEVEL_LIGHTRED = 4;
20
    public const LEVEL_DARKRED = 5;
21
22
    public $typePicture = 'mccert.png';
23
    public $explanationLangVar = 'Multiple answer true/false/degree of certainty';
24
    public $optionsTitle;
25
    public $options;
26
27
    public function __construct()
28
    {
29
        parent::__construct();
30
        $this->type = MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY;
31
        $this->isContent = $this->getIsContent();
32
        $this->optionsTitle = [1 => 'Answers', 2 => 'DegreeOfCertaintyThatMyAnswerIsCorrect'];
33
        $this->options = [
34
            1 => 'True',
35
            2 => 'False',
36
            3 => '50%',
37
            4 => '60%',
38
            5 => '70%',
39
            6 => '80%',
40
            7 => '90%',
41
            8 => '100%',
42
        ];
43
    }
44
45
    /**
46
     * Redefines Question::createAnswersForm: creates the HTML form to answer the question.
47
     *
48
     * @uses \globals $text and $class, defined in the calling script
49
     *
50
     * @param FormValidator $form
51
     */
52
    public function createAnswersForm($form)
53
    {
54
        global $text;
55
        $nbAnswers = isset($_POST['nb_answers']) ? (int) $_POST['nb_answers'] : 4;
56
        // The previous default value was 2. See task #1759.
57
        $nbAnswers += (isset($_POST['lessAnswers']) ? -1 : (isset($_POST['moreAnswers']) ? 1 : 0));
58
59
        $courseId = api_get_course_int_id();
60
        $objEx = Session::read('objExercise');
61
        $renderer = &$form->defaultRenderer();
62
        $defaults = [];
63
64
        $form->addHeader(get_lang('Answers'));
65
        $html = '<table class="table table-striped table-hover">
66
            <tr>
67
                <th width="10px">'.get_lang('Number').'</th>
68
                <th width="10px">'.get_lang('True').'</th>
69
                <th width="10px">'.get_lang('False').'</th>
70
                <th width="50%">'.get_lang('Answer').'</th>';
71
72
        // show column comment when feedback is enable
73
        if (EXERCISE_FEEDBACK_TYPE_EXAM != $objEx->getFeedbackType()) {
74
            $html .= '<th width="50%">'.get_lang('Comment').'</th>';
75
        }
76
        $html .= '</tr>';
77
        $form->addHtml($html);
78
        $answer = null;
79
        if (!empty($this->id)) {
80
            $answer = new Answer($this->id);
81
            $answer->read();
82
            if ($answer->nbrAnswers > 0 && !$form->isSubmitted()) {
83
                $nbAnswers = $answer->nbrAnswers;
84
            }
85
        }
86
87
        $form->addElement('hidden', 'nb_answers');
88
        if ($nbAnswers < 1) {
89
            $nbAnswers = 1;
90
            echo Display::return_message(get_lang('You have to create at least one answer'));
91
        }
92
93
        // Can be more options
94
        $optionData = Question::readQuestionOption($this->id);
95
96
        for ($i = 1; $i <= $nbAnswers; $i++) {
97
            $renderer->setElementTemplate(
98
                '<td><!-- BEGIN error --><span class="form_error">{error}</span><!-- END error --><br/>{element}</td>',
99
                'correct['.$i.']'
100
            );
101
            $renderer->setElementTemplate(
102
                '<td><!-- BEGIN error --><span class="form_error">{error}</span><!-- END error --><br/>{element}</td>',
103
                'counter['.$i.']'
104
            );
105
            $renderer->setElementTemplate(
106
                '<td><!-- BEGIN error --><span class="form_error">{error}</span><!-- END error --><br/>{element}</td>',
107
                'answer['.$i.']'
108
            );
109
            $renderer->setElementTemplate(
110
                '<td><!-- BEGIN error --><span class="form_error">{error}</span><!-- END error --><br/>{element}</td>',
111
                'comment['.$i.']'
112
            );
113
114
            $answerNumber = $form->addElement('text', 'counter['.$i.']', null, 'value="'.$i.'"');
115
            $answerNumber->freeze();
116
117
            $defaults['answer['.$i.']'] = '';
118
            $defaults['comment['.$i.']'] = '';
119
            $defaults['correct['.$i.']'] = '';
120
121
            if (is_object($answer)) {
122
                $defaults['answer['.$i.']'] = isset($answer->answer[$i]) ? $answer->answer[$i] : '';
123
                if (isset($_POST['answer']) && isset($_POST['answer'][$i])) {
124
                    $defaults['answer['.$i.']'] = Security::remove_XSS($_POST['answer'][$i]);
125
                }
126
127
                $defaults['comment['.$i.']'] = isset($answer->comment[$i]) ? $answer->comment[$i] : '';
128
                if (isset($_POST['comment']) && isset($_POST['comment'][$i])) {
129
                    $defaults['comment['.$i.']'] = Security::remove_XSS($_POST['comment'][$i]);
130
                }
131
132
                $defaults['weighting['.$i.']'] = isset($answer->weighting[$i]) ? float_format($answer->weighting[$i], 1) : '';
133
                $correct = $answer->correct[$i] ?? '';
134
                $defaults['correct['.$i.']'] = $correct;
135
                if (isset($_POST['correct']) && isset($_POST['correct'][$i])) {
136
                    $defaults['correct['.$i.']'] = Security::remove_XSS($_POST['correct'][$i]);
137
                }
138
139
                $j = 1;
140
                if (!empty($optionData)) {
141
                    foreach ($optionData as $id => $data) {
142
                        $rdoCorrect = $form->addElement('radio', 'correct['.$i.']', null, null, $id);
143
144
                        if (isset($_POST['correct']) && isset($_POST['correct'][$i]) && $j == $_POST['correct'][$i]) {
145
                            $rdoCorrect->setValue(Security::remove_XSS($_POST['correct'][$i]));
146
                        } else {
147
                            $rdoCorrect->setValue($j);
148
                        }
149
                        $j++;
150
                        if (3 == $j) {
151
                            break;
152
                        }
153
                    }
154
                }
155
            } else {
156
                $form->addElement('radio', 'correct['.$i.']', null, null, 1);
157
                $form->addElement('radio', 'correct['.$i.']', null, null, 2);
158
            }
159
160
            $form->addHtmlEditor(
161
                'answer['.$i.']',
162
                null,
163
                true,
164
                false,
165
                ['ToolbarSet' => 'TestProposedAnswer', 'Width' => '100%', 'Height' => '100'],
166
                ['style' => 'vertical-align:middle;'],
167
            );
168
            $form->addRule('answer['.$i.']', get_lang('Required field'), 'required');
169
            $form->applyFilter("answer[$i]", 'attr_on_filter');
170
171
            if (isset($_POST['answer']) && isset($_POST['answer'][$i])) {
172
                $form->getElement("answer[$i]")->setValue(Security::remove_XSS($_POST['answer'][$i]));
173
            }
174
            // show comment when feedback is enable
175
            if (EXERCISE_FEEDBACK_TYPE_EXAM != $objEx->getFeedbackType()) {
176
                $form->addHtmlEditor(
177
                    'comment['.$i.']',
178
                    null,
179
                    false,
180
                    false,
181
                    ['ToolbarSet' => 'TestProposedAnswer', 'Width' => '100%', 'Height' => '100'],
182
                    ['style' => 'vertical-align:middle;'],
183
                );
184
                if (isset($_POST['comment']) && isset($_POST['comment'][$i])) {
185
                    $form->getElement("comment[$i]")->setValue(Security::remove_XSS($_POST['comment'][$i]));
186
                }
187
                $form->applyFilter("comment[$i]", 'attr_on_filter');
188
            }
189
            $form->addElement('html', '</tr>');
190
        }
191
192
        $form->addElement('html', '</table>');
193
        $form->addElement('html', '<br />');
194
195
        // 3 scores
196
        $txtOption1 = $form->addElement('text', 'option[1]', get_lang('Correct'), ['value' => '1']);
197
        $txtOption2 = $form->addElement('text', 'option[2]', get_lang('Wrong'), ['value' => '-0.5']);
198
199
        $form->addElement('hidden', 'option[3]', 0);
200
201
        $form->addRule('option[1]', get_lang('Required field'), 'required');
202
        $form->addRule('option[2]', get_lang('Required field'), 'required');
203
204
        $form->addElement('html', '</tr><table>');
205
        $form->addElement('hidden', 'options_count', 3);
206
        $form->addElement('html', '</table><br /><br />');
207
208
        //Extra values True, false,  Dont known
209
        if (!empty($this->extra)) {
210
            $scores = explode(':', $this->extra);
211
            if (!empty($scores)) {
212
                $txtOption1->setValue($scores[0]);
213
                $txtOption2->setValue($scores[1]);
214
            }
215
        }
216
217
        if (true === $objEx->edit_exercise_in_lp ||
218
            (empty($this->exerciseList) && empty($objEx->id))
219
        ) {
220
            $form->addElement('submit', 'lessAnswers', get_lang('Remove answer option'), 'class="btn btn--danger minus"');
221
            $form->addElement('submit', 'moreAnswers', get_lang('Add answer option'), 'class="btn btn--primary plus"');
222
            $form->addElement('submit', 'submitQuestion', $text, 'class = "btn btn--primary"');
223
        }
224
        $renderer->setElementTemplate('{element}&nbsp;', 'lessAnswers');
225
        $renderer->setElementTemplate('{element}&nbsp;', 'submitQuestion');
226
        $renderer->setElementTemplate('{element}&nbsp;', 'moreAnswers');
227
        $form->addElement('html', '</div></div>');
228
229
        if (!empty($this->id) && !$form->isSubmitted()) {
230
            $form->setDefaults($defaults);
231
        }
232
        $form->setConstants(['nb_answers' => $nbAnswers]);
233
    }
234
235
    /**
236
     * abstract function which creates the form to create / edit the answers of the question.
237
     *
238
     * @param FormValidator $form
239
     * @param Exercise      $exercise
240
     */
241
    public function processAnswersCreation($form, $exercise)
242
    {
243
        $questionWeighting = 0;
244
        $objAnswer = new Answer($this->id);
245
        $nbAnswers = $form->getSubmitValue('nb_answers');
246
        $courseId = api_get_course_int_id();
247
248
        $repo = Container::getQuestionRepository();
249
        /** @var CQuizQuestion $question */
250
        $question = $repo->find($this->id);
251
        $options = $question->getOptions();
252
253
        if (!$options->isEmpty()) {
254
            foreach ($options as $optionData) {
255
                $optionData->setTitle($optionData->getTitle());
256
            }
257
        } else {
258
            for ($i = 1; $i <= 8; $i++) {
259
                Question::saveQuestionOption($question, $this->options[$i], $i);
260
            }
261
        }
262
263
        /* Getting quiz_question_options (true, false, doubt) because
264
          it's possible that there are more options in the future */
265
        $newOptions = Question::readQuestionOption($this->id, $courseId);
266
        $sortedByPosition = [];
267
        foreach ($newOptions as $item) {
268
            $sortedByPosition[$item['position']] = $item;
269
        }
270
271
        /* Saving quiz_question.extra values that has the correct scores of
272
          the true, false, doubt options registered in this format
273
          XX:YY:ZZZ where XX is a float score value. */
274
        $extraValues = [];
275
        for ($i = 1; $i <= 3; $i++) {
276
            $score = trim($form->getSubmitValue('option['.$i.']'));
277
            $extraValues[] = $score;
278
        }
279
        $this->setExtra(implode(':', $extraValues));
280
281
        for ($i = 1; $i <= $nbAnswers; $i++) {
282
            $answer = trim($form->getSubmitValue('answer['.$i.']'));
283
            $comment = trim($form->getSubmitValue('comment['.$i.']'));
284
            $goodAnswer = trim($form->getSubmitValue('correct['.$i.']'));
285
            if (empty($options)) {
286
                // If this is the first time that the question is created then change
287
                // the default values from the form 1 and 2 by the correct "option id" registered
288
                if (!empty($goodAnswer)) {
289
                    $goodAnswer = $sortedByPosition[$goodAnswer]['iid'];
290
                }
291
            }
292
            $questionWeighting += $extraValues[0]; //By default 0 has the correct answers
293
            $objAnswer->createAnswer($answer, $goodAnswer, $comment, '', $i);
294
        }
295
296
        // saves the answers into the data base
297
        $objAnswer->save();
298
299
        // sets the total weighting of the question
300
        $this->updateWeighting($questionWeighting);
301
        $this->save($exercise);
302
    }
303
304
    public function return_header(Exercise $exercise, $counter = null, $score = [])
305
    {
306
        $header = parent::return_header($exercise, $counter, $score);
307
        $header .= '<table class="'.$this->questionTableClass.'"><tr>';
308
        $header .= '<th>'.get_lang('Your choice').'</th>';
309
310
        if ($exercise->showExpectedChoiceColumn()) {
311
            $header .= '<th>'.get_lang('Expected choice').'</th>';
312
        }
313
314
        $header .= '<th>'
315
            .get_lang('Answer')
316
            .'</th><th colspan="2" style="text-align:center;">'
317
            .get_lang('Your degree of certainty')
318
            .'</th>'
319
        ;
320
        if (false === $exercise->hideComment) {
321
            if (EXERCISE_FEEDBACK_TYPE_EXAM != $exercise->getFeedbackType()) {
322
                $header .= '<th>'.get_lang('Comment').'</th>';
323
            }
324
        }
325
        $header .= '</tr>';
326
327
        return $header;
328
    }
329
330
    /**
331
     * Get color code, status, label and description for the current answer.
332
     *
333
     * @param string $studentAnswer
334
     * @param string $expectedAnswer
335
     * @param int    $studentDegreeChoicePosition
336
     *
337
     * @return array An array with indexes 'color', 'background-color', 'status', 'label' and 'description'
338
     */
339
    public function getResponseDegreeInfo($studentAnswer, $expectedAnswer, $studentDegreeChoicePosition)
340
    {
341
        $result = [];
342
        if (3 == $studentDegreeChoicePosition) {
343
            $result = [
344
                'color' => '#000000',
345
                'background-color' => '#F6BA2A',
346
                'status' => self::LEVEL_WHITE,
347
                'label' => get_lang('Declared ignorance'),
348
                'description' => get_lang('Declared ignoranceDescription'),
349
            ];
350
        } else {
351
            $checkResult = $studentAnswer == $expectedAnswer ? true : false;
352
            if ($checkResult) {
353
                if ($studentDegreeChoicePosition >= 6) {
354
                    $result = [
355
                        'color' => '#FFFFFF',
356
                        'background-color' => '#1E9C55',
357
                        'status' => self::LEVEL_DARKGREEN,
358
                        'label' => get_lang('Very sure'),
359
                        'description' => get_lang('Very sureDescription'),
360
                    ];
361
                } elseif ($studentDegreeChoicePosition >= 4 && $studentDegreeChoicePosition <= 5) {
362
                    $result = [
363
                        'color' => '#000000',
364
                        'background-color' => '#B1E183',
365
                        'status' => self::LEVEL_LIGHTGREEN,
366
                        'label' => get_lang('Pretty sure'),
367
                        'description' => get_lang('Pretty sureDescription'),
368
                    ];
369
                }
370
            } else {
371
                if ($studentDegreeChoicePosition >= 6) {
372
                    $result = [
373
                        'color' => '#FFFFFF',
374
                        'background-color' => '#ED4040',
375
                        'status' => self::LEVEL_DARKRED,
376
                        'label' => get_lang('Very unsure'),
377
                        'description' => get_lang('Very unsureDescription'),
378
                    ];
379
                } elseif ($studentDegreeChoicePosition >= 4 && $studentDegreeChoicePosition <= 5) {
380
                    $result = [
381
                        'color' => '#000000',
382
                        'background-color' => '#F79B88',
383
                        'status' => self::LEVEL_LIGHTRED,
384
                        'label' => get_lang('Unsure'),
385
                        'description' => get_lang('UnsureDescription'),
386
                    ];
387
                }
388
            }
389
        }
390
391
        return $result;
392
    }
393
394
    /**
395
     * Method to show the code color and his meaning for the test result.
396
     */
397
    public static function showColorCodes()
398
    {
399
        ?>
400
        <table class="fc-border-separate" cellspacing="0" style="width:600px;
401
            margin: auto; border: 3px solid #A39E9E;" >
402
            <tr style="border-bottom: 1px solid #A39E9E;">
403
                <td style="width:15%; height:30px; background-color: #088A08; border-right: 1px solid #A39E9E;">
404
                    &nbsp;
405
                </td>
406
                <td style="padding-left:10px;">
407
                    <b><?php echo get_lang('Very sure'); ?> :</b>
408
                    <?php echo get_lang('Very sureDescription'); ?>
409
                </td>
410
            </tr>
411
            <tr style="border-bottom: 1px solid #A39E9E;">
412
                <td style="width:15%; height:30px; background-color: #A9F5A9; border-right: 1px solid #A39E9E;">
413
                    &nbsp;
414
                </td>
415
                <td style="padding-left:10px;">
416
                    <b><?php echo get_lang('Pretty sure'); ?> :</b>
417
                    <?php echo get_lang('Pretty sureDescription'); ?>
418
                </td>
419
            </tr>
420
            <tr style="border: 1px solid #A39E9E;">
421
                <td style="width:15%; height:30px; background-color: #FFFFFF; border-right: 1px solid #A39E9E;">
422
                    &nbsp;
423
                </td>
424
                <td style="padding-left:10px;">
425
                    <b><?php echo get_lang('Declared ignorance'); ?> :</b>
426
                    <?php echo get_lang('Declared ignoranceDescription'); ?>
427
                </td>
428
            </tr>
429
            <tr style="border: 1px solid #A39E9E;">
430
                <td style="width:15%; height:30px; background-color: #F6CECE; border-right: 1px solid #A39E9E;">
431
                    &nbsp;
432
                </td>
433
                <td style="padding-left:10px;">
434
                    <b><?php echo get_lang('Unsure'); ?> :</b>
435
                    <?php echo get_lang('UnsureDescription'); ?>
436
                </td>
437
            </tr>
438
            <tr style="border-bottom: 1px solid #A39E9E;">
439
                <td style="width:15%; height:30px; background-color: #FE2E2E; border-right: 1px solid #A39E9E;">
440
                    &nbsp;
441
                </td>
442
                <td style="padding-left:10px;">
443
                    <b><?php echo get_lang('Very unsure'); ?> :</b>
444
                    <?php echo get_lang('Very unsureDescription'); ?>
445
                </td>
446
            </tr>
447
        </table><br/>
448
        <?php
449
    }
450
451
    /**
452
     * Display basic bar charts of results by category of questions.
453
     *
454
     * @param array  $scoreListAll
455
     * @param string $title        The block title
456
     * @param int    $sizeRatio
457
     *
458
     * @return string The HTML/CSS code for the charts block
459
     */
460
    public static function displayDegreeChartByCategory($scoreListAll, $title, $sizeRatio = 1)
461
    {
462
        $maxHeight = 0;
463
        $groupCategoriesByBracket = false;
464
        if ($groupCategoriesByBracket) {
465
            $scoreList = [];
466
            $categoryPrefixList = [];
467
            // categoryPrefix['Math'] = firstCategoryId for this prefix
468
            // rebuild $scoreList factorizing data with category prefix
469
            foreach ($scoreListAll as $categoryId => $scoreListForCategory) {
470
                $objCategory = new Testcategory();
471
                $objCategoryNum = $objCategory->getCategory($categoryId);
472
                preg_match("/^\[([^]]+)\]/", $objCategoryNum->name, $matches);
473
474
                if (count($matches) > 1) {
475
                    // check if we have already see this prefix
476
                    if (array_key_exists($matches[1], $categoryPrefixList)) {
477
                        // add the result color for this entry
478
                        $scoreList[$categoryPrefixList[$matches[1]]][self::LEVEL_DARKGREEN] +=
479
                            $scoreListForCategory[self::LEVEL_DARKGREEN];
480
                        $scoreList[$categoryPrefixList[$matches[1]]][self::LEVEL_LIGHTGREEN] +=
481
                            $scoreListForCategory[self::LEVEL_LIGHTGREEN];
482
                        $scoreList[$categoryPrefixList[$matches[1]]][self::LEVEL_WHITE] +=
483
                            $scoreListForCategory[self::LEVEL_WHITE];
484
                        $scoreList[$categoryPrefixList[$matches[1]]][self::LEVEL_LIGHTRED] +=
485
                            $scoreListForCategory[self::LEVEL_LIGHTRED];
486
                        $scoreList[$categoryPrefixList[$matches[1]]][self::LEVEL_DARKRED] +=
487
                            $scoreListForCategory[self::LEVEL_DARKRED];
488
                    } else {
489
                        $categoryPrefixList[$matches[1]] = $categoryId;
490
                        $scoreList[$categoryId] = $scoreListAll[$categoryId];
491
                    }
492
                } else {
493
                    // doesn't match the prefix '[math] Math category'
494
                    $scoreList[$categoryId] = $scoreListAll[$categoryId];
495
                }
496
            }
497
        } else {
498
            $scoreList = $scoreListAll;
499
        }
500
501
        // get the max height of item to have each table the same height if displayed side by side
502
        foreach ($scoreList as $categoryId => $scoreListForCategory) {
503
            [$noValue, $height] = self::displayDegreeChartChildren(
504
                $scoreListForCategory,
505
                300,
506
                '',
507
                1,
508
                0,
509
                false,
510
                true,
511
                0
512
            );
513
            if ($height > $maxHeight) {
514
                $maxHeight = $height;
515
            }
516
        }
517
518
        $html = '<div class="row-chart">';
519
        $html .= '<h4 class="chart-title">'.$title.'</h4>';
520
521
        $legendTitle = [
522
            'Very unsure',
523
            'Unsure',
524
            'Declared ignorance',
525
            'Pretty sure',
526
            'Very sure',
527
        ];
528
        $html .= '<ul class="chart-legend">';
529
        foreach ($legendTitle as $i => $item) {
530
            $html .= '<li><i class="fa fa-square square_color'.$i.'" aria-hidden="true"></i> '.get_lang($item).'</li>';
531
        }
532
        $html .= '</ul>';
533
534
        // get the html of items
535
        $i = 0;
536
        $testCategory = new Testcategory();
537
        foreach ($scoreList as $categoryId => $scoreListForCategory) {
538
            $category = $testCategory->getCategory($categoryId);
539
            $categoryQuestionName = '';
540
            if ($category) {
541
                $categoryQuestionName = $category->name;
542
            }
543
544
            if ('' === $categoryQuestionName) {
545
                $categoryName = get_lang('Without category');
546
            } else {
547
                $categoryName = $categoryQuestionName;
548
            }
549
550
            $html .= '<div class="col-md-4">';
551
            $html .= self::displayDegreeChartChildren(
552
                $scoreListForCategory,
553
                300,
554
                $categoryName,
555
                1,
556
                $maxHeight,
557
                false,
558
                false,
559
                $groupCategoriesByBracket
560
            );
561
            $html .= '</div>';
562
563
            if (2 == $i) {
564
                $html .= '<div style="clear:both; height: 10px;">&nbsp;</div>';
565
                $i = 0;
566
            } else {
567
                $i++;
568
            }
569
        }
570
        $html .= '</div>';
571
572
        return $html.'<div style="clear:both; height: 10px;" >&nbsp;</div>';
573
    }
574
575
    /**
576
     * Return HTML code for the $scoreList of MultipleAnswerTrueFalseDegreeCertainty questions.
577
     *
578
     * @param        $scoreList
579
     * @param        $widthTable
580
     * @param string $title
581
     * @param int    $sizeRatio
582
     * @param int    $minHeight
583
     * @param bool   $displayExplanationText
584
     * @param bool   $returnHeight
585
     * @param bool   $groupCategoriesByBracket
586
     * @param int    $numberOfQuestions
587
     *
588
     * @return array|string
589
     */
590
    public static function displayDegreeChart(
591
        $scoreList,
592
        $widthTable,
593
        $title = '',
594
        $sizeRatio = 1,
595
        $minHeight = 0,
596
        $displayExplanationText = true,
597
        $returnHeight = false,
598
        $groupCategoriesByBracket = false,
599
        $numberOfQuestions = 0
600
    ) {
601
        $topAndBottomMargin = 10;
602
        $colorList = [
603
            self::LEVEL_DARKRED,
604
            self::LEVEL_LIGHTRED,
605
            self::LEVEL_WHITE,
606
            self::LEVEL_LIGHTGREEN,
607
            self::LEVEL_DARKGREEN,
608
        ];
609
610
        // get total attempt number
611
        $highterColorHeight = 0;
612
        foreach ($scoreList as $color => $number) {
613
            if ($number > $highterColorHeight) {
614
                $highterColorHeight = $number;
615
            }
616
        }
617
618
        $totalAttemptNumber = $numberOfQuestions;
619
        $verticalLineHeight = $highterColorHeight * $sizeRatio * 2 + 122 + $topAndBottomMargin * 2;
620
        if ($verticalLineHeight < $minHeight) {
621
            $minHeightCorrection = $minHeight - $verticalLineHeight;
622
            $verticalLineHeight += $minHeightCorrection;
623
        }
624
625
        // draw chart
626
        $html = '';
627
628
        if ($groupCategoriesByBracket) {
629
            $title = api_preg_replace('/[^]]*$/', '', $title);
630
            $title = ucfirst(api_preg_replace("/[\[\]]/", '', $title));
631
        }
632
633
        $titleDisplay = strpos($title, 'ensemble') > 0 ?
634
            $title."<br/>($totalAttemptNumber questions)" :
635
            $title;
636
        $textSize = strpos($title, 'ensemble') > 0 ||
637
            strpos($title, 'votre dernier résultat à ce test') > 0 ? 100 : 80;
638
639
        $html .= '<div class="row-chart">';
640
        $html .= '<h4 class="chart-title">'.$titleDisplay.'</h4>';
641
642
        $nbResponsesInc = 0;
643
        if (isset($scoreList[4])) {
644
            $nbResponsesInc += (int) $scoreList[4];
645
        }
646
        if (isset($scoreList[5])) {
647
            $nbResponsesInc += (int) $scoreList[5];
648
        }
649
650
        $nbResponsesIng = isset($scoreList[3]) ? $scoreList[3] : 0;
651
652
        $nbResponsesCor = 0;
653
        if (isset($scoreList[1])) {
654
            $nbResponsesCor += (int) $scoreList[1];
655
        }
656
        if (isset($scoreList[2])) {
657
            $nbResponsesCor += (int) $scoreList[2];
658
        }
659
660
        $IncorrectAnswers = sprintf(get_lang('Incorrect answers: %s'), $nbResponsesInc);
661
        $IgnoranceAnswers = sprintf(get_lang('Ignorance: %s'), $nbResponsesIng);
662
        $CorrectAnswers = sprintf(get_lang('Correct answers: %s'), $nbResponsesCor);
663
664
        $html .= '<div class="chart-grid">';
665
666
        $explainHistoList = null;
667
        if ($displayExplanationText) {
668
            // Display of histogram text
669
            $explainHistoList = [
670
                'Very unsure',
671
                'Unsure',
672
                'Declared ignorance',
673
                'Pretty sure',
674
                'Very sure',
675
            ];
676
        }
677
678
        foreach ($colorList as $i => $color) {
679
            if (array_key_exists($color, $scoreList)) {
680
                $scoreOnBottom = $scoreList[$color]; // height of the colored area on the bottom
681
            } else {
682
                $scoreOnBottom = 0;
683
            }
684
            $sizeBar = ($scoreOnBottom * $sizeRatio * 2).'px;';
685
686
            if (0 == $i) {
687
                $html .= '<div class="item">';
688
                $html .= '<div class="panel-certaint" style="min-height:'.$verticalLineHeight.'px; position: relative;">';
689
                $html .= '<div class="answers-title">'.$IncorrectAnswers.'</div>';
690
                $html .= '<ul class="certaint-list-two">';
691
            } elseif (3 == $i) {
692
                $html .= '<div class="item">';
693
                $html .= '<div class="panel-certaint" style="height:'.$verticalLineHeight.'px;  position: relative;">';
694
                $html .= '<div class="answers-title">'.$CorrectAnswers.'</div>';
695
                $html .= '<ul class="certaint-list-two">';
696
            } elseif (2 == $i) {
697
                $html .= '<div class="item">';
698
                $html .= '<div class="panel-certaint" style="height:'.$verticalLineHeight.'px;  position: relative;">';
699
                $html .= '<div class="answers-title">'.$IgnoranceAnswers.'</div>';
700
                $html .= '<ul class="certaint-list">';
701
            }
702
            $html .= '<li>';
703
            $html .= '<div class="certaint-score">';
704
            $html .= $scoreOnBottom;
705
            $html .= '</div>';
706
            $html .= '<div class="levelbar_'.$color.'" style="height:'.$sizeBar.'">&nbsp;</div>';
707
            $html .= '<div class="certaint-text">'.get_lang($explainHistoList[$i]).'</div>';
708
            $html .= '</li>';
709
710
            if (1 == $i || 2 == $i || 4 == $i) {
711
                $html .= '</ul>';
712
                $html .= '</div>';
713
                $html .= '</div>';
714
            }
715
        }
716
717
        $html .= '</div>';
718
        $html .= '</div>';
719
720
        if ($returnHeight) {
721
            return [$html, $verticalLineHeight];
722
        } else {
723
            return $html;
724
        }
725
    }
726
727
    /**
728
     * Return HTML code for the $scoreList of MultipleAnswerTrueFalseDegreeCertainty questions.
729
     *
730
     * @param        $scoreList
731
     * @param        $widthTable
732
     * @param string $title
733
     * @param int    $sizeRatio
734
     * @param int    $minHeight
735
     * @param bool   $displayExplanationText
736
     * @param bool   $returnHeight
737
     * @param bool   $groupCategoriesByBracket
738
     * @param int    $numberOfQuestions
739
     *
740
     * @return array|string
741
     */
742
    public static function displayDegreeChartChildren(
743
        $scoreList,
744
        $widthTable,
745
        $title = '',
746
        $sizeRatio = 1,
747
        $minHeight = 0,
748
        $displayExplanationText = true,
749
        $returnHeight = false,
750
        $groupCategoriesByBracket = false,
751
        $numberOfQuestions = 0
752
    ) {
753
        $topAndBottomMargin = 10;
754
        $colorList = [
755
            self::LEVEL_DARKRED,
756
            self::LEVEL_LIGHTRED,
757
            self::LEVEL_WHITE,
758
            self::LEVEL_LIGHTGREEN,
759
            self::LEVEL_DARKGREEN,
760
        ];
761
762
        // get total attempt number
763
        $highterColorHeight = 0;
764
        foreach ($scoreList as $color => $number) {
765
            if ($number > $highterColorHeight) {
766
                $highterColorHeight = $number;
767
            }
768
        }
769
770
        $totalAttemptNumber = $numberOfQuestions;
771
        $verticalLineHeight = $highterColorHeight * $sizeRatio * 2 + 122 + $topAndBottomMargin * 2;
772
        if ($verticalLineHeight < $minHeight) {
773
            $minHeightCorrection = $minHeight - $verticalLineHeight;
774
            $verticalLineHeight += $minHeightCorrection;
775
        }
776
777
        // draw chart
778
        $html = '';
779
780
        if ($groupCategoriesByBracket) {
781
            $title = api_preg_replace('/[^]]*$/', '', $title);
782
            $title = ucfirst(api_preg_replace("/[\[\]]/", '', $title));
783
        }
784
785
        $textSize = 80;
786
787
        $classGlobalChart = '';
788
        if ($displayExplanationText) {
789
            // global chart
790
            $classGlobalChart = 'globalChart';
791
        }
792
793
        $html .= '<table class="certaintyTable" style="height :'.$verticalLineHeight.'px; margin-bottom: 10px;" >';
794
        $html .= '<tr><th colspan="5" class="'.$classGlobalChart.'">'
795
            .$title
796
            .'</th><tr>'
797
        ;
798
799
        $nbResponsesInc = 0;
800
        if (isset($scoreList[4])) {
801
            $nbResponsesInc += (int) $scoreList[4];
802
        }
803
        if (isset($scoreList[5])) {
804
            $nbResponsesInc += (int) $scoreList[5];
805
        }
806
807
        $nbResponsesIng = isset($scoreList[3]) ? $scoreList[3] : 0;
808
809
        $nbResponsesCor = 0;
810
        if (isset($scoreList[1])) {
811
            $nbResponsesCor += (int) $scoreList[1];
812
        }
813
        if (isset($scoreList[2])) {
814
            $nbResponsesCor += (int) $scoreList[2];
815
        }
816
817
        $colWidth = $widthTable / 5;
818
819
        $html .= '<tr>
820
                <td class="firstLine borderRight '.$classGlobalChart.'"
821
                    colspan="2"
822
                    style="width:'.($colWidth * 2).'px; line-height: 15px; font-size:'.$textSize.'%;">'.
823
            sprintf(get_lang('Incorrect answers: %s'), $nbResponsesInc).'
824
                </td>
825
                <td class="firstLine borderRight '.$classGlobalChart.'"
826
                    style="width:'.$colWidth.'px; line-height: 15px; font-size :'.$textSize.'%;">'.
827
            sprintf(get_lang('Ignorance: %s'), $nbResponsesIng).'
828
                </td>
829
                <td class="firstLine '.$classGlobalChart.'"
830
                    colspan="2"
831
                    style="width:'.($colWidth * 2).'px; line-height: 15px; font-size:'.$textSize.'%;">'.
832
            sprintf(get_lang('Correct answers: %s'), $nbResponsesCor).'
833
                </td>
834
            </tr>';
835
        $html .= '<tr>';
836
837
        foreach ($colorList as $i => $color) {
838
            if (array_key_exists($color, $scoreList)) {
839
                $scoreOnBottom = $scoreList[$color]; // height of the colored area on the bottom
840
            } else {
841
                $scoreOnBottom = 0;
842
            }
843
            $sizeOnBottom = $scoreOnBottom * $sizeRatio * 2;
844
            if (1 == $i || 2 == $i) {
845
                $html .= '<td width="'
846
                    .$colWidth
847
                    .'px" style="border-right: 1px dotted #7FC5FF; vertical-align: bottom;font-size: '
848
                    .$textSize
849
                    .'%;">'
850
                ;
851
            } else {
852
                $html .= '<td width="'
853
                    .$colWidth
854
                    .'px" style="vertical-align: bottom;font-size: '
855
                    .$textSize
856
                    .'%;">'
857
                ;
858
            }
859
            $html .= '<div class="certaint-score">'
860
                .$scoreOnBottom
861
                .'</div><div class="levelbar_'
862
                .$color
863
                .'" style="height: '
864
                .$sizeOnBottom
865
                .'px;">&nbsp;</div>'
866
            ;
867
            $html .= '</td>';
868
        }
869
870
        $html .= '</tr>';
871
872
        if ($displayExplanationText) {
873
            // Display of histogram text
874
            $explainHistoList = [
875
                'Very unsure',
876
                'Unsure',
877
                'Declared ignorance',
878
                'Pretty sure',
879
                'Very sure',
880
            ];
881
            $html .= '<tr>';
882
            $i = 0;
883
            foreach ($explainHistoList as $explain) {
884
                if (1 == $i || 2 == $i) {
885
                    $class = 'borderRight';
886
                } else {
887
                    $class = '';
888
                }
889
                $html .= '<td class="firstLine '
890
                    .$class
891
                    .' '
892
                    .$classGlobalChart
893
                    .'" style="width="'
894
                    .$colWidth
895
                    .'px; font-size:'
896
                    .$textSize
897
                    .'%;">'
898
                ;
899
                $html .= get_lang($explain);
900
                $html .= '</td>';
901
                $i++;
902
            }
903
            $html .= '</tr>';
904
        }
905
        $html .= '</table></center>';
906
907
        if ($returnHeight) {
908
            return [$html, $verticalLineHeight];
909
        } else {
910
            return $html;
911
        }
912
    }
913
914
    /**
915
     * return previous attempt id for this test for student, 0 if no previous attempt.
916
     *
917
     * @param $exeId
918
     *
919
     * @return int
920
     */
921
    public static function getPreviousAttemptId($exeId)
922
    {
923
        $tblTrackEExercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
924
        $exeId = (int) $exeId;
925
        $sql = "SELECT * FROM $tblTrackEExercise
926
                WHERE exe_id = ".$exeId;
927
        $res = Database::query($sql);
928
929
        if (empty(Database::num_rows($res))) {
930
            // if we cannot find the exe_id
931
            return 0;
932
        }
933
934
        $data = Database::fetch_assoc($res);
935
        $courseCode = $data['c_id'];
936
        $exerciseId = $data['exe_exo_id'];
937
        $userId = $data['exe_user_id'];
938
        $attemptDate = $data['exe_date'];
939
940
        if ('0000-00-00 00:00:00' === $attemptDate) {
941
            // incomplete attempt, close it before continue
942
            return 0;
943
        }
944
945
        // look for previous attempt
946
        $exerciseId = (int) $exerciseId;
947
        $userId = (int) $userId;
948
        $sql = "SELECT *
949
                FROM $tblTrackEExercise
950
                WHERE
951
                      c_id = '$courseCode' AND
952
                      exe_exo_id = $exerciseId AND
953
                      exe_user_id = $userId AND
954
                      status = '' AND
955
                      exe_date > '0000-00-00 00:00:00' AND
956
                      exe_date < '$attemptDate'
957
                ORDER BY exe_date DESC";
958
959
        $res = Database::query($sql);
960
961
        if (0 == Database::num_rows($res)) {
962
            // no previous attempt
963
            return 0;
964
        }
965
966
        $data = Database::fetch_assoc($res);
967
968
        return $data['exe_id'];
969
    }
970
971
    /**
972
     * return an array of number of answer color for exe attempt
973
     * for question type = MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY
974
     * e.g.
975
     * [LEVEL_DARKGREEN => 3, LEVEL_LIGHTGREEN => 0, LEVEL_WHITE => 5, LEVEL_LIGHTRED => 12, LEVEL_DARKTRED => 0].
976
     *
977
     * @param $exeId
978
     *
979
     * @return array
980
     */
981
    public static function getColorNumberListForAttempt($exeId)
982
    {
983
        $result = [
984
            self::LEVEL_DARKGREEN => 0,
985
            self::LEVEL_LIGHTGREEN => 0,
986
            self::LEVEL_WHITE => 0,
987
            self::LEVEL_LIGHTRED => 0,
988
            self::LEVEL_DARKRED => 0,
989
        ];
990
991
        $attemptInfoList = self::getExerciseAttemptInfo($exeId);
992
993
        foreach ($attemptInfoList as $attemptInfo) {
994
            $oQuestion = new self();
995
            $oQuestion->read($attemptInfo['question_id']);
996
            if (MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY == $oQuestion->type) {
997
                $answerColor = self::getAnswerColor($exeId, $attemptInfo['question_id'], $attemptInfo['position']);
998
                if ($answerColor) {
999
                    $result[$answerColor]++;
1000
                }
1001
            }
1002
        }
1003
1004
        return $result;
1005
    }
1006
1007
    /**
1008
     * return an array of number of color for question type = MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY
1009
     * for each question category.
1010
     *
1011
     * e.g.
1012
     * [
1013
     *      (categoryId=)5 => [LEVEL_DARKGREEN => 3, LEVEL_WHITE => 5, LEVEL_LIGHTRED => 12]
1014
     *      (categoryId=)2 => [LEVEL_DARKGREEN => 8, LEVEL_LIGHTRED => 2, LEVEL_DARKTRED => 8]
1015
     *      (categoryId=)0 => [LEVEL_DARKGREEN => 1,
1016
     *          LEVEL_LIGHTGREEN => 2,
1017
     *          LEVEL_WHITE => 6,
1018
     *          LEVEL_LIGHTRED => 1,
1019
     *          LEVEL_DARKTRED => 9]
1020
     * ]
1021
     *
1022
     * @param int $exeId
1023
     *
1024
     * @return array
1025
     */
1026
    public static function getColorNumberListForAttemptByCategory($exeId)
1027
    {
1028
        $result = [];
1029
        $attemptInfoList = self::getExerciseAttemptInfo($exeId);
1030
1031
        foreach ($attemptInfoList as $attemptInfo) {
1032
            $oQuestion = new self();
1033
            $oQuestion->read($attemptInfo['question_id']);
1034
            if (MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY == $oQuestion->type) {
1035
                $questionCategory = Testcategory::getCategoryForQuestion($attemptInfo['question_id']);
1036
1037
                if (!array_key_exists($questionCategory, $result)) {
1038
                    $result[$questionCategory] = [];
1039
                }
1040
1041
                $answerColor = self::getAnswerColor($exeId, $attemptInfo['question_id'], $attemptInfo['position']);
1042
                if ($answerColor && isset($result[$questionCategory])) {
1043
                    if (!isset($result[$questionCategory][$answerColor])) {
1044
                        $result[$questionCategory][$answerColor] = 0;
1045
                    }
1046
                    $result[$questionCategory][$answerColor]++;
1047
                }
1048
            }
1049
        }
1050
1051
        return $result;
1052
    }
1053
1054
    /**
1055
     * Return true if answer of $exeId, $questionId, $position is correct, otherwise return false.
1056
     *
1057
     * @param $exeId
1058
     * @param $questionId
1059
     * @param $position
1060
     *
1061
     * @return int
1062
     */
1063
    public static function getAnswerColor($exeId, $questionId, $position)
1064
    {
1065
        $attemptInfoList = self::getExerciseAttemptInfo($exeId, $questionId, $position);
1066
1067
        if (1 != count($attemptInfoList)) {
1068
            // havent got the answer
1069
            return 0;
1070
        }
1071
1072
        $answerCodes = $attemptInfoList[0]['answer'];
1073
1074
        // student answer
1075
        $splitAnswer = preg_split('/:/', $answerCodes);
1076
        // get correct answer option id
1077
        $correctAnswerOptionId = self::getCorrectAnswerOptionId($splitAnswer[0]);
1078
        if (0 == $correctAnswerOptionId) {
1079
            // error returning the correct answer option id
1080
            return 0;
1081
        }
1082
1083
        // get student answer option id
1084
        $studentAnswerOptionId = $splitAnswer[1] ?? null;
1085
1086
        // we got the correct answer option id, let's compare ti with the student answer
1087
        $percentage = null;
1088
        if (isset($splitAnswer[2])) {
1089
            $percentage = self::getPercentagePosition($splitAnswer[2]);
1090
        }
1091
1092
        if ($studentAnswerOptionId == $correctAnswerOptionId) {
1093
            // yeah, student got correct answer
1094
            switch ($percentage) {
1095
                case 3:
1096
                    return self::LEVEL_WHITE;
1097
                case 4:
1098
                case 5:
1099
                    return self::LEVEL_LIGHTGREEN;
1100
                case 6:
1101
                case 7:
1102
                case 8:
1103
                    return self::LEVEL_DARKGREEN;
1104
                default:
1105
                    return 0;
1106
            }
1107
        } else {
1108
            // bummer, wrong answer dude
1109
            switch ($percentage) {
1110
                case 3:
1111
                    return self::LEVEL_WHITE;
1112
                case 4:
1113
                case 5:
1114
                    return self::LEVEL_LIGHTRED;
1115
                case 6:
1116
                case 7:
1117
                case 8:
1118
                    return self::LEVEL_DARKRED;
1119
                default:
1120
                    return 0;
1121
            }
1122
        }
1123
    }
1124
1125
    /**
1126
     * Return the position of certitude %age choose by student.
1127
     *
1128
     * @param $optionId
1129
     *
1130
     * @return int
1131
     */
1132
    public static function getPercentagePosition($optionId)
1133
    {
1134
        $tblAnswerOption = Database::get_course_table(TABLE_QUIZ_QUESTION_OPTION);
1135
        $courseId = api_get_course_int_id();
1136
        $optionId = (int) $optionId;
1137
        $sql = "SELECT position
1138
                FROM $tblAnswerOption
1139
                WHERE c_id = $courseId AND id = $optionId";
1140
        $res = Database::query($sql);
1141
1142
        if (0 == Database::num_rows($res)) {
1143
            return 0;
1144
        }
1145
1146
        $data = Database::fetch_assoc($res);
1147
1148
        return $data['position'];
1149
    }
1150
1151
    /**
1152
     * return the correct id from c_quiz_question_option for question idAuto.
1153
     *
1154
     * @param $idAuto
1155
     *
1156
     * @return int
1157
     */
1158
    public static function getCorrectAnswerOptionId($idAuto)
1159
    {
1160
        $tblAnswer = Database::get_course_table(TABLE_QUIZ_ANSWER);
1161
        $idAuto = (int) $idAuto;
1162
        $sql = "SELECT correct FROM $tblAnswer
1163
                WHERE iid = $idAuto";
1164
1165
        $res = Database::query($sql);
1166
        $data = Database::fetch_assoc($res);
1167
        if (Database::num_rows($res) > 0) {
1168
            return $data['correct'];
1169
        }
1170
1171
        return 0;
1172
    }
1173
1174
    /**
1175
     * return an array of exe info from track_e_attempt.
1176
     *
1177
     * @param int $exeId
1178
     * @param int $questionId
1179
     * @param int $position
1180
     *
1181
     * @return array
1182
     */
1183
    public static function getExerciseAttemptInfo($exeId, $questionId = -1, $position = -1)
1184
    {
1185
        $result = [];
1186
        $and = '';
1187
        $questionId = (int) $questionId;
1188
        $position = (int) $position;
1189
        $exeId = (int) $exeId;
1190
1191
        if ($questionId >= 0) {
1192
            $and .= " AND question_id = $questionId";
1193
        }
1194
        if ($position >= 0) {
1195
            $and .= " AND position = $position";
1196
        }
1197
1198
        $tblExeAttempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
1199
        $sql = "SELECT * FROM $tblExeAttempt
1200
                WHERE exe_id = $exeId $and";
1201
1202
        $res = Database::query($sql);
1203
        while ($data = Database::fetch_assoc($res)) {
1204
            $result[] = $data;
1205
        }
1206
1207
        return $result;
1208
    }
1209
1210
    /**
1211
     * @param int $exeId
1212
     *
1213
     * @return int
1214
     */
1215
    public static function getNumberOfQuestionsForExeId($exeId)
1216
    {
1217
        $tableTrackEExercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
1218
        $exeId = (int) $exeId;
1219
1220
        $sql = "SELECT exe_exo_id
1221
                FROM $tableTrackEExercise
1222
                WHERE exe_id=".$exeId;
1223
        $res = Database::query($sql);
1224
        $data = Database::fetch_assoc($res);
1225
        if ($data) {
1226
            $exerciseId = $data['exe_exo_id'];
1227
1228
            $objectExercise = new Exercise();
1229
            $objectExercise->read($exerciseId);
1230
1231
            return $objectExercise->getQuestionCount();
1232
        }
1233
1234
        return 0;
1235
    }
1236
1237
    /**
1238
     * Display student chart results for these question types.
1239
     *
1240
     * @param int      $exeId
1241
     * @param Exercise $objExercice
1242
     *
1243
     * @return string
1244
     */
1245
    public static function displayStudentsChartResults($exeId, $objExercice)
1246
    {
1247
        $numberOfQuestions = self::getNumberOfQuestionsForExeId($exeId);
1248
        $globalScoreList = self::getColorNumberListForAttempt($exeId);
1249
        $html = self::displayDegreeChart(
1250
            $globalScoreList,
1251
            600,
1252
            get_lang('Your overall results for the test'),
1253
            2,
1254
            0,
1255
            true,
1256
            false,
1257
            false,
1258
            $numberOfQuestions
1259
        );
1260
        $html .= '<br/>';
1261
1262
        $previousAttemptId = self::getPreviousAttemptId($exeId);
1263
        if ($previousAttemptId > 0) {
1264
            $previousAttemptScoreList = self::getColorNumberListForAttempt(
1265
                $previousAttemptId
1266
            );
1267
            $html .= self::displayDegreeChart(
1268
                $previousAttemptScoreList,
1269
                600,
1270
                get_lang('In comparison, your latest results for this test'),
1271
                2
1272
            );
1273
            $html .= '<br/>';
1274
        }
1275
1276
        $list = self::getColorNumberListForAttemptByCategory($exeId);
1277
        $html .= self::displayDegreeChartByCategory(
1278
            $list,
1279
            get_lang('Your results by discipline'),
1280
            1,
1281
            $objExercice
1282
        );
1283
        $html .= '<br/>';
1284
1285
        return $html;
1286
    }
1287
1288
    /**
1289
     * send mail to student with degre certainty result test.
1290
     *
1291
     * @param int      $userId
1292
     * @param Exercise $objExercise
1293
     * @param int      $exeId
1294
     */
1295
    public static function sendQuestionCertaintyNotification($userId, $objExercise, $exeId)
1296
    {
1297
        $userInfo = api_get_user_info($userId);
1298
        $recipientName = api_get_person_name($userInfo['firstname'],
1299
            $userInfo['lastname'],
1300
            null,
1301
            PERSON_NAME_EMAIL_ADDRESS
1302
        );
1303
        $subject = '['.get_lang('Please do not reply').'] '
1304
            .html_entity_decode(get_lang('Results for the accomplished test').' "'.$objExercise->title.'"');
1305
1306
        // message sended to the student
1307
        $message = get_lang('Dear').' '.$recipientName.',<br /><br />';
1308
        $exerciseLink = "<a href='".api_get_path(WEB_CODE_PATH).'/exercise/result.php?show_headers=1&'
1309
            .api_get_cidreq()
1310
            ."&id=$exeId'>";
1311
        $exerciseTitle = $objExercise->title;
1312
1313
        $message .= sprintf(
1314
            get_lang('Please follow the instructions below to check your results for test %s.<br /><br />'),
1315
            $exerciseTitle,
1316
            api_get_path(WEB_PATH),
1317
            $exerciseLink
1318
        );
1319
1320
        // show histogram
1321
        $message .= self::displayStudentsChartResults($exeId, $objExercise);
1322
        $message .= get_lang('Kind regards,');
1323
        $message = api_preg_replace("/\\\n/", '', $message);
1324
1325
        MessageManager::send_message_simple($userId, $subject, $message);
1326
    }
1327
}
1328