Total Complexity | 160 |
Total Lines | 1327 |
Duplicated Lines | 0 % |
Changes | 0 |
Complex classes like MultipleAnswerTrueFalseDegreeCertainty often do a lot of different things. To break such a class down, we need to identify a cohesive component within that class. A common approach to find such a component is to look for fields/methods that share the same prefixes, or suffixes.
Once you have determined the fields that belong together, you can apply the Extract Class refactoring. If the component makes sense as a sub-class, Extract Subclass is also a candidate, and is often faster.
While breaking up the class, it is a good idea to analyze how other classes use MultipleAnswerTrueFalseDegreeCertainty, and based on these observations, apply Extract Interface, too.
1 | <?php |
||
12 | class MultipleAnswerTrueFalseDegreeCertainty extends Question |
||
13 | { |
||
14 | public const LEVEL_DARKGREEN = 1; |
||
15 | public const LEVEL_LIGHTGREEN = 2; |
||
16 | public const LEVEL_WHITE = 3; |
||
17 | public const LEVEL_LIGHTRED = 4; |
||
18 | public const LEVEL_DARKRED = 5; |
||
19 | |||
20 | public $typePicture = 'mccert.png'; |
||
21 | public $explanationLangVar = 'MultipleAnswerTrueFalseDegreeCertainty'; |
||
22 | public $optionsTitle; |
||
23 | public $options; |
||
24 | |||
25 | /** |
||
26 | * Constructor. |
||
27 | */ |
||
28 | public function __construct() |
||
29 | { |
||
30 | parent::__construct(); |
||
31 | $this->type = MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY; |
||
32 | $this->isContent = $this->getIsContent(); |
||
33 | $this->optionsTitle = [1 => 'Answers', 2 => 'DegreeOfCertaintyThatMyAnswerIsCorrect']; |
||
34 | $this->options = [ |
||
35 | 1 => 'True', |
||
36 | 2 => 'False', |
||
37 | 3 => '50%', |
||
38 | 4 => '60%', |
||
39 | 5 => '70%', |
||
40 | 6 => '80%', |
||
41 | 7 => '90%', |
||
42 | 8 => '100%', |
||
43 | ]; |
||
44 | } |
||
45 | |||
46 | /** |
||
47 | * Redefines Question::createAnswersForm: creates the HTML form to answer the question. |
||
48 | * |
||
49 | * @uses \globals $text and $class, defined in the calling script |
||
50 | * |
||
51 | * @param FormValidator $form |
||
52 | * |
||
53 | * @throws Exception |
||
54 | * @throws HTML_QuickForm_Error |
||
55 | */ |
||
56 | public function createAnswersForm($form) |
||
57 | { |
||
58 | global $text; |
||
59 | $nbAnswers = isset($_POST['nb_answers']) ? (int) $_POST['nb_answers'] : 4; |
||
60 | // The previous default value was 2. See task #1759. |
||
61 | $nbAnswers += (isset($_POST['lessAnswers']) ? -1 : (isset($_POST['moreAnswers']) ? 1 : 0)); |
||
62 | |||
63 | $courseId = api_get_course_int_id(); |
||
64 | $objEx = Session::read('objExercise'); |
||
65 | $renderer = &$form->defaultRenderer(); |
||
66 | $defaults = []; |
||
67 | |||
68 | $form->addHeader(get_lang('Answers')); |
||
69 | $html = '<table class="table table-striped table-hover"> |
||
70 | <tr> |
||
71 | <th width="10px">'.get_lang('Number').'</th> |
||
72 | <th width="10px">'.get_lang('True').'</th> |
||
73 | <th width="10px">'.get_lang('False').'</th> |
||
74 | <th width="50%">'.get_lang('Answer').'</th>'; |
||
75 | |||
76 | // show column comment when feedback is enable |
||
77 | if ($objEx->getFeedbackType() != EXERCISE_FEEDBACK_TYPE_EXAM) { |
||
78 | $html .= '<th width="50%">'.get_lang('Comment').'</th>'; |
||
79 | } |
||
80 | $html .= '</tr>'; |
||
81 | |||
82 | $form->addHtml($html); |
||
83 | |||
84 | $correct = 0; |
||
85 | $answer = null; |
||
86 | if (!empty($this->iid)) { |
||
87 | $answer = new Answer($this->iid); |
||
88 | $answer->read(); |
||
89 | if ($answer->nbrAnswers > 0 && !$form->isSubmitted()) { |
||
90 | $nbAnswers = $answer->nbrAnswers; |
||
91 | } |
||
92 | } |
||
93 | |||
94 | $form->addElement('hidden', 'nb_answers'); |
||
95 | $boxesNames = []; |
||
96 | |||
97 | if ($nbAnswers < 1) { |
||
98 | $nbAnswers = 1; |
||
99 | echo Display::return_message(get_lang('YouHaveToCreateAtLeastOneAnswer')); |
||
100 | } |
||
101 | |||
102 | // Can be more options |
||
103 | $optionData = Question::readQuestionOption($this->iid, $courseId); |
||
104 | |||
105 | for ($i = 1; $i <= $nbAnswers; $i++) { |
||
106 | $renderer->setElementTemplate( |
||
107 | '<td><!-- BEGIN error --><span class="form_error">{error}</span><!-- END error --><br/>{element}</td>', |
||
108 | 'correct['.$i.']' |
||
109 | ); |
||
110 | $renderer->setElementTemplate( |
||
111 | '<td><!-- BEGIN error --><span class="form_error">{error}</span><!-- END error --><br/>{element}</td>', |
||
112 | 'counter['.$i.']' |
||
113 | ); |
||
114 | $renderer->setElementTemplate( |
||
115 | '<td><!-- BEGIN error --><span class="form_error">{error}</span><!-- END error --><br/>{element}</td>', |
||
116 | 'answer['.$i.']' |
||
117 | ); |
||
118 | $renderer->setElementTemplate( |
||
119 | '<td><!-- BEGIN error --><span class="form_error">{error}</span><!-- END error --><br/>{element}</td>', |
||
120 | 'comment['.$i.']' |
||
121 | ); |
||
122 | |||
123 | $answerNumber = $form->addElement('text', 'counter['.$i.']', null, 'value="'.$i.'"'); |
||
124 | $answerNumber->freeze(); |
||
125 | |||
126 | $defaults['answer['.$i.']'] = ''; |
||
127 | $defaults['comment['.$i.']'] = ''; |
||
128 | $defaults['correct['.$i.']'] = ''; |
||
129 | |||
130 | if (is_object($answer)) { |
||
131 | $defaults['answer['.$i.']'] = isset($answer->answer[$i]) ? $answer->answer[$i] : ''; |
||
132 | $defaults['comment['.$i.']'] = isset($answer->comment[$i]) ? $answer->comment[$i] : ''; |
||
133 | $defaults['weighting['.$i.']'] = isset($answer->weighting[$i]) ? float_format($answer->weighting[$i], 1) : ''; |
||
134 | $correct = isset($answer->correct[$i]) ? $answer->correct[$i] : ''; |
||
135 | $defaults['correct['.$i.']'] = $correct; |
||
136 | |||
137 | $j = 1; |
||
138 | if (!empty($optionData)) { |
||
139 | foreach ($optionData as $id => $data) { |
||
140 | $rdoCorrect = $form->addElement('radio', 'correct['.$i.']', null, null, $id); |
||
141 | |||
142 | if (isset($_POST['correct']) && isset($_POST['correct'][$i]) && $id == $_POST['correct'][$i]) { |
||
143 | $rdoCorrect->setValue(Security::remove_XSS($_POST['correct'][$i])); |
||
144 | } |
||
145 | |||
146 | $j++; |
||
147 | if ($j == 3) { |
||
148 | break; |
||
149 | } |
||
150 | } |
||
151 | } |
||
152 | } else { |
||
153 | $form->addElement('radio', 'correct['.$i.']', null, null, 1); |
||
154 | $form->addElement('radio', 'correct['.$i.']', null, null, 2); |
||
155 | } |
||
156 | |||
157 | $boxesNames[] = 'correct['.$i.']'; |
||
158 | $txtAnswer = $form->addElement( |
||
159 | 'html_editor', |
||
160 | 'answer['.$i.']', |
||
161 | null, |
||
162 | ['style' => 'vertical-align:middle;'], |
||
163 | ['ToolbarSet' => 'TestProposedAnswer', 'Width' => '100%', 'Height' => '100'] |
||
164 | ); |
||
165 | $form->addRule('answer['.$i.']', get_lang('ThisFieldIsRequired'), 'required'); |
||
166 | $form->applyFilter("answer[$i]", 'attr_on_filter'); |
||
167 | |||
168 | if (isset($_POST['answer']) && isset($_POST['answer'][$i])) { |
||
169 | $txtAnswer->setValue(Security::remove_XSS($_POST['answer'][$i])); |
||
170 | } |
||
171 | |||
172 | // show comment when feedback is enable |
||
173 | if ($objEx->getFeedbackType() != EXERCISE_FEEDBACK_TYPE_EXAM) { |
||
174 | $txtComment = $form->addElement( |
||
175 | 'html_editor', |
||
176 | 'comment['.$i.']', |
||
177 | null, |
||
178 | ['style' => 'vertical-align:middle;'], |
||
179 | ['ToolbarSet' => 'TestProposedAnswer', 'Width' => '100%', 'Height' => '100'] |
||
180 | ); |
||
181 | $form->applyFilter("comment[$i]", 'attr_on_filter'); |
||
182 | |||
183 | if (isset($_POST['comment']) && isset($_POST['comment'][$i])) { |
||
184 | $txtComment->setValue(Security::remove_XSS($_POST['comment'][$i])); |
||
185 | } |
||
186 | } |
||
187 | $form->addElement('html', '</tr>'); |
||
188 | } |
||
189 | |||
190 | $form->addElement('html', '</table>'); |
||
191 | $form->addElement('html', '<br />'); |
||
192 | |||
193 | // 3 scores |
||
194 | $txtOption1 = $form->addElement('text', 'option[1]', get_lang('Correct'), ['value' => '1']); |
||
195 | $txtOption2 = $form->addElement('text', 'option[2]', get_lang('Wrong'), ['value' => '-0.5']); |
||
196 | |||
197 | $form->addElement('hidden', 'option[3]', 0); |
||
198 | |||
199 | $form->addRule('option[1]', get_lang('ThisFieldIsRequired'), 'required'); |
||
200 | $form->addRule('option[2]', get_lang('ThisFieldIsRequired'), 'required'); |
||
201 | |||
202 | $form->addElement('html', '</tr><table>'); |
||
203 | $form->addElement('hidden', 'options_count', 3); |
||
204 | $form->addElement('html', '</table><br /><br />'); |
||
205 | |||
206 | //Extra values True, false, Don't known |
||
207 | if (!empty($this->extra)) { |
||
208 | $scores = explode(':', $this->extra); |
||
209 | if (!empty($scores)) { |
||
210 | $txtOption1->setValue($scores[0]); |
||
211 | $txtOption2->setValue($scores[1]); |
||
212 | } |
||
213 | } |
||
214 | |||
215 | if ($objEx->edit_exercise_in_lp === true || |
||
216 | (empty($this->exerciseList) && empty($objEx->iid)) |
||
217 | ) { |
||
218 | $form->addElement('submit', 'lessAnswers', get_lang('LessAnswer'), 'class="btn btn-danger minus"'); |
||
219 | $form->addElement('submit', 'moreAnswers', get_lang('PlusAnswer'), 'class="btn btn-primary plus"'); |
||
220 | $form->addElement('submit', 'submitQuestion', $text, 'class = "btn btn-primary"'); |
||
221 | } |
||
222 | $renderer->setElementTemplate('{element} ', 'lessAnswers'); |
||
223 | $renderer->setElementTemplate('{element} ', 'submitQuestion'); |
||
224 | $renderer->setElementTemplate('{element} ', 'moreAnswers'); |
||
225 | $form->addElement('html', '</div></div>'); |
||
226 | |||
227 | if (!empty($this->iid) && !$form->isSubmitted()) { |
||
228 | $form->setDefaults($defaults); |
||
229 | } |
||
230 | |||
231 | $form->setConstants(['nb_answers' => $nbAnswers]); |
||
232 | } |
||
233 | |||
234 | /** |
||
235 | * abstract function which creates the form to create / edit the answers of the question. |
||
236 | * |
||
237 | * @param FormValidator $form |
||
238 | * @param Exercise $exercise |
||
239 | */ |
||
240 | public function processAnswersCreation($form, $exercise) |
||
299 | } |
||
300 | |||
301 | /** |
||
302 | * {@inheritdoc} |
||
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->question_table_class.'"><tr>'; |
||
308 | $header .= '<th>'.get_lang('Choice').'</th>'; |
||
309 | |||
310 | if ($exercise->showExpectedChoiceColumn()) { |
||
311 | $header .= '<th>'.get_lang('ExpectedChoice').'</th>'; |
||
312 | } |
||
313 | |||
314 | $header .= '<th>' |
||
315 | .get_lang('Answer') |
||
316 | .'</th><th colspan="2" style="text-align:center;">' |
||
317 | .get_lang('YourDegreeOfCertainty') |
||
318 | .'</th>' |
||
319 | ; |
||
320 | |||
321 | if (false === $exercise->hideComment) { |
||
322 | if ($exercise->getFeedbackType() != EXERCISE_FEEDBACK_TYPE_EXAM) { |
||
323 | $header .= '<th>'.get_lang('Comment').'</th>'; |
||
324 | } |
||
325 | } |
||
326 | $header .= '</tr>'; |
||
327 | |||
328 | return $header; |
||
329 | } |
||
330 | |||
331 | /** |
||
332 | * Get color code, status, label and description for the current answer. |
||
333 | * |
||
334 | * @param string $studentAnswer |
||
335 | * @param string $expectedAnswer |
||
336 | * @param int $studentDegreeChoicePosition |
||
337 | * |
||
338 | * @return array An array with indexes 'color', 'background-color', 'status', 'label' and 'description' |
||
339 | */ |
||
340 | public function getResponseDegreeInfo($studentAnswer, $expectedAnswer, $studentDegreeChoicePosition) |
||
341 | { |
||
342 | $result = []; |
||
343 | if ($studentDegreeChoicePosition == 3) { |
||
344 | $result = [ |
||
345 | 'color' => '#000000', |
||
346 | 'background-color' => '#F6BA2A', |
||
347 | 'status' => self::LEVEL_WHITE, |
||
348 | 'label' => get_lang('DegreeOfCertaintyDeclaredIgnorance'), |
||
349 | 'description' => get_lang('DegreeOfCertaintyDeclaredIgnoranceDescription'), |
||
350 | ]; |
||
351 | } else { |
||
352 | $checkResult = $studentAnswer == $expectedAnswer ? true : false; |
||
353 | if ($checkResult) { |
||
354 | if ($studentDegreeChoicePosition >= 6) { |
||
355 | $result = [ |
||
356 | 'color' => '#FFFFFF', |
||
357 | 'background-color' => '#1E9C55', |
||
358 | 'status' => self::LEVEL_DARKGREEN, |
||
359 | 'label' => get_lang('DegreeOfCertaintyVerySure'), |
||
360 | 'description' => get_lang('DegreeOfCertaintyVerySureDescription'), |
||
361 | ]; |
||
362 | } elseif ($studentDegreeChoicePosition >= 4 && $studentDegreeChoicePosition <= 5) { |
||
363 | $result = [ |
||
364 | 'color' => '#000000', |
||
365 | 'background-color' => '#B1E183', |
||
366 | 'status' => self::LEVEL_LIGHTGREEN, |
||
367 | 'label' => get_lang('DegreeOfCertaintyPrettySure'), |
||
368 | 'description' => get_lang('DegreeOfCertaintyPrettySureDescription'), |
||
369 | ]; |
||
370 | } |
||
371 | } else { |
||
372 | if ($studentDegreeChoicePosition >= 6) { |
||
373 | $result = [ |
||
374 | 'color' => '#FFFFFF', |
||
375 | 'background-color' => '#ED4040', |
||
376 | 'status' => self::LEVEL_DARKRED, |
||
377 | 'label' => get_lang('DegreeOfCertaintyVeryUnsure'), |
||
378 | 'description' => get_lang('DegreeOfCertaintyVeryUnsureDescription'), |
||
379 | ]; |
||
380 | } elseif ($studentDegreeChoicePosition >= 4 && $studentDegreeChoicePosition <= 5) { |
||
381 | $result = [ |
||
382 | 'color' => '#000000', |
||
383 | 'background-color' => '#F79B88', |
||
384 | 'status' => self::LEVEL_LIGHTRED, |
||
385 | 'label' => get_lang('DegreeOfCertaintyUnsure'), |
||
386 | 'description' => get_lang('DegreeOfCertaintyUnsureDescription'), |
||
387 | ]; |
||
388 | } |
||
389 | } |
||
390 | } |
||
391 | |||
392 | return $result; |
||
393 | } |
||
394 | |||
395 | /** |
||
396 | * Method to show the code color and his meaning for the test result. |
||
397 | */ |
||
398 | public static function showColorCodes() |
||
399 | { |
||
400 | ?> |
||
401 | <table class="fc-border-separate" cellspacing="0" style="width:600px; |
||
402 | margin: auto; border: 3px solid #A39E9E;" > |
||
403 | <tr style="border-bottom: 1px solid #A39E9E;"> |
||
404 | <td style="width:15%; height:30px; background-color: #088A08; border-right: 1px solid #A39E9E;"> |
||
405 | |
||
406 | </td> |
||
407 | <td style="padding-left:10px;"> |
||
408 | <b><?php echo get_lang('DegreeOfCertaintyVerySure'); ?> :</b> |
||
409 | <?php echo get_lang('DegreeOfCertaintyVerySureDescription'); ?> |
||
410 | </td> |
||
411 | </tr> |
||
412 | <tr style="border-bottom: 1px solid #A39E9E;"> |
||
413 | <td style="width:15%; height:30px; background-color: #A9F5A9; border-right: 1px solid #A39E9E;"> |
||
414 | |
||
415 | </td> |
||
416 | <td style="padding-left:10px;"> |
||
417 | <b><?php echo get_lang('DegreeOfCertaintyPrettySure'); ?> :</b> |
||
418 | <?php echo get_lang('DegreeOfCertaintyPrettySureDescription'); ?> |
||
419 | </td> |
||
420 | </tr> |
||
421 | <tr style="border: 1px solid #A39E9E;"> |
||
422 | <td style="width:15%; height:30px; background-color: #FFFFFF; border-right: 1px solid #A39E9E;"> |
||
423 | |
||
424 | </td> |
||
425 | <td style="padding-left:10px;"> |
||
426 | <b><?php echo get_lang('DegreeOfCertaintyDeclaredIgnorance'); ?> :</b> |
||
427 | <?php echo get_lang('DegreeOfCertaintyDeclaredIgnoranceDescription'); ?> |
||
428 | </td> |
||
429 | </tr> |
||
430 | <tr style="border: 1px solid #A39E9E;"> |
||
431 | <td style="width:15%; height:30px; background-color: #F6CECE; border-right: 1px solid #A39E9E;"> |
||
432 | |
||
433 | </td> |
||
434 | <td style="padding-left:10px;"> |
||
435 | <b><?php echo get_lang('DegreeOfCertaintyUnsure'); ?> :</b> |
||
436 | <?php echo get_lang('DegreeOfCertaintyUnsureDescription'); ?> |
||
437 | </td> |
||
438 | </tr> |
||
439 | <tr style="border-bottom: 1px solid #A39E9E;"> |
||
440 | <td style="width:15%; height:30px; background-color: #FE2E2E; border-right: 1px solid #A39E9E;"> |
||
441 | |
||
442 | </td> |
||
443 | <td style="padding-left:10px;"> |
||
444 | <b><?php echo get_lang('DegreeOfCertaintyVeryUnsure'); ?> :</b> |
||
445 | <?php echo get_lang('DegreeOfCertaintyVeryUnsureDescription'); ?> |
||
446 | </td> |
||
447 | </tr> |
||
448 | </table><br/> |
||
449 | <?php |
||
450 | } |
||
451 | |||
452 | /** |
||
453 | * Display basic bar charts of results by category of questions. |
||
454 | * |
||
455 | * @param array $scoreListAll |
||
456 | * @param string $title The block title |
||
457 | * @param int $sizeRatio |
||
458 | * |
||
459 | * @return string The HTML/CSS code for the charts block |
||
460 | */ |
||
461 | public static function displayDegreeChartByCategory($scoreListAll, $title, $sizeRatio = 1) |
||
462 | { |
||
463 | $maxHeight = 0; |
||
464 | $groupCategoriesByBracket = false; |
||
465 | if ($groupCategoriesByBracket) { |
||
466 | $scoreList = []; |
||
467 | $categoryPrefixList = []; |
||
468 | // categoryPrefix['Math'] = firstCategoryId for this prefix |
||
469 | // rebuild $scoreList factorizing data with category prefix |
||
470 | foreach ($scoreListAll as $categoryId => $scoreListForCategory) { |
||
471 | $objCategory = new Testcategory(); |
||
472 | $objCategoryNum = $objCategory->getCategory($categoryId); |
||
473 | preg_match("/^\[([^]]+)\]/", $objCategoryNum->name, $matches); |
||
474 | |||
475 | if (count($matches) > 1) { |
||
476 | // check if we have already see this prefix |
||
477 | if (array_key_exists($matches[1], $categoryPrefixList)) { |
||
478 | // add the result color for this entry |
||
479 | $scoreList[$categoryPrefixList[$matches[1]]][self::LEVEL_DARKGREEN] += |
||
480 | $scoreListForCategory[self::LEVEL_DARKGREEN]; |
||
481 | $scoreList[$categoryPrefixList[$matches[1]]][self::LEVEL_LIGHTGREEN] += |
||
482 | $scoreListForCategory[self::LEVEL_LIGHTGREEN]; |
||
483 | $scoreList[$categoryPrefixList[$matches[1]]][self::LEVEL_WHITE] += |
||
484 | $scoreListForCategory[self::LEVEL_WHITE]; |
||
485 | $scoreList[$categoryPrefixList[$matches[1]]][self::LEVEL_LIGHTRED] += |
||
486 | $scoreListForCategory[self::LEVEL_LIGHTRED]; |
||
487 | $scoreList[$categoryPrefixList[$matches[1]]][self::LEVEL_DARKRED] += |
||
488 | $scoreListForCategory[self::LEVEL_DARKRED]; |
||
489 | } else { |
||
490 | $categoryPrefixList[$matches[1]] = $categoryId; |
||
491 | $scoreList[$categoryId] = $scoreListAll[$categoryId]; |
||
492 | } |
||
493 | } else { |
||
494 | // doesn't match the prefix '[math] Math category' |
||
495 | $scoreList[$categoryId] = $scoreListAll[$categoryId]; |
||
496 | } |
||
497 | } |
||
498 | } else { |
||
499 | $scoreList = $scoreListAll; |
||
500 | } |
||
501 | |||
502 | // get the max height of item to have each table the same height if displayed side by side |
||
503 | $testCategory = new TestCategory(); |
||
504 | foreach ($scoreList as $categoryId => $scoreListForCategory) { |
||
505 | $category = $testCategory->getCategory($categoryId); |
||
506 | if ($category) { |
||
507 | $categoryQuestionName = $category->name; |
||
508 | } |
||
509 | list($noValue, $height) = self::displayDegreeChartChildren( |
||
510 | $scoreListForCategory, |
||
511 | 300, |
||
512 | '', |
||
513 | 1, |
||
514 | 0, |
||
515 | false, |
||
516 | true, |
||
517 | 0 |
||
518 | ); |
||
519 | if ($height > $maxHeight) { |
||
520 | $maxHeight = $height; |
||
521 | } |
||
522 | } |
||
523 | |||
524 | if (count($scoreList) > 1) { |
||
525 | $boxWidth = $sizeRatio * 300 * 2 + 54; |
||
526 | } else { |
||
527 | $boxWidth = $sizeRatio * 300 + 54; |
||
528 | } |
||
529 | |||
530 | $html = '<div class="row-chart">'; |
||
531 | $html .= '<h4 class="chart-title">'.$title.'</h4>'; |
||
532 | |||
533 | $legendTitle = [ |
||
534 | 'DegreeOfCertaintyVeryUnsure', |
||
535 | 'DegreeOfCertaintyUnsure', |
||
536 | 'DegreeOfCertaintyDeclaredIgnorance', |
||
537 | 'DegreeOfCertaintyPrettySure', |
||
538 | 'DegreeOfCertaintyVerySure', |
||
539 | ]; |
||
540 | $html .= '<ul class="chart-legend">'; |
||
541 | foreach ($legendTitle as $i => $item) { |
||
542 | $html .= '<li><i class="fa fa-square square_color'.$i.'" aria-hidden="true"></i> '.get_lang($item).'</li>'; |
||
543 | } |
||
544 | $html .= '</ul>'; |
||
545 | |||
546 | // get the html of items |
||
547 | $i = 0; |
||
548 | $testCategory = new Testcategory(); |
||
549 | foreach ($scoreList as $categoryId => $scoreListForCategory) { |
||
550 | $category = $testCategory->getCategory($categoryId); |
||
551 | $categoryQuestionName = ''; |
||
552 | if ($category) { |
||
553 | $categoryQuestionName = $category->name; |
||
554 | } |
||
555 | |||
556 | if ($categoryQuestionName === '') { |
||
557 | $categoryName = get_lang('WithoutCategory'); |
||
558 | } else { |
||
559 | $categoryName = $categoryQuestionName; |
||
560 | } |
||
561 | |||
562 | $html .= '<div class="col-md-4">'; |
||
563 | $html .= self::displayDegreeChartChildren( |
||
564 | $scoreListForCategory, |
||
565 | 300, |
||
566 | $categoryName, |
||
567 | 1, |
||
568 | $maxHeight, |
||
569 | false, |
||
570 | false, |
||
571 | $groupCategoriesByBracket |
||
572 | ); |
||
573 | $html .= '</div>'; |
||
574 | |||
575 | if ($i == 2) { |
||
576 | $html .= '<div style="clear:both; height: 10px;"> </div>'; |
||
577 | $i = 0; |
||
578 | } else { |
||
579 | $i++; |
||
580 | } |
||
581 | } |
||
582 | $html .= '</div>'; |
||
583 | |||
584 | return $html.'<div style="clear:both; height: 10px;" > </div>'; |
||
585 | } |
||
586 | |||
587 | /** |
||
588 | * Return HTML code for the $scoreList of MultipleAnswerTrueFalseDegreeCertainty questions. |
||
589 | * |
||
590 | * @param $scoreList |
||
591 | * @param $widthTable |
||
592 | * @param string $title |
||
593 | * @param int $sizeRatio |
||
594 | * @param int $minHeight |
||
595 | * @param bool $displayExplanationText |
||
596 | * @param bool $returnHeight |
||
597 | * @param bool $groupCategoriesByBracket |
||
598 | * @param int $numberOfQuestions |
||
599 | * |
||
600 | * @return array|string |
||
601 | */ |
||
602 | public static function displayDegreeChart( |
||
603 | $scoreList, |
||
604 | $widthTable, |
||
605 | $title = '', |
||
606 | $sizeRatio = 1, |
||
607 | $minHeight = 0, |
||
608 | $displayExplanationText = true, |
||
609 | $returnHeight = false, |
||
610 | $groupCategoriesByBracket = false, |
||
611 | $numberOfQuestions = 0 |
||
612 | ) { |
||
613 | $topAndBottomMargin = 10; |
||
614 | $colorList = [ |
||
615 | self::LEVEL_DARKRED, |
||
616 | self::LEVEL_LIGHTRED, |
||
617 | self::LEVEL_WHITE, |
||
618 | self::LEVEL_LIGHTGREEN, |
||
619 | self::LEVEL_DARKGREEN, |
||
620 | ]; |
||
621 | |||
622 | // get total attempt number |
||
623 | $highterColorHeight = 0; |
||
624 | foreach ($scoreList as $color => $number) { |
||
625 | if ($number > $highterColorHeight) { |
||
626 | $highterColorHeight = $number; |
||
627 | } |
||
628 | } |
||
629 | |||
630 | $totalAttemptNumber = $numberOfQuestions; |
||
631 | $verticalLineHeight = $highterColorHeight * $sizeRatio * 2 + 122 + $topAndBottomMargin * 2; |
||
632 | if ($verticalLineHeight < $minHeight) { |
||
633 | $minHeightCorrection = $minHeight - $verticalLineHeight; |
||
634 | $verticalLineHeight += $minHeightCorrection; |
||
635 | } |
||
636 | |||
637 | // draw chart |
||
638 | $html = ''; |
||
639 | |||
640 | if ($groupCategoriesByBracket) { |
||
641 | $title = api_preg_replace("/[^]]*$/", '', $title); |
||
642 | $title = ucfirst(api_preg_replace("/[\[\]]/", '', $title)); |
||
643 | } |
||
644 | |||
645 | $titleDisplay = (strpos($title, "ensemble") > 0) ? |
||
646 | $title."<br/>($totalAttemptNumber questions)" : |
||
647 | $title; |
||
648 | $textSize = ( |
||
649 | strpos($title, 'ensemble') > 0 || |
||
650 | strpos($title, 'votre dernier résultat à ce test') > 0 |
||
651 | ) ? 100 : 80; |
||
652 | |||
653 | $html .= '<div class="row-chart">'; |
||
654 | $html .= '<h4 class="chart-title">'.$titleDisplay.'</h4>'; |
||
655 | |||
656 | $nbResponsesInc = 0; |
||
657 | if (isset($scoreList[4])) { |
||
658 | $nbResponsesInc += (int) $scoreList[4]; |
||
659 | } |
||
660 | if (isset($scoreList[5])) { |
||
661 | $nbResponsesInc += (int) $scoreList[5]; |
||
662 | } |
||
663 | |||
664 | $nbResponsesIng = isset($scoreList[3]) ? $scoreList[3] : 0; |
||
665 | |||
666 | $nbResponsesCor = 0; |
||
667 | if (isset($scoreList[1])) { |
||
668 | $nbResponsesCor += (int) $scoreList[1]; |
||
669 | } |
||
670 | if (isset($scoreList[2])) { |
||
671 | $nbResponsesCor += (int) $scoreList[2]; |
||
672 | } |
||
673 | |||
674 | $IncorrectAnswers = sprintf(get_lang('IncorrectAnswersX'), $nbResponsesInc); |
||
675 | $IgnoranceAnswers = sprintf(get_lang('IgnoranceAnswersX'), $nbResponsesIng); |
||
676 | $CorrectAnswers = sprintf(get_lang('CorrectAnswersX'), $nbResponsesCor); |
||
677 | |||
678 | $html .= '<div class="chart-grid">'; |
||
679 | |||
680 | $explainHistoList = null; |
||
681 | if ($displayExplanationText) { |
||
682 | // Display of histogram text |
||
683 | $explainHistoList = [ |
||
684 | 'DegreeOfCertaintyVeryUnsure', |
||
685 | 'DegreeOfCertaintyUnsure', |
||
686 | 'DegreeOfCertaintyDeclaredIgnorance', |
||
687 | 'DegreeOfCertaintyPrettySure', |
||
688 | 'DegreeOfCertaintyVerySure', |
||
689 | ]; |
||
690 | } |
||
691 | |||
692 | foreach ($colorList as $i => $color) { |
||
693 | if (array_key_exists($color, $scoreList)) { |
||
694 | $scoreOnBottom = $scoreList[$color]; // height of the colored area on the bottom |
||
695 | } else { |
||
696 | $scoreOnBottom = 0; |
||
697 | } |
||
698 | $sizeBar = ($scoreOnBottom * $sizeRatio * 2).'px;'; |
||
699 | |||
700 | if ($i == 0) { |
||
701 | $html .= '<div class="item">'; |
||
702 | $html .= '<div class="panel-certaint" style="min-height:'.$verticalLineHeight.'px; position: relative;">'; |
||
703 | $html .= '<div class="answers-title">'.$IncorrectAnswers.'</div>'; |
||
704 | $html .= '<ul class="certaint-list-two">'; |
||
705 | } elseif ($i == 3) { |
||
706 | $html .= '<div class="item">'; |
||
707 | $html .= '<div class="panel-certaint" style="height:'.$verticalLineHeight.'px; position: relative;">'; |
||
708 | $html .= '<div class="answers-title">'.$CorrectAnswers.'</div>'; |
||
709 | $html .= '<ul class="certaint-list-two">'; |
||
710 | } elseif ($i == 2) { |
||
711 | $html .= '<div class="item">'; |
||
712 | $html .= '<div class="panel-certaint" style="height:'.$verticalLineHeight.'px; position: relative;">'; |
||
713 | $html .= '<div class="answers-title">'.$IgnoranceAnswers.'</div>'; |
||
714 | $html .= '<ul class="certaint-list">'; |
||
715 | } |
||
716 | $html .= '<li>'; |
||
717 | $html .= '<div class="certaint-score">'; |
||
718 | $html .= $scoreOnBottom; |
||
719 | $html .= '</div>'; |
||
720 | $html .= '<div class="levelbar_'.$color.'" style="height:'.$sizeBar.'"> </div>'; |
||
721 | $html .= '<div class="certaint-text">'.get_lang($explainHistoList[$i]).'</div>'; |
||
722 | $html .= '</li>'; |
||
723 | |||
724 | if ($i == 1 || $i == 2 || $i == 4) { |
||
725 | $html .= '</ul>'; |
||
726 | $html .= '</div>'; |
||
727 | $html .= '</div>'; |
||
728 | } |
||
729 | } |
||
730 | |||
731 | $html .= '</div>'; |
||
732 | $html .= '</div>'; |
||
733 | |||
734 | if ($returnHeight) { |
||
735 | return [$html, $verticalLineHeight]; |
||
736 | } else { |
||
737 | return $html; |
||
738 | } |
||
739 | } |
||
740 | |||
741 | /** |
||
742 | * Return HTML code for the $scoreList of MultipleAnswerTrueFalseDegreeCertainty questions. |
||
743 | * |
||
744 | * @param $scoreList |
||
745 | * @param $widthTable |
||
746 | * @param string $title |
||
747 | * @param int $sizeRatio |
||
748 | * @param int $minHeight |
||
749 | * @param bool $displayExplanationText |
||
750 | * @param bool $returnHeight |
||
751 | * @param bool $groupCategoriesByBracket |
||
752 | * @param int $numberOfQuestions |
||
753 | * |
||
754 | * @return array|string |
||
755 | */ |
||
756 | public static function displayDegreeChartChildren( |
||
925 | } |
||
926 | } |
||
927 | |||
928 | /** |
||
929 | * return previous attempt id for this test for student, 0 if no previous attempt. |
||
930 | * |
||
931 | * @param $exeId |
||
932 | * |
||
933 | * @return int |
||
934 | */ |
||
935 | public static function getPreviousAttemptId($exeId) |
||
936 | { |
||
937 | $tblTrackEExercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES); |
||
938 | $exeId = (int) $exeId; |
||
939 | $sql = "SELECT * FROM $tblTrackEExercise |
||
940 | WHERE exe_id = ".$exeId; |
||
941 | $res = Database::query($sql); |
||
942 | |||
943 | if (empty(Database::num_rows($res))) { |
||
944 | // if we cannot find the exe_id |
||
945 | return 0; |
||
946 | } |
||
947 | |||
948 | $data = Database::fetch_assoc($res); |
||
949 | $courseCode = $data['c_id']; |
||
950 | $exerciseId = $data['exe_exo_id']; |
||
951 | $userId = $data['exe_user_id']; |
||
952 | $attemptDate = $data['exe_date']; |
||
953 | |||
954 | if ($attemptDate == '0000-00-00 00:00:00') { |
||
955 | // incomplete attempt, close it before continue |
||
956 | return 0; |
||
957 | } |
||
958 | |||
959 | // look for previous attempt |
||
960 | $exerciseId = (int) $exerciseId; |
||
961 | $userId = (int) $userId; |
||
962 | $sql = "SELECT * |
||
963 | FROM $tblTrackEExercise |
||
964 | WHERE c_id = '$courseCode' |
||
965 | AND exe_exo_id = $exerciseId |
||
966 | AND exe_user_id = $userId |
||
967 | AND status = '' |
||
968 | AND exe_date > '0000-00-00 00:00:00' |
||
969 | AND exe_date < '$attemptDate' |
||
970 | ORDER BY exe_date DESC"; |
||
971 | |||
972 | $res = Database::query($sql); |
||
973 | |||
974 | if (Database::num_rows($res) == 0) { |
||
975 | // no previous attempt |
||
976 | return 0; |
||
977 | } |
||
978 | |||
979 | $data = Database::fetch_assoc($res); |
||
980 | |||
981 | return $data['exe_id']; |
||
982 | } |
||
983 | |||
984 | /** |
||
985 | * return an array of number of answer color for exe attempt |
||
986 | * for question type = MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY |
||
987 | * e.g. |
||
988 | * [LEVEL_DARKGREEN => 3, LEVEL_LIGHTGREEN => 0, LEVEL_WHITE => 5, LEVEL_LIGHTRED => 12, LEVEL_DARKTRED => 0]. |
||
989 | * |
||
990 | * @param $exeId |
||
991 | * |
||
992 | * @return array |
||
993 | */ |
||
994 | public static function getColorNumberListForAttempt($exeId) |
||
1018 | } |
||
1019 | |||
1020 | /** |
||
1021 | * return an array of number of color for question type = MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY |
||
1022 | * for each question category. |
||
1023 | * |
||
1024 | * e.g. |
||
1025 | * [ |
||
1026 | * (categoryId=)5 => [LEVEL_DARKGREEN => 3, LEVEL_WHITE => 5, LEVEL_LIGHTRED => 12] |
||
1027 | * (categoryId=)2 => [LEVEL_DARKGREEN => 8, LEVEL_LIGHTRED => 2, LEVEL_DARKTRED => 8] |
||
1028 | * (categoryId=)0 => [LEVEL_DARKGREEN => 1, |
||
1029 | * LEVEL_LIGHTGREEN => 2, |
||
1030 | * LEVEL_WHITE => 6, |
||
1031 | * LEVEL_LIGHTRED => 1, |
||
1032 | * LEVEL_DARKTRED => 9] |
||
1033 | * ] |
||
1034 | * |
||
1035 | * @param int $exeId |
||
1036 | * |
||
1037 | * @return array |
||
1038 | */ |
||
1039 | public static function getColorNumberListForAttemptByCategory($exeId) |
||
1040 | { |
||
1041 | $result = []; |
||
1042 | $attemptInfoList = self::getExerciseAttemptInfo($exeId); |
||
1043 | |||
1044 | foreach ($attemptInfoList as $attemptInfo) { |
||
1045 | $oQuestion = new MultipleAnswerTrueFalseDegreeCertainty(); |
||
1046 | $oQuestion->read($attemptInfo['question_id']); |
||
1047 | if ($oQuestion->type == MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY) { |
||
1048 | $questionCategory = Testcategory::getCategoryForQuestion($attemptInfo['question_id']); |
||
1049 | |||
1050 | if (!array_key_exists($questionCategory, $result)) { |
||
1051 | $result[$questionCategory] = []; |
||
1052 | } |
||
1053 | |||
1054 | $answerColor = self::getAnswerColor($exeId, $attemptInfo['question_id'], $attemptInfo['position']); |
||
1055 | if ($answerColor && isset($result[$questionCategory])) { |
||
1056 | if (!isset($result[$questionCategory][$answerColor])) { |
||
1057 | $result[$questionCategory][$answerColor] = 0; |
||
1058 | } |
||
1059 | $result[$questionCategory][$answerColor]++; |
||
1060 | } |
||
1061 | } |
||
1062 | } |
||
1063 | |||
1064 | return $result; |
||
1065 | } |
||
1066 | |||
1067 | /** |
||
1068 | * Return true if answer of $exeId, $questionId, $position is correct, otherwise return false. |
||
1069 | * |
||
1070 | * @param $exeId |
||
1071 | * @param $questionId |
||
1072 | * @param $position |
||
1073 | * |
||
1074 | * @return int |
||
1075 | */ |
||
1076 | public static function getAnswerColor($exeId, $questionId, $position) |
||
1134 | } |
||
1135 | } |
||
1136 | } |
||
1137 | |||
1138 | /** |
||
1139 | * Return the position of certitude %age choose by student. |
||
1140 | * |
||
1141 | * @param $optionId |
||
1142 | * |
||
1143 | * @return int |
||
1144 | */ |
||
1145 | public static function getPercentagePosition($optionId) |
||
1146 | { |
||
1147 | $tblAnswerOption = Database::get_course_table(TABLE_QUIZ_QUESTION_OPTION); |
||
1148 | $optionId = (int) $optionId; |
||
1149 | $sql = "SELECT position |
||
1150 | FROM $tblAnswerOption |
||
1151 | WHERE iid = $optionId"; |
||
1152 | $res = Database::query($sql); |
||
1153 | |||
1154 | if (Database::num_rows($res) == 0) { |
||
1155 | return 0; |
||
1156 | } |
||
1157 | |||
1158 | $data = Database::fetch_assoc($res); |
||
1159 | |||
1160 | return $data['position']; |
||
1161 | } |
||
1162 | |||
1163 | /** |
||
1164 | * return the correct id from c_quiz_question_option for question idAuto. |
||
1165 | * |
||
1166 | * @param $idAuto |
||
1167 | * |
||
1168 | * @return int |
||
1169 | */ |
||
1170 | public static function getCorrectAnswerOptionId($idAuto) |
||
1171 | { |
||
1172 | $tblAnswer = Database::get_course_table(TABLE_QUIZ_ANSWER); |
||
1173 | $idAuto = (int) $idAuto; |
||
1174 | $sql = "SELECT correct FROM $tblAnswer |
||
1175 | WHERE id_auto = $idAuto"; |
||
1176 | |||
1177 | $res = Database::query($sql); |
||
1178 | $data = Database::fetch_assoc($res); |
||
1179 | if (Database::num_rows($res) > 0) { |
||
1180 | return $data['correct']; |
||
1181 | } else { |
||
1182 | return 0; |
||
1183 | } |
||
1184 | } |
||
1185 | |||
1186 | /** |
||
1187 | * return an array of exe info from track_e_attempt. |
||
1188 | * |
||
1189 | * @param int $exeId |
||
1190 | * @param int $questionId |
||
1191 | * @param int $position |
||
1192 | * |
||
1193 | * @return array |
||
1194 | */ |
||
1195 | public static function getExerciseAttemptInfo($exeId, $questionId = -1, $position = -1) |
||
1196 | { |
||
1197 | $result = []; |
||
1198 | $and = ''; |
||
1199 | $questionId = (int) $questionId; |
||
1200 | $position = (int) $position; |
||
1201 | $exeId = (int) $exeId; |
||
1202 | |||
1203 | if ($questionId >= 0) { |
||
1204 | $and .= " AND question_id = $questionId"; |
||
1205 | } |
||
1206 | if ($position >= 0) { |
||
1207 | $and .= " AND position = $position"; |
||
1208 | } |
||
1209 | |||
1210 | $tblExeAttempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT); |
||
1211 | $cId = api_get_course_int_id(); |
||
1212 | $sql = "SELECT * FROM $tblExeAttempt |
||
1213 | WHERE c_id = $cId AND exe_id = $exeId $and"; |
||
1214 | |||
1215 | $res = Database::query($sql); |
||
1216 | while ($data = Database::fetch_assoc($res)) { |
||
1217 | $result[] = $data; |
||
1218 | } |
||
1219 | |||
1220 | return $result; |
||
1221 | } |
||
1222 | |||
1223 | /** |
||
1224 | * @param int $exeId |
||
1225 | * |
||
1226 | * @return int |
||
1227 | */ |
||
1228 | public static function getNumberOfQuestionsForExeId($exeId) |
||
1229 | { |
||
1230 | $tableTrackEExercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES); |
||
1231 | $exeId = (int) $exeId; |
||
1232 | |||
1233 | $sql = "SELECT exe_exo_id |
||
1234 | FROM $tableTrackEExercise |
||
1235 | WHERE exe_id = ".$exeId; |
||
1236 | $res = Database::query($sql); |
||
1237 | $data = Database::fetch_assoc($res); |
||
1238 | if ($data) { |
||
|
|||
1239 | $exerciseId = $data['exe_exo_id']; |
||
1240 | |||
1241 | $objectExercise = new Exercise(); |
||
1242 | $objectExercise->read($exerciseId); |
||
1243 | |||
1244 | return $objectExercise->getQuestionCount(); |
||
1245 | } |
||
1246 | |||
1247 | return 0; |
||
1248 | } |
||
1249 | |||
1250 | /** |
||
1251 | * Display student chart results for these question types. |
||
1252 | * |
||
1253 | * @param int $exeId |
||
1254 | * @param Exercise $objExercice |
||
1255 | * |
||
1256 | * @return string |
||
1257 | */ |
||
1258 | public static function displayStudentsChartResults($exeId, $objExercice) |
||
1299 | } |
||
1300 | |||
1301 | /** |
||
1302 | * send mail to student with degre certainty result test. |
||
1303 | * |
||
1304 | * @param int $userId |
||
1305 | * @param Exercise $objExercise |
||
1306 | * @param int $exeId |
||
1307 | */ |
||
1308 | public static function sendQuestionCertaintyNotification($userId, $objExercise, $exeId) |
||
1339 | } |
||
1340 | } |
||
1341 |
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.