getResponseDegreeInfo()   B
last analyzed

Complexity

Conditions 10
Paths 13

Size

Total Lines 53
Code Lines 41

Duplication

Lines 0
Ratio 0 %

Importance

Changes 0
Metric Value
cc 10
eloc 41
nc 13
nop 3
dl 0
loc 53
rs 7.6666
c 0
b 0
f 0

How to fix   Long Method    Complexity   

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:

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