|
|
|
|
<?php
|
|
|
|
|
/* For licensing terms, see /license.txt */
|
|
|
|
|
|
|
|
|
|
use ChamiloSession as Session;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Class MultipleAnswerTrueFalseDegreeCertainty
|
|
|
|
|
* This class allows to instantiate an object of type MULTIPLE_ANSWER
|
|
|
|
|
* (MULTIPLE CHOICE, MULTIPLE ANSWER), extending the class question.
|
|
|
|
|
*
|
|
|
|
|
* @package chamilo.exercise
|
|
|
|
|
*/
|
|
|
|
|
class MultipleAnswerTrueFalseDegreeCertainty extends Question
|
|
|
|
|
{
|
|
|
|
|
const LEVEL_DARKGREEN = 1;
|
|
|
|
|
const LEVEL_LIGHTGREEN = 2;
|
|
|
|
|
const LEVEL_WHITE = 3;
|
|
|
|
|
const LEVEL_LIGHTRED = 4;
|
|
|
|
|
const LEVEL_DARKRED = 5;
|
|
|
|
|
|
|
|
|
|
public static $typePicture = 'mccert.png';
|
|
|
|
|
public static $explanationLangVar = 'MultipleAnswerTrueFalseDegreeCertainty';
|
|
|
|
|
public $optionsTitle;
|
|
|
|
|
public $options;
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Constructor.
|
|
|
|
|
*/
|
|
|
|
|
public function __construct()
|
|
|
|
|
{
|
|
|
|
|
parent::__construct();
|
|
|
|
|
$this->type = MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY;
|
|
|
|
|
$this->isContent = $this->getIsContent();
|
|
|
|
|
$this->optionsTitle = [1 => 'Answers', 2 => 'DegreeOfCertaintyThatMyAnswerIsCorrect'];
|
|
|
|
|
$this->options = [
|
|
|
|
|
1 => 'True',
|
|
|
|
|
2 => 'False',
|
|
|
|
|
3 => '50%',
|
|
|
|
|
4 => '60%',
|
|
|
|
|
5 => '70%',
|
|
|
|
|
6 => '80%',
|
|
|
|
|
7 => '90%',
|
|
|
|
|
8 => '100%',
|
|
|
|
|
];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Redefines Question::createAnswersForm: creates the HTML form to answer the question.
|
|
|
|
|
*
|
|
|
|
|
* @uses \globals $text and $class, defined in the calling script
|
|
|
|
|
*
|
|
|
|
|
* @param FormValidator $form
|
|
|
|
|
*
|
|
|
|
|
* @throws Exception
|
|
|
|
|
* @throws HTML_QuickForm_Error
|
|
|
|
|
*/
|
|
|
|
|
public function createAnswersForm($form)
|
|
|
|
|
{
|
|
|
|
|
global $text;
|
|
|
|
|
$nbAnswers = isset($_POST['nb_answers']) ? (int) $_POST['nb_answers'] : 4;
|
|
|
|
|
// The previous default value was 2. See task #1759.
|
|
|
|
|
$nbAnswers += (isset($_POST['lessAnswers']) ? -1 : (isset($_POST['moreAnswers']) ? 1 : 0));
|
|
|
|
|
|
|
|
|
|
$courseId = api_get_course_int_id();
|
|
|
|
|
$objEx = Session::read('objExercise');
|
|
|
|
|
$renderer = &$form->defaultRenderer();
|
|
|
|
|
$defaults = [];
|
|
|
|
|
|
|
|
|
|
$html = '<table class="data_table"><tr style="text-align: center;"><th>'
|
|
|
|
|
.get_lang('Number')
|
|
|
|
|
.'</th><th>'
|
|
|
|
|
.get_lang('True')
|
|
|
|
|
.'</th><th>'
|
|
|
|
|
.get_lang('False')
|
|
|
|
|
.'</th><th>'
|
|
|
|
|
.get_lang('Answer')
|
|
|
|
|
.'</th>';
|
|
|
|
|
|
|
|
|
|
// show column comment when feedback is enable
|
|
|
|
|
if ($objEx->selectFeedbackType() != EXERCISE_FEEDBACK_TYPE_EXAM) {
|
|
|
|
|
$html .= '<th>'.get_lang('Comment').'</th>';
|
|
|
|
|
}
|
|
|
|
|
$html .= '</tr>';
|
|
|
|
|
$form->addElement('label', get_lang('Answers').'<br /> <img src="../img/fill_field.png">', $html);
|
|
|
|
|
|
|
|
|
|
$correct = 0;
|
|
|
|
|
$answer = null;
|
|
|
|
|
if (!empty($this->id)) {
|
|
|
|
|
$answer = new Answer($this->id);
|
|
|
|
|
$answer->read();
|
|
|
|
|
if (count($answer->nbrAnswers) > 0 && !$form->isSubmitted()) {
|
|
|
|
|
$nbAnswers = $answer->nbrAnswers;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$form->addElement('hidden', 'nb_answers');
|
|
|
|
|
$boxesNames = [];
|
|
|
|
|
|
|
|
|
|
if ($nbAnswers < 1) {
|
|
|
|
|
$nbAnswers = 1;
|
|
|
|
|
echo Display::return_message(get_lang('YouHaveToCreateAtLeastOneAnswer'));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Can be more options
|
|
|
|
|
$optionData = Question::readQuestionOption($this->id, $courseId);
|
|
|
|
|
|
|
|
|
|
for ($i = 1; $i <= $nbAnswers; ++$i) {
|
|
|
|
|
$renderer->setElementTemplate(
|
|
|
|
|
'<td><!-- BEGIN error --><span class="form_error">{error}</span><!-- END error --><br/>{element}</td>',
|
|
|
|
|
'correct['.$i.']'
|
|
|
|
|
);
|
|
|
|
|
$renderer->setElementTemplate(
|
|
|
|
|
'<td><!-- BEGIN error --><span class="form_error">{error}</span><!-- END error --><br/>{element}</td>',
|
|
|
|
|
'counter['.$i.']'
|
|
|
|
|
);
|
|
|
|
|
$renderer->setElementTemplate(
|
|
|
|
|
'<td><!-- BEGIN error --><span class="form_error">{error}</span><!-- END error --><br/>{element}</td>',
|
|
|
|
|
'answer['.$i.']'
|
|
|
|
|
);
|
|
|
|
|
$renderer->setElementTemplate(
|
|
|
|
|
'<td><!-- BEGIN error --><span class="form_error">{error}</span><!-- END error --><br/>{element}</td>',
|
|
|
|
|
'comment['.$i.']'
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
$answerNumber = $form->addElement('text', 'counter['.$i.']', null, 'value="'.$i.'"');
|
|
|
|
|
$answerNumber->freeze();
|
|
|
|
|
|
|
|
|
|
$defaults['answer['.$i.']'] = '';
|
|
|
|
|
$defaults['comment['.$i.']'] = '';
|
|
|
|
|
$defaults['correct['.$i.']'] = '';
|
|
|
|
|
|
|
|
|
|
if (is_object($answer)) {
|
|
|
|
|
$defaults['answer['.$i.']'] = $answer->answer[$i];
|
|
|
|
|
$defaults['comment['.$i.']'] = $answer->comment[$i];
|
|
|
|
|
$defaults['weighting['.$i.']'] = float_format($answer->weighting[$i], 1);
|
|
|
|
|
$correct = $answer->correct[$i];
|
|
|
|
|
$defaults['correct['.$i.']'] = $correct;
|
|
|
|
|
$j = 1;
|
|
|
|
|
if (!empty($optionData)) {
|
|
|
|
|
foreach ($optionData as $id => $data) {
|
|
|
|
|
$form->addElement('radio', 'correct['.$i.']', null, null, $id);
|
|
|
|
|
$j++;
|
|
|
|
|
if ($j == 3) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$form->addElement('radio', 'correct['.$i.']', null, null, 1);
|
|
|
|
|
$form->addElement('radio', 'correct['.$i.']', null, null, 2);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$boxesNames[] = 'correct['.$i.']';
|
|
|
|
|
$form->addElement(
|
|
|
|
|
'html_editor',
|
|
|
|
|
'answer['.$i.']',
|
|
|
|
|
null,
|
|
|
|
|
['style' => 'vertical-align:middle;'],
|
|
|
|
|
['ToolbarSet' => 'TestProposedAnswer', 'Width' => '100%', 'Height' => '100']);
|
|
|
|
|
$form->addRule('answer['.$i.']', get_lang('ThisFieldIsRequired'), 'required');
|
|
|
|
|
|
|
|
|
|
// show comment when feedback is enable
|
|
|
|
|
if ($objEx->selectFeedbackType() != EXERCISE_FEEDBACK_TYPE_EXAM) {
|
|
|
|
|
$form->addElement('html_editor',
|
|
|
|
|
'comment['.$i.']',
|
|
|
|
|
null,
|
|
|
|
|
['style' => 'vertical-align:middle;'],
|
|
|
|
|
['ToolbarSet' => 'TestProposedAnswer', 'Width' => '100%', 'Height' => '100']);
|
|
|
|
|
}
|
|
|
|
|
$form->addElement('html', '</tr>');
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$form->addElement('html', '</table>');
|
|
|
|
|
$form->addElement('html', '<br />');
|
|
|
|
|
|
|
|
|
|
// 3 scores
|
|
|
|
|
$form->addElement('text', 'option[1]', get_lang('Correct'), ['class' => 'span1', 'value' => '1']);
|
|
|
|
|
$form->addElement('text', 'option[2]', get_lang('Wrong'), ['class' => 'span1', 'value' => '-0.5']);
|
|
|
|
|
|
|
|
|
|
$form->addElement('hidden', 'option[3]', 0);
|
|
|
|
|
|
|
|
|
|
$form->addRule('option[1]', get_lang('ThisFieldIsRequired'), 'required');
|
|
|
|
|
$form->addRule('option[2]', get_lang('ThisFieldIsRequired'), 'required');
|
|
|
|
|
|
|
|
|
|
$form->addElement('html', '</tr><table>');
|
|
|
|
|
$form->addElement('hidden', 'options_count', 3);
|
|
|
|
|
$form->addElement('html', '</table><br /><br />');
|
|
|
|
|
|
|
|
|
|
//Extra values True, false, Dont known
|
|
|
|
|
if (!empty($this->extra)) {
|
|
|
|
|
$scores = explode(':', $this->extra);
|
|
|
|
|
if (!empty($scores)) {
|
|
|
|
|
for ($i = 1; $i <= 3; $i++) {
|
|
|
|
|
$defaults['option['.$i.']'] = $scores[$i - 1];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($objEx->edit_exercise_in_lp === true) {
|
|
|
|
|
$form->addElement('submit', 'lessAnswers', get_lang('LessAnswer'), 'class="btn btn-danger minus"');
|
|
|
|
|
$form->addElement('submit', 'moreAnswers', get_lang('PlusAnswer'), 'class="btn btn-primary plus"');
|
|
|
|
|
//$text and $class defined in calling script
|
|
|
|
|
$form->addElement('submit', 'submitQuestion', $text, 'class = "btn btn-primary"');
|
|
|
|
|
}
|
|
|
|
|
$renderer->setElementTemplate('{element} ', 'lessAnswers');
|
|
|
|
|
$renderer->setElementTemplate('{element} ', 'submitQuestion');
|
|
|
|
|
$renderer->setElementTemplate('{element} ', 'moreAnswers');
|
|
|
|
|
$form->addElement('html', '</div></div>');
|
|
|
|
|
$defaults['correct'] = $correct;
|
|
|
|
|
|
|
|
|
|
if (!empty($this->id)) {
|
|
|
|
|
$form->setDefaults($defaults);
|
|
|
|
|
} else {
|
|
|
|
|
$form->setDefaults($defaults);
|
|
|
|
|
}
|
|
|
|
|
$form->setConstants(['nb_answers' => $nbAnswers]);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* abstract function which creates the form to create / edit the answers of the question.
|
|
|
|
|
*
|
|
|
|
|
* @param FormValidator $form
|
|
|
|
|
* @param Exercise $exercise
|
|
|
|
|
*/
|
|
|
|
|
public function processAnswersCreation($form, $exercise)
|
|
|
|
|
{
|
|
|
|
|
$questionWeighting = 0;
|
|
|
|
|
$objAnswer = new Answer($this->id);
|
|
|
|
|
$nbAnswers = $form->getSubmitValue('nb_answers');
|
|
|
|
|
$courseId = api_get_course_int_id();
|
|
|
|
|
$correct = [];
|
|
|
|
|
$options = Question::readQuestionOption($this->id, $courseId);
|
|
|
|
|
|
|
|
|
|
if (!empty($options)) {
|
|
|
|
|
foreach ($options as $optionData) {
|
|
|
|
|
$id = $optionData['id'];
|
|
|
|
|
unset($optionData['id']);
|
|
|
|
|
Question::updateQuestionOption($id, $optionData, $courseId);
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
for ($i = 1; $i <= 8; $i++) {
|
|
|
|
|
$lastId = Question::saveQuestionOption($this->id, $this->options[$i], $courseId, $i);
|
|
|
|
|
$correct[$i] = $lastId;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Getting quiz_question_options (true, false, doubt) because
|
|
|
|
|
it's possible that there are more options in the future */
|
|
|
|
|
$newOptions = Question::readQuestionOption($this->id, $courseId);
|
|
|
|
|
$sortedByPosition = [];
|
|
|
|
|
foreach ($newOptions as $item) {
|
|
|
|
|
$sortedByPosition[$item['position']] = $item;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* Saving quiz_question.extra values that has the correct scores of
|
|
|
|
|
the true, false, doubt options registered in this format
|
|
|
|
|
XX:YY:ZZZ where XX is a float score value. */
|
|
|
|
|
$extraValues = [];
|
|
|
|
|
for ($i = 1; $i <= 3; $i++) {
|
|
|
|
|
$score = trim($form->getSubmitValue('option['.$i.']'));
|
|
|
|
|
$extraValues[] = $score;
|
|
|
|
|
}
|
|
|
|
|
$this->setExtra(implode(':', $extraValues));
|
|
|
|
|
|
|
|
|
|
for ($i = 1; $i <= $nbAnswers; $i++) {
|
|
|
|
|
$answer = trim($form->getSubmitValue('answer['.$i.']'));
|
|
|
|
|
$comment = trim($form->getSubmitValue('comment['.$i.']'));
|
|
|
|
|
$goodAnswer = trim($form->getSubmitValue('correct['.$i.']'));
|
|
|
|
|
if (empty($options)) {
|
|
|
|
|
// If this is the first time that the question is created then change
|
|
|
|
|
// the default values from the form 1 and 2 by the correct "option id" registered
|
|
|
|
|
$goodAnswer = $sortedByPosition[$goodAnswer]['id'];
|
|
|
|
|
}
|
|
|
|
|
$questionWeighting += $extraValues[0]; //By default 0 has the correct answers
|
|
|
|
|
$objAnswer->createAnswer($answer, $goodAnswer, $comment, '', $i);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// saves the answers into the data base
|
|
|
|
|
$objAnswer->save();
|
|
|
|
|
|
|
|
|
|
// sets the total weighting of the question
|
|
|
|
|
$this->updateWeighting($questionWeighting);
|
|
|
|
|
$this->save($exercise);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Show result table headers.
|
|
|
|
|
*
|
|
|
|
|
* @param Exercise $exercise
|
|
|
|
|
* @param int $counter
|
|
|
|
|
* @param float $score
|
|
|
|
|
*
|
|
|
|
|
* @return null|string
|
|
|
|
|
*/
|
|
|
|
|
public function return_header($exercise, $counter = null, $score = null)
|
|
|
|
|
{
|
|
|
|
|
$header = parent::return_header($exercise, $counter, $score);
|
|
|
|
|
$header .= '<table class="'
|
|
|
|
|
.$this->question_table_class
|
|
|
|
|
.'"><tr><th>'
|
|
|
|
|
.get_lang('Choice')
|
|
|
|
|
.'</th><th>'
|
|
|
|
|
.get_lang('ExpectedChoice')
|
|
|
|
|
.'</th><th>'
|
|
|
|
|
.get_lang('Answer')
|
|
|
|
|
.'</th><th colspan="2" style="text-align:center;">'
|
|
|
|
|
.get_lang('YourDegreeOfCertainty')
|
|
|
|
|
.'</th>'
|
|
|
|
|
;
|
|
|
|
|
if ($exercise->feedback_type != EXERCISE_FEEDBACK_TYPE_EXAM) {
|
|
|
|
|
$header .= '<th>'.get_lang('Comment').'</th>';
|
|
|
|
|
} else {
|
|
|
|
|
$header .= '<th> </th>';
|
|
|
|
|
}
|
|
|
|
|
$header .= '</tr>';
|
|
|
|
|
|
|
|
|
|
return $header;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Get color code, status, label and description for the current answer.
|
|
|
|
|
*
|
|
|
|
|
* @param string $studentAnswer
|
|
|
|
|
* @param string $expectedAnswer
|
|
|
|
|
* @param int $studentDegreeChoicePosition
|
|
|
|
|
*
|
|
|
|
|
* @return array An array with indexes 'color', 'background-color', 'status', 'label' and 'description'
|
|
|
|
|
*/
|
|
|
|
|
public function getResponseDegreeInfo($studentAnswer, $expectedAnswer, $studentDegreeChoicePosition)
|
|
|
|
|
{
|
|
|
|
|
$result = [];
|
|
|
|
|
if ($studentDegreeChoicePosition == 3) {
|
|
|
|
|
$result = [
|
|
|
|
|
'color' => '#000000',
|
|
|
|
|
'background-color' => '#FFFFFF',
|
|
|
|
|
'status' => self::LEVEL_WHITE,
|
|
|
|
|
'label' => get_lang('DegreeOfCertaintyDeclaredIgnorance'),
|
|
|
|
|
'description' => get_lang('DegreeOfCertaintyDeclaredIgnoranceDescription'),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
|
} else {
|
|
|
|
|
$checkResult = $studentAnswer == $expectedAnswer ? true : false;
|
|
|
|
|
if ($checkResult) {
|
|
|
|
|
if ($studentDegreeChoicePosition >= 6) {
|
|
|
|
|
$result = [
|
|
|
|
|
'color' => '#FFFFFF',
|
|
|
|
|
'background-color' => '#088A08',
|
|
|
|
|
'status' => self::LEVEL_DARKGREEN,
|
|
|
|
|
'label' => get_lang('DegreeOfCertaintyVerySure'),
|
|
|
|
|
'description' => get_lang('DegreeOfCertaintyVerySureDescription'),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
|
}
|
|
|
|
|
if ($studentDegreeChoicePosition >= 4 && $studentDegreeChoicePosition <= 5) {
|
|
|
|
|
$result = [
|
|
|
|
|
'color' => '#000000',
|
|
|
|
|
'background-color' => '#A9F5A9',
|
|
|
|
|
'status' => self::LEVEL_LIGHTGREEN,
|
|
|
|
|
'label' => get_lang('DegreeOfCertaintyPrettySure'),
|
|
|
|
|
'description' => get_lang('DegreeOfCertaintyPrettySureDescription'),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
if ($studentDegreeChoicePosition >= 6) {
|
|
|
|
|
$result = [
|
|
|
|
|
'color' => '#FFFFFF',
|
|
|
|
|
'background-color' => '#FE2E2E',
|
|
|
|
|
'status' => self::LEVEL_DARKRED,
|
|
|
|
|
'label' => get_lang('DegreeOfCertaintyVeryUnsure'),
|
|
|
|
|
'description' => get_lang('DegreeOfCertaintyVeryUnsureDescription'),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
|
}
|
|
|
|
|
if ($studentDegreeChoicePosition >= 4 && $studentDegreeChoicePosition <= 5) {
|
|
|
|
|
$result = [
|
|
|
|
|
'color' => '#000000',
|
|
|
|
|
'background-color' => '#F6CECE',
|
|
|
|
|
'status' => self::LEVEL_LIGHTRED,
|
|
|
|
|
'label' => get_lang('DegreeOfCertaintyUnsure'),
|
|
|
|
|
'description' => get_lang('DegreeOfCertaintyUnsureDescription'),
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Method to show the code color and his meaning for the test result.
|
|
|
|
|
*/
|
|
|
|
|
public static function showColorCodes()
|
|
|
|
|
{
|
|
|
|
|
?>
|
|
|
|
|
<table class="fc-border-separate" cellspacing="0" style="width:600px;
|
|
|
|
|
margin: auto; border: 3px solid #A39E9E;" >
|
|
|
|
|
<tr style="border-bottom: 1px solid #A39E9E;">
|
|
|
|
|
<td style="width:15%; height:30px; background-color: #088A08; border-right: 1px solid #A39E9E;">
|
|
|
|
|
|
|
|
|
|
</td>
|
|
|
|
|
<td style="padding-left:10px;">
|
|
|
|
|
<b><?php echo get_lang('DegreeOfCertaintyVerySure'); ?> :</b>
|
|
|
|
|
<?php echo get_lang('DegreeOfCertaintyVerySureDescription'); ?>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr style="border-bottom: 1px solid #A39E9E;">
|
|
|
|
|
<td style="width:15%; height:30px; background-color: #A9F5A9; border-right: 1px solid #A39E9E;">
|
|
|
|
|
|
|
|
|
|
</td>
|
|
|
|
|
<td style="padding-left:10px;">
|
|
|
|
|
<b><?php echo get_lang('DegreeOfCertaintyPrettySure'); ?> :</b>
|
|
|
|
|
<?php echo get_lang('DegreeOfCertaintyPrettySureDescription'); ?>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr style="border: 1px solid #A39E9E;">
|
|
|
|
|
<td style="width:15%; height:30px; background-color: #FFFFFF; border-right: 1px solid #A39E9E;">
|
|
|
|
|
|
|
|
|
|
</td>
|
|
|
|
|
<td style="padding-left:10px;">
|
|
|
|
|
<b><?php echo get_lang('DegreeOfCertaintyDeclaredIgnorance'); ?> :</b>
|
|
|
|
|
<?php echo get_lang('DegreeOfCertaintyDeclaredIgnoranceDescription'); ?>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr style="border: 1px solid #A39E9E;">
|
|
|
|
|
<td style="width:15%; height:30px; background-color: #F6CECE; border-right: 1px solid #A39E9E;">
|
|
|
|
|
|
|
|
|
|
</td>
|
|
|
|
|
<td style="padding-left:10px;">
|
|
|
|
|
<b><?php echo get_lang('DegreeOfCertaintyUnsure'); ?> :</b>
|
|
|
|
|
<?php echo get_lang('DegreeOfCertaintyUnsureDescription'); ?>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
<tr style="border-bottom: 1px solid #A39E9E;">
|
|
|
|
|
<td style="width:15%; height:30px; background-color: #FE2E2E; border-right: 1px solid #A39E9E;">
|
|
|
|
|
|
|
|
|
|
</td>
|
|
|
|
|
<td style="padding-left:10px;">
|
|
|
|
|
<b><?php echo get_lang('DegreeOfCertaintyVeryUnsure'); ?> :</b>
|
|
|
|
|
<?php echo get_lang('DegreeOfCertaintyVeryUnsureDescription'); ?>
|
|
|
|
|
</td>
|
|
|
|
|
</tr>
|
|
|
|
|
</table><br/>
|
|
|
|
|
<?php
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Display basic bar charts of results by category of questions.
|
|
|
|
|
*
|
|
|
|
|
* @param array $scoreListAll
|
|
|
|
|
* @param string $title The block title
|
|
|
|
|
* @param int $sizeRatio
|
|
|
|
|
*
|
|
|
|
|
* @return string The HTML/CSS code for the charts block
|
|
|
|
|
*/
|
|
|
|
|
public static function displayDegreeChartByCategory($scoreListAll, $title, $sizeRatio = 1)
|
|
|
|
|
{
|
|
|
|
|
$maxHeight = 0;
|
|
|
|
|
$groupCategoriesByBracket = false;
|
|
|
|
|
if ($groupCategoriesByBracket) {
|
|
|
|
|
$scoreList = [];
|
|
|
|
|
$categoryPrefixList = [];
|
|
|
|
|
// categoryPrefix['Math'] = firstCategoryId for this prefix
|
|
|
|
|
// rebuild $scoreList factorizing data with category prefix
|
|
|
|
|
foreach ($scoreListAll as $categoryId => $scoreListForCategory) {
|
|
|
|
|
$objCategory = new Testcategory();
|
|
|
|
|
$objCategoryNum = $objCategory->getCategory($categoryId);
|
|
|
|
|
preg_match("/^\[([^]]+)\]/", $objCategoryNum->name, $matches);
|
|
|
|
|
|
|
|
|
|
if (count($matches) > 1) {
|
|
|
|
|
// check if we have already see this prefix
|
|
|
|
|
if (array_key_exists($matches[1], $categoryPrefixList)) {
|
|
|
|
|
// add the result color for this entry
|
|
|
|
|
$scoreList[$categoryPrefixList[$matches[1]]][self::LEVEL_DARKGREEN] +=
|
|
|
|
|
$scoreListForCategory[self::LEVEL_DARKGREEN];
|
|
|
|
|
$scoreList[$categoryPrefixList[$matches[1]]][self::LEVEL_LIGHTGREEN] +=
|
|
|
|
|
$scoreListForCategory[self::LEVEL_LIGHTGREEN];
|
|
|
|
|
$scoreList[$categoryPrefixList[$matches[1]]][self::LEVEL_WHITE] +=
|
|
|
|
|
$scoreListForCategory[self::LEVEL_WHITE];
|
|
|
|
|
$scoreList[$categoryPrefixList[$matches[1]]][self::LEVEL_LIGHTRED] +=
|
|
|
|
|
$scoreListForCategory[self::LEVEL_LIGHTRED];
|
|
|
|
|
$scoreList[$categoryPrefixList[$matches[1]]][self::LEVEL_DARKRED] +=
|
|
|
|
|
$scoreListForCategory[self::LEVEL_DARKRED];
|
|
|
|
|
} else {
|
|
|
|
|
$categoryPrefixList[$matches[1]] = $categoryId;
|
|
|
|
|
$scoreList[$categoryId] = $scoreListAll[$categoryId];
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// doesn't match the prefix '[math] Math category'
|
|
|
|
|
$scoreList[$categoryId] = $scoreListAll[$categoryId];
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
$scoreList = $scoreListAll;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// get the max height of item to have each table the same height if displayed side by side
|
|
|
|
|
$testCategory = new TestCategory();
|
|
|
|
|
foreach ($scoreList as $categoryId => $scoreListForCategory) {
|
|
|
|
|
$category = $testCategory->getCategory($categoryId);
|
|
|
|
|
if ($category) {
|
|
|
|
|
$categoryQuestionName = $category->name;
|
|
|
|
|
}
|
|
|
|
|
list($noValue, $height) = self::displayDegreeChart(
|
|
|
|
|
$scoreListForCategory,
|
|
|
|
|
300,
|
|
|
|
|
'',
|
|
|
|
|
1,
|
|
|
|
|
0,
|
|
|
|
|
false,
|
|
|
|
|
true,
|
|
|
|
|
0
|
|
|
|
|
);
|
|
|
|
|
if ($height > $maxHeight) {
|
|
|
|
|
$maxHeight = $height;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (count($scoreList) > 1) {
|
|
|
|
|
$boxWidth = $sizeRatio * 300 * 2 + 54;
|
|
|
|
|
} else {
|
|
|
|
|
$boxWidth = $sizeRatio * 300 + 54;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$html = '<div style="width: '.$boxWidth.'px; margin : auto; padding-left : 15px;">';
|
|
|
|
|
$html .= '<h3 style="text-align: center; margin : 10px 0">'.$title.'</h3>';
|
|
|
|
|
|
|
|
|
|
// get the html of items
|
|
|
|
|
$i = 0;
|
|
|
|
|
$testCategory = new Testcategory();
|
|
|
|
|
foreach ($scoreList as $categoryId => $scoreListForCategory) {
|
|
|
|
|
$category = $testCategory->getCategory($categoryId);
|
|
|
|
|
$categoryQuestionName = '';
|
|
|
|
|
if ($category) {
|
|
|
|
|
$categoryQuestionName = $category->name;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if ($categoryQuestionName === '') {
|
|
|
|
|
$categoryName = get_lang('WithoutCategory');
|
|
|
|
|
} else {
|
|
|
|
|
$categoryName = $categoryQuestionName;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$html .= '<div style="float:left; margin-right: 10px;">';
|
|
|
|
|
$html .= self::displayDegreeChart(
|
|
|
|
|
$scoreListForCategory,
|
|
|
|
|
300,
|
|
|
|
|
$categoryName,
|
|
|
|
|
1,
|
|
|
|
|
$maxHeight,
|
|
|
|
|
false,
|
|
|
|
|
false,
|
|
|
|
|
$groupCategoriesByBracket
|
|
|
|
|
);
|
|
|
|
|
$html .= '</div>';
|
|
|
|
|
|
|
|
|
|
if ($i == 1) {
|
|
|
|
|
$html .= '<div style="clear:both"> </div>';
|
|
|
|
|
$i = 0;
|
|
|
|
|
} else {
|
|
|
|
|
$i++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $html.'<div style="clear:both"> </div></div>';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return HTML code for the $scoreList of MultipleAnswerTrueFalseDegreeCertainty questions.
|
|
|
|
|
*
|
|
|
|
|
* @param $scoreList
|
|
|
|
|
* @param $widthTable
|
|
|
|
|
* @param string $title
|
|
|
|
|
* @param int $sizeRatio
|
|
|
|
|
* @param int $minHeight
|
|
|
|
|
* @param bool $displayExplanationText
|
|
|
|
|
* @param bool $returnHeight
|
|
|
|
|
* @param bool $groupCategoriesByBracket
|
|
|
|
|
* @param int $numberOfQuestions
|
|
|
|
|
*
|
|
|
|
|
* @return array|string
|
|
|
|
|
*/
|
|
|
|
|
public static function displayDegreeChart(
|
|
|
|
|
$scoreList,
|
|
|
|
|
$widthTable,
|
|
|
|
|
$title = '',
|
|
|
|
|
$sizeRatio = 1,
|
|
|
|
|
$minHeight = 0,
|
|
|
|
|
$displayExplanationText = true,
|
|
|
|
|
$returnHeight = false,
|
|
|
|
|
$groupCategoriesByBracket = false,
|
|
|
|
|
$numberOfQuestions = 0
|
|
|
|
|
) {
|
|
|
|
|
$topAndBottomMargin = 10;
|
|
|
|
|
$colorList = [
|
|
|
|
|
self::LEVEL_DARKRED,
|
|
|
|
|
self::LEVEL_LIGHTRED,
|
|
|
|
|
self::LEVEL_WHITE,
|
|
|
|
|
self::LEVEL_LIGHTGREEN,
|
|
|
|
|
self::LEVEL_DARKGREEN,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
// get total attempt number
|
|
|
|
|
$highterColorHeight = 0;
|
|
|
|
|
foreach ($scoreList as $color => $number) {
|
|
|
|
|
if ($number > $highterColorHeight) {
|
|
|
|
|
$highterColorHeight = $number;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$totalAttemptNumber = $numberOfQuestions;
|
|
|
|
|
$verticalLineHeight = $highterColorHeight * $sizeRatio * 2 + 122 + $topAndBottomMargin * 2;
|
|
|
|
|
if ($verticalLineHeight < $minHeight) {
|
|
|
|
|
$minHeightCorrection = $minHeight - $verticalLineHeight;
|
|
|
|
|
$verticalLineHeight += $minHeightCorrection;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// draw chart
|
|
|
|
|
$html = '<style type="text/css">
|
|
|
|
|
.certaintyQuizBox {
|
|
|
|
|
border : 1px solid black;
|
|
|
|
|
margin : auto;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.certaintyQuizColumn {
|
|
|
|
|
float : left;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* text at the top of the column */
|
|
|
|
|
.certaintyQuizDivMiddle {
|
|
|
|
|
height : 20px;
|
|
|
|
|
margin : 0;
|
|
|
|
|
padding : 0;
|
|
|
|
|
text-align: center
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.certaintyQuizDivBottom {
|
|
|
|
|
border : 1px solid black;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.certaintyVerticalLine {
|
|
|
|
|
float: left;
|
|
|
|
|
border-left : 1px solid black;
|
|
|
|
|
font-size: 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.certaintyQuizClearer {
|
|
|
|
|
clear : both;
|
|
|
|
|
font-size: 0;
|
|
|
|
|
height:0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.certaintyQuizLevel_'.self::LEVEL_DARKGREEN.' {
|
|
|
|
|
background-color: #088A08;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.certaintyQuizLevel_'.self::LEVEL_LIGHTGREEN.' {
|
|
|
|
|
background-color: #A9F5A9;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.certaintyQuizLevel_'.self::LEVEL_WHITE.' {
|
|
|
|
|
background-color: #FFFFFF;
|
|
|
|
|
width:88%
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.certaintyQuizLevel_'.self::LEVEL_LIGHTRED.' {
|
|
|
|
|
background-color: #F6CECE;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
.certaintyQuizLevel_'.self::LEVEL_DARKRED.' {
|
|
|
|
|
background-color: #FE2E2E;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table.certaintyTable {
|
|
|
|
|
margin : auto;
|
|
|
|
|
border: 1px solid #999A9B;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table.certaintyTable th {
|
|
|
|
|
text-align: center;
|
|
|
|
|
border-bottom: 1px solid #999A9B;
|
|
|
|
|
background-color: #cdd0d4;
|
|
|
|
|
padding : 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table.certaintyTable td {
|
|
|
|
|
padding : 10px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table.certaintyTable td.borderRight {
|
|
|
|
|
border-right: 1px dotted #000000;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table.certaintyTable td.firstLine {
|
|
|
|
|
vertical-align: top;
|
|
|
|
|
text-align: center;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table.certaintyTable th.globalChart {
|
|
|
|
|
font-size : 18pt;
|
|
|
|
|
line-height : 120%;
|
|
|
|
|
padding : 20px;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
table.certaintyTable td.globalChart {
|
|
|
|
|
font-weight : bold;
|
|
|
|
|
}
|
|
|
|
|
</style>';
|
|
|
|
|
|
|
|
|
|
if ($groupCategoriesByBracket) {
|
|
|
|
|
$title = api_preg_replace("/[^]]*$/", '', $title);
|
|
|
|
|
$title = ucfirst(api_preg_replace("/[\[\]]/", '', $title));
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$display = (strpos($title, "ensemble") > 0) ?
|
|
|
|
|
$title."<br/>($totalAttemptNumber questions)" :
|
|
|
|
|
$title;
|
|
|
|
|
$textSize = (
|
|
|
|
|
strpos($title, 'ensemble') > 0 ||
|
|
|
|
|
strpos($title, 'votre dernier résultat à ce test') > 0
|
|
|
|
|
) ? 100 : 80;
|
|
|
|
|
|
|
|
|
|
$classGlobalChart = '';
|
|
|
|
|
if ($displayExplanationText) {
|
|
|
|
|
// global chart
|
|
|
|
|
$classGlobalChart = 'globalChart';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$html .= '<table class="certaintyTable" style="height :'.$verticalLineHeight.'px; width: '.$widthTable.'px;">';
|
|
|
|
|
$html .= '<tr><th colspan="5" class="'
|
|
|
|
|
.$classGlobalChart
|
|
|
|
|
.'">'
|
|
|
|
|
.$display
|
|
|
|
|
.'</th><tr>'
|
|
|
|
|
;
|
|
|
|
|
|
|
|
|
|
$nbResponsesInc = 0;
|
|
|
|
|
if (isset($scoreList[4])) {
|
|
|
|
|
$nbResponsesInc += (int) $scoreList[4];
|
|
|
|
|
}
|
|
|
|
|
if (isset($scoreList[5])) {
|
|
|
|
|
$nbResponsesInc += (int) $scoreList[5];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$nbResponsesIng = isset($scoreList[3]) ? $scoreList[3] : 0;
|
|
|
|
|
|
|
|
|
|
$nbResponsesCor = 0;
|
|
|
|
|
if (isset($scoreList[1])) {
|
|
|
|
|
$nbResponsesCor += (int) $scoreList[1];
|
|
|
|
|
}
|
|
|
|
|
if (isset($scoreList[2])) {
|
|
|
|
|
$nbResponsesCor += (int) $scoreList[2];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$colWidth = $widthTable / 5;
|
|
|
|
|
|
|
|
|
|
$html .= '<tr>
|
|
|
|
|
<td class="firstLine borderRight '.$classGlobalChart.'"
|
|
|
|
|
colspan="2"
|
|
|
|
|
style="width:'.($colWidth * 2).'px; font-size:'.$textSize.'%;">'.
|
|
|
|
|
sprintf(get_lang('IncorrectAnswersX'), $nbResponsesInc).'
|
|
|
|
|
</td>
|
|
|
|
|
<td class="firstLine borderRight '.$classGlobalChart.'"
|
|
|
|
|
style="width:'.$colWidth.'px; font-size :'.$textSize.'%;">'.
|
|
|
|
|
sprintf(get_lang('IgnoranceAnswersX'), $nbResponsesIng).'
|
|
|
|
|
</td>
|
|
|
|
|
<td class="firstLine '.$classGlobalChart.'"
|
|
|
|
|
colspan="2"
|
|
|
|
|
style="width:'.($colWidth * 2).'px; font-size:'.$textSize.'%;">'.
|
|
|
|
|
sprintf(get_lang('CorrectAnswersX'), $nbResponsesCor).'
|
|
|
|
|
</td>
|
|
|
|
|
</tr>';
|
|
|
|
|
$html .= '<tr>';
|
|
|
|
|
|
|
|
|
|
foreach ($colorList as $i => $color) {
|
|
|
|
|
if (array_key_exists($color, $scoreList)) {
|
|
|
|
|
$scoreOnBottom = $scoreList[$color]; // height of the colored area on the bottom
|
|
|
|
|
} else {
|
|
|
|
|
$scoreOnBottom = 0;
|
|
|
|
|
}
|
|
|
|
|
$sizeOnBottom = $scoreOnBottom * $sizeRatio * 2;
|
|
|
|
|
if ($i == 1 || $i == 2) {
|
|
|
|
|
$html .= '<td width="'
|
|
|
|
|
.$colWidth
|
|
|
|
|
.'px" style="border-right: 1px dotted #000000; vertical-align: bottom;font-size: '
|
|
|
|
|
.$textSize
|
|
|
|
|
.'%;">'
|
|
|
|
|
;
|
|
|
|
|
} else {
|
|
|
|
|
$html .= '<td width="'
|
|
|
|
|
.$colWidth
|
|
|
|
|
.'px" style="vertical-align: bottom;font-size: '
|
|
|
|
|
.$textSize
|
|
|
|
|
.'%;">'
|
|
|
|
|
;
|
|
|
|
|
}
|
|
|
|
|
$html .= '<div class="certaintyQuizDivMiddle">'
|
|
|
|
|
.$scoreOnBottom
|
|
|
|
|
.'</div><div class="certaintyQuizDivBottom certaintyQuizLevel_'
|
|
|
|
|
.$color
|
|
|
|
|
.'" style="height: '
|
|
|
|
|
.$sizeOnBottom
|
|
|
|
|
.'px;"> </div>'
|
|
|
|
|
;
|
|
|
|
|
$html .= '</td>';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$html .= '</tr>';
|
|
|
|
|
|
|
|
|
|
if ($displayExplanationText) {
|
|
|
|
|
// Display of histogram text
|
|
|
|
|
$explainHistoList = [
|
|
|
|
|
'DegreeOfCertaintyVeryUnsure',
|
|
|
|
|
'DegreeOfCertaintyUnsure',
|
|
|
|
|
'DegreeOfCertaintyDeclaredIgnorance',
|
|
|
|
|
'DegreeOfCertaintyPrettySure',
|
|
|
|
|
'DegreeOfCertaintyVerySure',
|
|
|
|
|
];
|
|
|
|
|
$html .= '<tr>';
|
|
|
|
|
$i = 0;
|
|
|
|
|
foreach ($explainHistoList as $explain) {
|
|
|
|
|
if ($i == 1 || $i == 2) {
|
|
|
|
|
$class = 'borderRight';
|
|
|
|
|
} else {
|
|
|
|
|
$class = '';
|
|
|
|
|
}
|
|
|
|
|
$html .= '<td class="firstLine '
|
|
|
|
|
.$class
|
|
|
|
|
.' '
|
|
|
|
|
.$classGlobalChart
|
|
|
|
|
.'" style="width="'
|
|
|
|
|
.$colWidth
|
|
|
|
|
.'px; font-size:'
|
|
|
|
|
.$textSize
|
|
|
|
|
.'%;">'
|
|
|
|
|
;
|
|
|
|
|
$html .= get_lang($explain);
|
|
|
|
|
$html .= '</td>';
|
|
|
|
|
$i++;
|
|
|
|
|
}
|
|
|
|
|
$html .= '</tr>';
|
|
|
|
|
}
|
|
|
|
|
$html .= '</table></center>';
|
|
|
|
|
|
|
|
|
|
if ($returnHeight) {
|
|
|
|
|
return [$html, $verticalLineHeight];
|
|
|
|
|
} else {
|
|
|
|
|
return $html;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* return previous attempt id for this test for student, 0 if no previous attempt.
|
|
|
|
|
*
|
|
|
|
|
* @param $exeId
|
|
|
|
|
*
|
|
|
|
|
* @return int
|
|
|
|
|
*/
|
|
|
|
|
public static function getPreviousAttemptId($exeId)
|
|
|
|
|
{
|
|
|
|
|
$tblTrackEExercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
|
|
|
|
|
$exeId = (int) $exeId;
|
|
|
|
|
$sql = "SELECT * FROM $tblTrackEExercise
|
|
|
|
|
WHERE exe_id = ".$exeId;
|
|
|
|
|
$res = Database::query($sql);
|
|
|
|
|
|
|
|
|
|
if (empty(Database::num_rows($res))) {
|
|
|
|
|
// if we cannot find the exe_id
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$data = Database::fetch_assoc($res);
|
|
|
|
|
$courseCode = $data['c_id'];
|
|
|
|
|
$exerciseId = $data['exe_exo_id'];
|
|
|
|
|
$userId = $data['exe_user_id'];
|
|
|
|
|
$attemptDate = $data['exe_date'];
|
|
|
|
|
|
|
|
|
|
if ($attemptDate == '0000-00-00 00:00:00') {
|
|
|
|
|
// incomplete attempt, close it before continue
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// look for previous attempt
|
|
|
|
|
$exerciseId = (int) $exerciseId;
|
|
|
|
|
$userId = (int) $userId;
|
|
|
|
|
$sql = "SELECT *
|
|
|
|
|
FROM $tblTrackEExercise
|
|
|
|
|
WHERE c_id = '$courseCode'
|
|
|
|
|
AND exe_exo_id = $exerciseId
|
|
|
|
|
AND exe_user_id = $userId
|
|
|
|
|
AND status = ''
|
|
|
|
|
AND exe_date > '0000-00-00 00:00:00'
|
|
|
|
|
AND exe_date < '$attemptDate'
|
|
|
|
|
ORDER BY exe_date DESC";
|
|
|
|
|
|
|
|
|
|
$res = Database::query($sql);
|
|
|
|
|
|
|
|
|
|
if (Database::num_rows($res) == 0) {
|
|
|
|
|
// no previous attempt
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$data = Database::fetch_assoc($res);
|
|
|
|
|
|
|
|
|
|
return $data['exe_id'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* return an array of number of answer color for exe attempt
|
|
|
|
|
* for question type = MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY
|
|
|
|
|
* e.g.
|
|
|
|
|
* [LEVEL_DARKGREEN => 3, LEVEL_LIGHTGREEN => 0, LEVEL_WHITE => 5, LEVEL_LIGHTRED => 12, LEVEL_DARKTRED => 0].
|
|
|
|
|
*
|
|
|
|
|
* @param $exeId
|
|
|
|
|
*
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public static function getColorNumberListForAttempt($exeId)
|
|
|
|
|
{
|
|
|
|
|
$result = [
|
|
|
|
|
self::LEVEL_DARKGREEN => 0,
|
|
|
|
|
self::LEVEL_LIGHTGREEN => 0,
|
|
|
|
|
self::LEVEL_WHITE => 0,
|
|
|
|
|
self::LEVEL_LIGHTRED => 0,
|
|
|
|
|
self::LEVEL_DARKRED => 0,
|
|
|
|
|
];
|
|
|
|
|
|
|
|
|
|
$attemptInfoList = self::getExerciseAttemptInfo($exeId);
|
|
|
|
|
|
|
|
|
|
foreach ($attemptInfoList as $attemptInfo) {
|
|
|
|
|
$oQuestion = new MultipleAnswerTrueFalseDegreeCertainty();
|
|
|
|
|
$oQuestion->read($attemptInfo['question_id']);
|
|
|
|
|
if ($oQuestion->type == MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY) {
|
|
|
|
|
$answerColor = self::getAnswerColor($exeId, $attemptInfo['question_id'], $attemptInfo['position']);
|
|
|
|
|
if ($answerColor) {
|
|
|
|
|
$result[$answerColor]++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* return an array of number of color for question type = MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY
|
|
|
|
|
* for each question category.
|
|
|
|
|
*
|
|
|
|
|
* e.g.
|
|
|
|
|
* [
|
|
|
|
|
* (categoryId=)5 => [LEVEL_DARKGREEN => 3, LEVEL_WHITE => 5, LEVEL_LIGHTRED => 12]
|
|
|
|
|
* (categoryId=)2 => [LEVEL_DARKGREEN => 8, LEVEL_LIGHTRED => 2, LEVEL_DARKTRED => 8]
|
|
|
|
|
* (categoryId=)0 => [LEVEL_DARKGREEN => 1,
|
|
|
|
|
* LEVEL_LIGHTGREEN => 2,
|
|
|
|
|
* LEVEL_WHITE => 6,
|
|
|
|
|
* LEVEL_LIGHTRED => 1,
|
|
|
|
|
* LEVEL_DARKTRED => 9]
|
|
|
|
|
* ]
|
|
|
|
|
*
|
|
|
|
|
* @param int $exeId
|
|
|
|
|
*
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public static function getColorNumberListForAttemptByCategory($exeId)
|
|
|
|
|
{
|
|
|
|
|
$result = [];
|
|
|
|
|
$attemptInfoList = self::getExerciseAttemptInfo($exeId);
|
|
|
|
|
|
|
|
|
|
foreach ($attemptInfoList as $attemptInfo) {
|
|
|
|
|
$oQuestion = new MultipleAnswerTrueFalseDegreeCertainty();
|
|
|
|
|
$oQuestion->read($attemptInfo['question_id']);
|
|
|
|
|
if ($oQuestion->type == MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY) {
|
|
|
|
|
$questionCategory = Testcategory::getCategoryForQuestion($attemptInfo['question_id']);
|
|
|
|
|
|
|
|
|
|
if (!array_key_exists($questionCategory, $result)) {
|
|
|
|
|
$result[$questionCategory] = [];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$answerColor = self::getAnswerColor($exeId, $attemptInfo['question_id'], $attemptInfo['position']);
|
|
|
|
|
if ($answerColor && isset($result[$questionCategory])) {
|
|
|
|
|
if (!isset($result[$questionCategory][$answerColor])) {
|
|
|
|
|
$result[$questionCategory][$answerColor] = 0;
|
|
|
|
|
}
|
|
|
|
|
$result[$questionCategory][$answerColor]++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return true if answer of $exeId, $questionId, $position is correct, otherwise return false.
|
|
|
|
|
*
|
|
|
|
|
* @param $exeId
|
|
|
|
|
* @param $questionId
|
|
|
|
|
* @param $position
|
|
|
|
|
*
|
|
|
|
|
* @return int
|
|
|
|
|
*/
|
|
|
|
|
public static function getAnswerColor($exeId, $questionId, $position)
|
|
|
|
|
{
|
|
|
|
|
$attemptInfoList = self::getExerciseAttemptInfo($exeId, $questionId, $position);
|
|
|
|
|
|
|
|
|
|
if (count($attemptInfoList) != 1) {
|
|
|
|
|
// havent got the answer
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$answerCodes = $attemptInfoList[0]['answer'];
|
|
|
|
|
|
|
|
|
|
// student answer
|
|
|
|
|
$splitAnswer = preg_split("/:/", $answerCodes);
|
|
|
|
|
// get correct answer option id
|
|
|
|
|
$correctAnswerOptionId = self::getCorrectAnswerOptionId($splitAnswer[0]);
|
|
|
|
|
if ($correctAnswerOptionId == 0) {
|
|
|
|
|
// error returning the correct answer option id
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// get student answer option id
|
|
|
|
|
$studentAnswerOptionId = $splitAnswer[1];
|
|
|
|
|
|
|
|
|
|
// we got the correct answer option id, let's compare ti with the student answer
|
|
|
|
|
$percentage = self::getPercentagePosition($splitAnswer[2]);
|
|
|
|
|
if ($studentAnswerOptionId == $correctAnswerOptionId) {
|
|
|
|
|
// yeah, student got correct answer
|
|
|
|
|
switch ($percentage) {
|
|
|
|
|
case 3:
|
|
|
|
|
return self::LEVEL_WHITE;
|
|
|
|
|
case 4:
|
|
|
|
|
case 5:
|
|
|
|
|
return self::LEVEL_LIGHTGREEN;
|
|
|
|
|
case 6:
|
|
|
|
|
case 7:
|
|
|
|
|
case 8:
|
|
|
|
|
return self::LEVEL_DARKGREEN;
|
|
|
|
|
default:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
// bummer, wrong answer dude
|
|
|
|
|
switch ($percentage) {
|
|
|
|
|
case 3:
|
|
|
|
|
return self::LEVEL_WHITE;
|
|
|
|
|
case 4:
|
|
|
|
|
case 5:
|
|
|
|
|
return self::LEVEL_LIGHTRED;
|
|
|
|
|
case 6:
|
|
|
|
|
case 7:
|
|
|
|
|
case 8:
|
|
|
|
|
return self::LEVEL_DARKRED;
|
|
|
|
|
default:
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Return the position of certitude %age choose by student.
|
|
|
|
|
*
|
|
|
|
|
* @param $optionId
|
|
|
|
|
*
|
|
|
|
|
* @return int
|
|
|
|
|
*/
|
|
|
|
|
public static function getPercentagePosition($optionId)
|
|
|
|
|
{
|
|
|
|
|
$tblAnswerOption = Database::get_course_table(TABLE_QUIZ_QUESTION_OPTION);
|
|
|
|
|
$courseId = api_get_course_int_id();
|
|
|
|
|
$optionId = (int) $optionId;
|
|
|
|
|
$sql = "SELECT position
|
|
|
|
|
FROM $tblAnswerOption
|
|
|
|
|
WHERE c_id = $courseId AND id = $optionId";
|
|
|
|
|
$res = Database::query($sql);
|
|
|
|
|
|
|
|
|
|
if (Database::num_rows($res) == 0) {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$data = Database::fetch_assoc($res);
|
|
|
|
|
|
|
|
|
|
return $data['position'];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* return the correct id from c_quiz_question_option for question idAuto.
|
|
|
|
|
*
|
|
|
|
|
* @param $idAuto
|
|
|
|
|
*
|
|
|
|
|
* @return int
|
|
|
|
|
*/
|
|
|
|
|
public static function getCorrectAnswerOptionId($idAuto)
|
|
|
|
|
{
|
|
|
|
|
$tblAnswer = Database::get_course_table(TABLE_QUIZ_ANSWER);
|
|
|
|
|
$courseId = api_get_course_int_id();
|
|
|
|
|
$idAuto = (int) $idAuto;
|
|
|
|
|
$sql = "SELECT correct FROM $tblAnswer
|
|
|
|
|
WHERE c_id = $courseId AND id_auto = $idAuto";
|
|
|
|
|
|
|
|
|
|
$res = Database::query($sql);
|
|
|
|
|
$data = Database::fetch_assoc($res);
|
|
|
|
|
if (Database::num_rows($res) > 0) {
|
|
|
|
|
return $data['correct'];
|
|
|
|
|
} else {
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* return an array of exe info from track_e_attempt.
|
|
|
|
|
*
|
|
|
|
|
* @param int $exeId
|
|
|
|
|
* @param int $questionId
|
|
|
|
|
* @param int $position
|
|
|
|
|
*
|
|
|
|
|
* @return array
|
|
|
|
|
*/
|
|
|
|
|
public static function getExerciseAttemptInfo($exeId, $questionId = -1, $position = -1)
|
|
|
|
|
{
|
|
|
|
|
$result = [];
|
|
|
|
|
$and = '';
|
|
|
|
|
$questionId = (int) $questionId;
|
|
|
|
|
$position = (int) $position;
|
|
|
|
|
|
|
|
|
|
if ($questionId >= 0) {
|
|
|
|
|
$and .= " AND question_id = $questionId";
|
|
|
|
|
}
|
|
|
|
|
if ($position >= 0) {
|
|
|
|
|
$and .= " AND position = $position";
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$tblExeAttempt = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
|
|
|
|
|
$cId = api_get_course_int_id();
|
|
|
|
|
$sql = "SELECT * FROM $tblExeAttempt
|
|
|
|
|
WHERE c_id = $cId AND exe_id = $exeId $and";
|
|
|
|
|
|
|
|
|
|
$res = Database::query($sql);
|
|
|
|
|
while ($data = Database::fetch_assoc($res)) {
|
|
|
|
|
$result[] = $data;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return $result;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* @param int $exeId
|
|
|
|
|
*
|
|
|
|
|
* @return int
|
|
|
|
|
*/
|
|
|
|
|
public static function getNumberOfQuestionsForExeId($exeId)
|
|
|
|
|
{
|
|
|
|
|
$tableTrackEExercise = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
|
|
|
|
|
$exeId = (int) $exeId;
|
|
|
|
|
|
|
|
|
|
$sql = "SELECT exe_exo_id
|
|
|
|
|
FROM $tableTrackEExercise
|
|
|
|
|
WHERE exe_id=".$exeId;
|
|
|
|
|
$res = Database::query($sql);
|
|
|
|
|
$data = Database::fetch_assoc($res);
|
|
|
|
|
if ($data) {
|
|
|
|
|
$exerciseId = $data['exe_exo_id'];
|
|
|
|
|
|
|
|
|
|
$objectExercise = new Exercise();
|
|
|
|
|
$objectExercise->read($exerciseId);
|
|
|
|
|
|
|
|
|
|
return $objectExercise->get_count_question_list();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* Display student chart results for these question types.
|
|
|
|
|
*
|
|
|
|
|
* @param int $exeId
|
|
|
|
|
* @param Exercise $objExercice
|
|
|
|
|
*
|
|
|
|
|
* @return string
|
|
|
|
|
*/
|
|
|
|
|
public static function displayStudentsChartResults($exeId, $objExercice)
|
|
|
|
|
{
|
|
|
|
|
$numberOfQuestions = self::getNumberOfQuestionsForExeId($exeId);
|
|
|
|
|
$globalScoreList = self::getColorNumberListForAttempt($exeId);
|
|
|
|
|
$html = self::displayDegreeChart(
|
|
|
|
|
$globalScoreList,
|
|
|
|
|
600,
|
|
|
|
|
get_lang('YourOverallResultForTheTest'),
|
|
|
|
|
2,
|
|
|
|
|
0,
|
|
|
|
|
true,
|
|
|
|
|
false,
|
|
|
|
|
false,
|
|
|
|
|
$numberOfQuestions
|
|
|
|
|
);
|
|
|
|
|
$html .= '<br/>';
|
|
|
|
|
|
|
|
|
|
$previousAttemptId = self::getPreviousAttemptId($exeId);
|
|
|
|
|
if ($previousAttemptId > 0) {
|
|
|
|
|
$previousAttemptScoreList = self::getColorNumberListForAttempt(
|
|
|
|
|
$previousAttemptId
|
|
|
|
|
);
|
|
|
|
|
$html .= self::displayDegreeChart(
|
|
|
|
|
$previousAttemptScoreList,
|
|
|
|
|
600,
|
|
|
|
|
get_lang('ForComparisonYourLastResultToThisTest'),
|
|
|
|
|
2
|
|
|
|
|
);
|
|
|
|
|
$html .= '<br/>';
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
$list = self::getColorNumberListForAttemptByCategory($exeId);
|
|
|
|
|
$html .= self::displayDegreeChartByCategory(
|
|
|
|
|
$list,
|
|
|
|
|
get_lang('YourResultsByDiscipline'),
|
|
|
|
|
1,
|
|
|
|
|
$objExercice
|
|
|
|
|
);
|
|
|
|
|
$html .= '<br/>';
|
|
|
|
|
|
|
|
|
|
return $html;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* send mail to student with degre certainty result test.
|
|
|
|
|
*
|
|
|
|
|
* @param int $userId
|
|
|
|
|
* @param Exercise $objExercise
|
|
|
|
|
* @param int $exeId
|
|
|
|
|
*/
|
|
|
|
|
public static function sendQuestionCertaintyNotification($userId, $objExercise, $exeId)
|
|
|
|
|
{
|
|
|
|
|
$userInfo = api_get_user_info($userId);
|
|
|
|
|
$recipientName = api_get_person_name($userInfo['firstname'],
|
|
|
|
|
$userInfo['lastname'],
|
|
|
|
|
null,
|
|
|
|
|
PERSON_NAME_EMAIL_ADDRESS
|
|
|
|
|
);
|
|
|
|
|
$subject = "[".get_lang('DoNotReply')."] "
|
|
|
|
|
.html_entity_decode(get_lang('ResultAccomplishedTest')." \"".$objExercise->title."\"");
|
|
|
|
|
|
|
|
|
|
// message sended to the student
|
|
|
|
|
$message = get_lang('Dear').' '.$recipientName.",<br /><br />";
|
|
|
|
|
$message .= get_lang('MessageQuestionCertainty');
|
|
|
|
|
$exerciseLink = "<a href='".api_get_path(WEB_CODE_PATH)."/exercise/result.php?show_headers=1&"
|
|
|
|
|
.api_get_cidreq()
|
|
|
|
|
."&id=$exeId'>";
|
|
|
|
|
$exerciseTitle = $objExercise->title;
|
|
|
|
|
$message = str_replace('%exerTitle', $exerciseTitle, $message);
|
|
|
|
|
$message = str_replace('%webPath', api_get_path(WEB_PATH), $message);
|
|
|
|
|
$message = str_replace('%s', $exerciseLink, $message);
|
|
|
|
|
|
|
|
|
|
// show histogram
|
|
|
|
|
$message .= self::displayStudentsChartResults($exeId, $objExercise);
|
|
|
|
|
$message .= get_lang('KindRegards');
|
|
|
|
|
$message = api_preg_replace("/\\\n/", '', $message);
|
|
|
|
|
|
|
|
|
|
MessageManager::send_message_simple($userId, $subject, $message);
|
|
|
|
|
}
|
|
|
|
|
}
|