1
|
|
|
<?php |
2
|
|
|
|
3
|
|
|
/* For licensing terms, see /license.txt */ |
4
|
|
|
|
5
|
|
|
use ChamiloSession as Session; |
6
|
|
|
|
7
|
|
|
/** |
8
|
|
|
* Class MultipleAnswerTrueFalseDegreeCertainty |
9
|
|
|
* This class allows to instantiate an object of type MULTIPLE_ANSWER |
10
|
|
|
* (MULTIPLE CHOICE, MULTIPLE ANSWER), extending the class question. |
11
|
|
|
*/ |
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) |
241
|
|
|
{ |
242
|
|
|
$questionWeighting = 0; |
243
|
|
|
$objAnswer = new Answer($this->iid); |
244
|
|
|
$nbAnswers = $form->getSubmitValue('nb_answers'); |
245
|
|
|
$courseId = api_get_course_int_id(); |
246
|
|
|
$correct = []; |
247
|
|
|
$options = Question::readQuestionOption($this->iid, $courseId); |
248
|
|
|
|
249
|
|
|
if (!empty($options)) { |
250
|
|
|
foreach ($options as $optionData) { |
251
|
|
|
$id = $optionData['iid']; |
252
|
|
|
unset($optionData['iid']); |
253
|
|
|
Question::updateQuestionOption($id, $optionData, $courseId); |
254
|
|
|
} |
255
|
|
|
} else { |
256
|
|
|
for ($i = 1; $i <= 8; $i++) { |
257
|
|
|
$lastId = Question::saveQuestionOption($this->iid, $this->options[$i], $courseId, $i); |
258
|
|
|
$correct[$i] = $lastId; |
259
|
|
|
} |
260
|
|
|
} |
261
|
|
|
|
262
|
|
|
/* Getting quiz_question_options (true, false, doubt) because |
263
|
|
|
it's possible that there are more options in the future */ |
264
|
|
|
$newOptions = Question::readQuestionOption($this->iid, $courseId); |
265
|
|
|
$sortedByPosition = []; |
266
|
|
|
foreach ($newOptions as $item) { |
267
|
|
|
$sortedByPosition[$item['position']] = $item; |
268
|
|
|
} |
269
|
|
|
|
270
|
|
|
/* Saving quiz_question.extra values that has the correct scores of |
271
|
|
|
the true, false, doubt options registered in this format |
272
|
|
|
XX:YY:ZZZ where XX is a float score value. */ |
273
|
|
|
$extraValues = []; |
274
|
|
|
for ($i = 1; $i <= 3; $i++) { |
275
|
|
|
$score = trim($form->getSubmitValue('option['.$i.']')); |
276
|
|
|
$extraValues[] = $score; |
277
|
|
|
} |
278
|
|
|
$this->setExtra(implode(':', $extraValues)); |
279
|
|
|
|
280
|
|
|
for ($i = 1; $i <= $nbAnswers; $i++) { |
281
|
|
|
$answer = trim($form->getSubmitValue('answer['.$i.']')); |
282
|
|
|
$comment = trim($form->getSubmitValue('comment['.$i.']')); |
283
|
|
|
$goodAnswer = trim($form->getSubmitValue('correct['.$i.']')); |
284
|
|
|
if (empty($options)) { |
285
|
|
|
// If this is the first time that the question is created then change |
286
|
|
|
// the default values from the form 1 and 2 by the correct "option id" registered |
287
|
|
|
$goodAnswer = $sortedByPosition[$goodAnswer]['iid']; |
288
|
|
|
} |
289
|
|
|
$questionWeighting += $extraValues[0]; //By default 0 has the correct answers |
290
|
|
|
$objAnswer->createAnswer($answer, $goodAnswer, $comment, '', $i); |
291
|
|
|
} |
292
|
|
|
|
293
|
|
|
// saves the answers into the data base |
294
|
|
|
$objAnswer->save(); |
295
|
|
|
|
296
|
|
|
// sets the total weighting of the question |
297
|
|
|
$this->updateWeighting($questionWeighting); |
298
|
|
|
$this->save($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( |
757
|
|
|
$scoreList, |
758
|
|
|
$widthTable, |
759
|
|
|
$title = '', |
760
|
|
|
$sizeRatio = 1, |
761
|
|
|
$minHeight = 0, |
762
|
|
|
$displayExplanationText = true, |
763
|
|
|
$returnHeight = false, |
764
|
|
|
$groupCategoriesByBracket = false, |
765
|
|
|
$numberOfQuestions = 0 |
766
|
|
|
) { |
767
|
|
|
$topAndBottomMargin = 10; |
768
|
|
|
$colorList = [ |
769
|
|
|
self::LEVEL_DARKRED, |
770
|
|
|
self::LEVEL_LIGHTRED, |
771
|
|
|
self::LEVEL_WHITE, |
772
|
|
|
self::LEVEL_LIGHTGREEN, |
773
|
|
|
self::LEVEL_DARKGREEN, |
774
|
|
|
]; |
775
|
|
|
|
776
|
|
|
// get total attempt number |
777
|
|
|
$highterColorHeight = 0; |
778
|
|
|
foreach ($scoreList as $color => $number) { |
779
|
|
|
if ($number > $highterColorHeight) { |
780
|
|
|
$highterColorHeight = $number; |
781
|
|
|
} |
782
|
|
|
} |
783
|
|
|
|
784
|
|
|
$totalAttemptNumber = $numberOfQuestions; |
785
|
|
|
$verticalLineHeight = $highterColorHeight * $sizeRatio * 2 + 122 + $topAndBottomMargin * 2; |
786
|
|
|
if ($verticalLineHeight < $minHeight) { |
787
|
|
|
$minHeightCorrection = $minHeight - $verticalLineHeight; |
788
|
|
|
$verticalLineHeight += $minHeightCorrection; |
789
|
|
|
} |
790
|
|
|
|
791
|
|
|
// draw chart |
792
|
|
|
$html = ''; |
793
|
|
|
|
794
|
|
|
if ($groupCategoriesByBracket) { |
795
|
|
|
$title = api_preg_replace("/[^]]*$/", '', $title); |
796
|
|
|
$title = ucfirst(api_preg_replace("/[\[\]]/", '', $title)); |
797
|
|
|
} |
798
|
|
|
|
799
|
|
|
$textSize = 80; |
800
|
|
|
|
801
|
|
|
$classGlobalChart = ''; |
802
|
|
|
if ($displayExplanationText) { |
803
|
|
|
// global chart |
804
|
|
|
$classGlobalChart = 'globalChart'; |
805
|
|
|
} |
806
|
|
|
|
807
|
|
|
$html .= '<table class="certaintyTable" style="height :'.$verticalLineHeight.'px; margin-bottom: 10px;" >'; |
808
|
|
|
$html .= '<tr><th colspan="5" class="'.$classGlobalChart.'">' |
809
|
|
|
.$title |
810
|
|
|
.'</th><tr>' |
811
|
|
|
; |
812
|
|
|
|
813
|
|
|
$nbResponsesInc = 0; |
814
|
|
|
if (isset($scoreList[4])) { |
815
|
|
|
$nbResponsesInc += (int) $scoreList[4]; |
816
|
|
|
} |
817
|
|
|
if (isset($scoreList[5])) { |
818
|
|
|
$nbResponsesInc += (int) $scoreList[5]; |
819
|
|
|
} |
820
|
|
|
|
821
|
|
|
$nbResponsesIng = isset($scoreList[3]) ? $scoreList[3] : 0; |
822
|
|
|
|
823
|
|
|
$nbResponsesCor = 0; |
824
|
|
|
if (isset($scoreList[1])) { |
825
|
|
|
$nbResponsesCor += (int) $scoreList[1]; |
826
|
|
|
} |
827
|
|
|
if (isset($scoreList[2])) { |
828
|
|
|
$nbResponsesCor += (int) $scoreList[2]; |
829
|
|
|
} |
830
|
|
|
|
831
|
|
|
$colWidth = $widthTable / 5; |
832
|
|
|
|
833
|
|
|
$html .= '<tr> |
834
|
|
|
<td class="firstLine borderRight '.$classGlobalChart.'" |
835
|
|
|
colspan="2" |
836
|
|
|
style="width:'.($colWidth * 2).'px; line-height: 15px; font-size:'.$textSize.'%;">'. |
837
|
|
|
sprintf(get_lang('IncorrectAnswersX'), $nbResponsesInc).' |
838
|
|
|
</td> |
839
|
|
|
<td class="firstLine borderRight '.$classGlobalChart.'" |
840
|
|
|
style="width:'.$colWidth.'px; line-height: 15px; font-size :'.$textSize.'%;">'. |
841
|
|
|
sprintf(get_lang('IgnoranceAnswersX'), $nbResponsesIng).' |
842
|
|
|
</td> |
843
|
|
|
<td class="firstLine '.$classGlobalChart.'" |
844
|
|
|
colspan="2" |
845
|
|
|
style="width:'.($colWidth * 2).'px; line-height: 15px; font-size:'.$textSize.'%;">'. |
846
|
|
|
sprintf(get_lang('CorrectAnswersX'), $nbResponsesCor).' |
847
|
|
|
</td> |
848
|
|
|
</tr>'; |
849
|
|
|
$html .= '<tr>'; |
850
|
|
|
|
851
|
|
|
foreach ($colorList as $i => $color) { |
852
|
|
|
if (array_key_exists($color, $scoreList)) { |
853
|
|
|
$scoreOnBottom = $scoreList[$color]; // height of the colored area on the bottom |
854
|
|
|
} else { |
855
|
|
|
$scoreOnBottom = 0; |
856
|
|
|
} |
857
|
|
|
$sizeOnBottom = $scoreOnBottom * $sizeRatio * 2; |
858
|
|
|
if ($i == 1 || $i == 2) { |
859
|
|
|
$html .= '<td width="' |
860
|
|
|
.$colWidth |
861
|
|
|
.'px" style="border-right: 1px dotted #7FC5FF; vertical-align: bottom;font-size: ' |
862
|
|
|
.$textSize |
863
|
|
|
.'%;">' |
864
|
|
|
; |
865
|
|
|
} else { |
866
|
|
|
$html .= '<td width="' |
867
|
|
|
.$colWidth |
868
|
|
|
.'px" style="vertical-align: bottom;font-size: ' |
869
|
|
|
.$textSize |
870
|
|
|
.'%;">' |
871
|
|
|
; |
872
|
|
|
} |
873
|
|
|
$html .= '<div class="certaint-score">' |
874
|
|
|
.$scoreOnBottom |
875
|
|
|
.'</div><div class="levelbar_' |
876
|
|
|
.$color |
877
|
|
|
.'" style="height: ' |
878
|
|
|
.$sizeOnBottom |
879
|
|
|
.'px;"> </div>' |
880
|
|
|
; |
881
|
|
|
$html .= '</td>'; |
882
|
|
|
} |
883
|
|
|
|
884
|
|
|
$html .= '</tr>'; |
885
|
|
|
|
886
|
|
|
if ($displayExplanationText) { |
887
|
|
|
// Display of histogram text |
888
|
|
|
$explainHistoList = [ |
889
|
|
|
'DegreeOfCertaintyVeryUnsure', |
890
|
|
|
'DegreeOfCertaintyUnsure', |
891
|
|
|
'DegreeOfCertaintyDeclaredIgnorance', |
892
|
|
|
'DegreeOfCertaintyPrettySure', |
893
|
|
|
'DegreeOfCertaintyVerySure', |
894
|
|
|
]; |
895
|
|
|
$html .= '<tr>'; |
896
|
|
|
$i = 0; |
897
|
|
|
foreach ($explainHistoList as $explain) { |
898
|
|
|
if ($i == 1 || $i == 2) { |
899
|
|
|
$class = 'borderRight'; |
900
|
|
|
} else { |
901
|
|
|
$class = ''; |
902
|
|
|
} |
903
|
|
|
$html .= '<td class="firstLine ' |
904
|
|
|
.$class |
905
|
|
|
.' ' |
906
|
|
|
.$classGlobalChart |
907
|
|
|
.'" style="width="' |
908
|
|
|
.$colWidth |
909
|
|
|
.'px; font-size:' |
910
|
|
|
.$textSize |
911
|
|
|
.'%;">' |
912
|
|
|
; |
913
|
|
|
$html .= get_lang($explain); |
914
|
|
|
$html .= '</td>'; |
915
|
|
|
$i++; |
916
|
|
|
} |
917
|
|
|
$html .= '</tr>'; |
918
|
|
|
} |
919
|
|
|
$html .= '</table></center>'; |
920
|
|
|
|
921
|
|
|
if ($returnHeight) { |
922
|
|
|
return [$html, $verticalLineHeight]; |
923
|
|
|
} else { |
924
|
|
|
return $html; |
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) |
995
|
|
|
{ |
996
|
|
|
$result = [ |
997
|
|
|
self::LEVEL_DARKGREEN => 0, |
998
|
|
|
self::LEVEL_LIGHTGREEN => 0, |
999
|
|
|
self::LEVEL_WHITE => 0, |
1000
|
|
|
self::LEVEL_LIGHTRED => 0, |
1001
|
|
|
self::LEVEL_DARKRED => 0, |
1002
|
|
|
]; |
1003
|
|
|
|
1004
|
|
|
$attemptInfoList = self::getExerciseAttemptInfo($exeId); |
1005
|
|
|
|
1006
|
|
|
foreach ($attemptInfoList as $attemptInfo) { |
1007
|
|
|
$oQuestion = new MultipleAnswerTrueFalseDegreeCertainty(); |
1008
|
|
|
$oQuestion->read($attemptInfo['question_id']); |
1009
|
|
|
if ($oQuestion->type == MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY) { |
1010
|
|
|
$answerColor = self::getAnswerColor($exeId, $attemptInfo['question_id'], $attemptInfo['position']); |
1011
|
|
|
if ($answerColor) { |
1012
|
|
|
$result[$answerColor]++; |
1013
|
|
|
} |
1014
|
|
|
} |
1015
|
|
|
} |
1016
|
|
|
|
1017
|
|
|
return $result; |
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) |
1077
|
|
|
{ |
1078
|
|
|
$attemptInfoList = self::getExerciseAttemptInfo($exeId, $questionId, $position); |
1079
|
|
|
|
1080
|
|
|
if (count($attemptInfoList) != 1) { |
1081
|
|
|
// havent got the answer |
1082
|
|
|
return 0; |
1083
|
|
|
} |
1084
|
|
|
|
1085
|
|
|
$answerCodes = $attemptInfoList[0]['answer']; |
1086
|
|
|
|
1087
|
|
|
// student answer |
1088
|
|
|
$splitAnswer = preg_split("/:/", $answerCodes); |
1089
|
|
|
// get correct answer option id |
1090
|
|
|
$correctAnswerOptionId = self::getCorrectAnswerOptionId($splitAnswer[0]); |
1091
|
|
|
if ($correctAnswerOptionId == 0) { |
1092
|
|
|
// error returning the correct answer option id |
1093
|
|
|
return 0; |
1094
|
|
|
} |
1095
|
|
|
|
1096
|
|
|
// get student answer option id |
1097
|
|
|
$studentAnswerOptionId = isset($splitAnswer[1]) ? $splitAnswer[1] : null; |
1098
|
|
|
|
1099
|
|
|
// we got the correct answer option id, let's compare ti with the student answer |
1100
|
|
|
$percentage = null; |
1101
|
|
|
if (isset($splitAnswer[2])) { |
1102
|
|
|
$percentage = self::getPercentagePosition($splitAnswer[2]); |
1103
|
|
|
} |
1104
|
|
|
|
1105
|
|
|
if ($studentAnswerOptionId == $correctAnswerOptionId) { |
1106
|
|
|
// yeah, student got correct answer |
1107
|
|
|
switch ($percentage) { |
1108
|
|
|
case 3: |
1109
|
|
|
return self::LEVEL_WHITE; |
1110
|
|
|
case 4: |
1111
|
|
|
case 5: |
1112
|
|
|
return self::LEVEL_LIGHTGREEN; |
1113
|
|
|
case 6: |
1114
|
|
|
case 7: |
1115
|
|
|
case 8: |
1116
|
|
|
return self::LEVEL_DARKGREEN; |
1117
|
|
|
default: |
1118
|
|
|
return 0; |
1119
|
|
|
} |
1120
|
|
|
} else { |
1121
|
|
|
// bummer, wrong answer dude |
1122
|
|
|
switch ($percentage) { |
1123
|
|
|
case 3: |
1124
|
|
|
return self::LEVEL_WHITE; |
1125
|
|
|
case 4: |
1126
|
|
|
case 5: |
1127
|
|
|
return self::LEVEL_LIGHTRED; |
1128
|
|
|
case 6: |
1129
|
|
|
case 7: |
1130
|
|
|
case 8: |
1131
|
|
|
return self::LEVEL_DARKRED; |
1132
|
|
|
default: |
1133
|
|
|
return 0; |
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) |
1259
|
|
|
{ |
1260
|
|
|
$numberOfQuestions = self::getNumberOfQuestionsForExeId($exeId); |
1261
|
|
|
$globalScoreList = self::getColorNumberListForAttempt($exeId); |
1262
|
|
|
$html = self::displayDegreeChart( |
1263
|
|
|
$globalScoreList, |
1264
|
|
|
600, |
1265
|
|
|
get_lang('YourOverallResultForTheTest'), |
1266
|
|
|
2, |
1267
|
|
|
0, |
1268
|
|
|
true, |
1269
|
|
|
false, |
1270
|
|
|
false, |
1271
|
|
|
$numberOfQuestions |
1272
|
|
|
); |
1273
|
|
|
$html .= '<br/>'; |
1274
|
|
|
|
1275
|
|
|
$previousAttemptId = self::getPreviousAttemptId($exeId); |
1276
|
|
|
if ($previousAttemptId > 0) { |
1277
|
|
|
$previousAttemptScoreList = self::getColorNumberListForAttempt( |
1278
|
|
|
$previousAttemptId |
1279
|
|
|
); |
1280
|
|
|
$html .= self::displayDegreeChart( |
1281
|
|
|
$previousAttemptScoreList, |
1282
|
|
|
600, |
1283
|
|
|
get_lang('ForComparisonYourLastResultToThisTest'), |
1284
|
|
|
2 |
1285
|
|
|
); |
1286
|
|
|
$html .= '<br/>'; |
1287
|
|
|
} |
1288
|
|
|
|
1289
|
|
|
$list = self::getColorNumberListForAttemptByCategory($exeId); |
1290
|
|
|
$html .= self::displayDegreeChartByCategory( |
1291
|
|
|
$list, |
1292
|
|
|
get_lang('YourResultsByDiscipline'), |
1293
|
|
|
1, |
1294
|
|
|
$objExercice |
1295
|
|
|
); |
1296
|
|
|
$html .= '<br/>'; |
1297
|
|
|
|
1298
|
|
|
return $html; |
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) |
1309
|
|
|
{ |
1310
|
|
|
$userInfo = api_get_user_info($userId); |
1311
|
|
|
$recipientName = api_get_person_name($userInfo['firstname'], |
1312
|
|
|
$userInfo['lastname'], |
1313
|
|
|
null, |
1314
|
|
|
PERSON_NAME_EMAIL_ADDRESS |
1315
|
|
|
); |
1316
|
|
|
$subject = "[".get_lang('DoNotReply')."] " |
1317
|
|
|
.html_entity_decode(get_lang('ResultAccomplishedTest')." \"".$objExercise->title."\""); |
1318
|
|
|
|
1319
|
|
|
// message sended to the student |
1320
|
|
|
$message = get_lang('Dear').' '.$recipientName.",<br /><br />"; |
1321
|
|
|
$exerciseLink = "<a href='".api_get_path(WEB_CODE_PATH)."/exercise/result.php?show_headers=1&" |
1322
|
|
|
.api_get_cidreq() |
1323
|
|
|
."&id=$exeId'>"; |
1324
|
|
|
$exerciseTitle = $objExercise->title; |
1325
|
|
|
|
1326
|
|
|
$message .= sprintf( |
1327
|
|
|
get_lang('MessageQuestionCertainty'), |
1328
|
|
|
$exerciseTitle, |
1329
|
|
|
api_get_path(WEB_PATH), |
1330
|
|
|
$exerciseLink |
1331
|
|
|
); |
1332
|
|
|
|
1333
|
|
|
// show histogram |
1334
|
|
|
$message .= self::displayStudentsChartResults($exeId, $objExercise); |
1335
|
|
|
$message .= get_lang('KindRegards'); |
1336
|
|
|
$message = api_preg_replace("/\\\n/", '', $message); |
1337
|
|
|
|
1338
|
|
|
MessageManager::send_message_simple($userId, $subject, $message); |
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.