Passed
Push — master ( 3b9d17...c5f69b )
by Julito
17:12
created

displayDegreeChartByCategory()   C

Complexity

Conditions 12
Paths 108

Size

Total Lines 113
Code Lines 77

Duplication

Lines 0
Ratio 0 %

Importance

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

This check marks implicit conversions of arrays to boolean values in a comparison. While in PHP an empty array is considered to be equal (but not identical) to false, this is not always apparent.

Consider making the comparison explicit by using empty(..) or ! empty(...) instead.

Loading history...
1229
            $exerciseId = $data['exe_exo_id'];
1230
1231
            $objectExercise = new Exercise();
1232
            $objectExercise->read($exerciseId);
1233
1234
            return $objectExercise->getQuestionCount();
1235
        }
1236
1237
        return 0;
1238
    }
1239
1240
    /**
1241
     * Display student chart results for these question types.
1242
     *
1243
     * @param int      $exeId
1244
     * @param Exercise $objExercice
1245
     *
1246
     * @return string
1247
     */
1248
    public static function displayStudentsChartResults($exeId, $objExercice)
1249
    {
1250
        $numberOfQuestions = self::getNumberOfQuestionsForExeId($exeId);
1251
        $globalScoreList = self::getColorNumberListForAttempt($exeId);
1252
        $html = self::displayDegreeChart(
1253
            $globalScoreList,
1254
            600,
1255
            get_lang('Your overall results for the test'),
1256
            2,
1257
            0,
1258
            true,
1259
            false,
1260
            false,
1261
            $numberOfQuestions
1262
        );
1263
        $html .= '<br/>';
1264
1265
        $previousAttemptId = self::getPreviousAttemptId($exeId);
1266
        if ($previousAttemptId > 0) {
1267
            $previousAttemptScoreList = self::getColorNumberListForAttempt(
1268
                $previousAttemptId
1269
            );
1270
            $html .= self::displayDegreeChart(
1271
                $previousAttemptScoreList,
1272
                600,
1273
                get_lang('In comparison, your latest results for this test'),
1274
                2
1275
            );
1276
            $html .= '<br/>';
1277
        }
1278
1279
        $list = self::getColorNumberListForAttemptByCategory($exeId);
1280
        $html .= self::displayDegreeChartByCategory(
1281
            $list,
1282
            get_lang('Your results by discipline'),
1283
            1,
1284
            $objExercice
1285
        );
1286
        $html .= '<br/>';
1287
1288
        return $html;
1289
    }
1290
1291
    /**
1292
     * send mail to student with degre certainty result test.
1293
     *
1294
     * @param int      $userId
1295
     * @param Exercise $objExercise
1296
     * @param int      $exeId
1297
     */
1298
    public static function sendQuestionCertaintyNotification($userId, $objExercise, $exeId)
1299
    {
1300
        $userInfo = api_get_user_info($userId);
1301
        $recipientName = api_get_person_name($userInfo['firstname'],
1302
            $userInfo['lastname'],
1303
            null,
1304
            PERSON_NAME_EMAIL_ADDRESS
1305
        );
1306
        $subject = '['.get_lang('Please do not reply').'] '
1307
            .html_entity_decode(get_lang('Results for the accomplished test').' "'.$objExercise->title.'"');
1308
1309
        // message sended to the student
1310
        $message = get_lang('Dear').' '.$recipientName.',<br /><br />';
1311
        $exerciseLink = "<a href='".api_get_path(WEB_CODE_PATH).'/exercise/result.php?show_headers=1&'
1312
            .api_get_cidreq()
1313
            ."&id=$exeId'>";
1314
        $exerciseTitle = $objExercise->title;
1315
1316
        $message .= sprintf(
1317
            get_lang('Please follow the instructions below to check your results for test %s.<br /><br />'),
1318
            $exerciseTitle,
1319
            api_get_path(WEB_PATH),
1320
            $exerciseLink
1321
        );
1322
1323
        // show histogram
1324
        $message .= self::displayStudentsChartResults($exeId, $objExercise);
1325
        $message .= get_lang('Kind regards,');
1326
        $message = api_preg_replace("/\\\n/", '', $message);
1327
1328
        MessageManager::send_message_simple($userId, $subject, $message);
1329
    }
1330
}
1331