You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1336 lines
47 KiB
1336 lines
47 KiB
<?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.
|
|
*/
|
|
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 $typePicture = 'mccert.png';
|
|
public $explanationLangVar = 'Multiple answer true/false/degree of certainty';
|
|
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
|
|
*/
|
|
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('N°')
|
|
.'</th><th>'
|
|
.get_lang('True')
|
|
.'</th><th>'
|
|
.get_lang('False')
|
|
.'</th><th>'
|
|
.get_lang('Answer')
|
|
.'</th>';
|
|
|
|
// show column comment when feedback is enable
|
|
if (EXERCISE_FEEDBACK_TYPE_EXAM != $objEx->getFeedbackType()) {
|
|
$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 ($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('You have to create at least one answer'));
|
|
}
|
|
|
|
// 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.']'] = isset($answer->answer[$i]) ? $answer->answer[$i] : '';
|
|
if (isset($_POST['answer']) && isset($_POST['answer'][$i])) {
|
|
$defaults['answer['.$i.']'] = Security::remove_XSS($_POST['answer'][$i]);
|
|
}
|
|
|
|
$defaults['comment['.$i.']'] = isset($answer->comment[$i]) ? $answer->comment[$i] : '';
|
|
if (isset($_POST['comment']) && isset($_POST['comment'][$i])) {
|
|
$defaults['comment['.$i.']'] = Security::remove_XSS($_POST['comment'][$i]);
|
|
}
|
|
|
|
$defaults['weighting['.$i.']'] = isset($answer->weighting[$i]) ? float_format($answer->weighting[$i], 1) : '';
|
|
$correct = isset($answer->correct[$i]) ? $answer->correct[$i] : '';
|
|
$defaults['correct['.$i.']'] = $correct;
|
|
if (isset($_POST['correct']) && isset($_POST['correct'][$i])) {
|
|
$defaults['correct['.$i.']'] = Security::remove_XSS($_POST['correct'][$i]);
|
|
}
|
|
|
|
$j = 1;
|
|
if (!empty($optionData)) {
|
|
foreach ($optionData as $id => $data) {
|
|
$form->addElement('radio', 'correct['.$i.']', null, null, $id);
|
|
$j++;
|
|
if (3 == $j) {
|
|
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('Required field'), 'required');
|
|
|
|
// show comment when feedback is enable
|
|
if (EXERCISE_FEEDBACK_TYPE_EXAM != $objEx->getFeedbackType()) {
|
|
$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('Required field'), 'required');
|
|
$form->addRule('option[2]', get_lang('Required field'), '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 (true === $objEx->edit_exercise_in_lp ||
|
|
(empty($this->exerciseList) && empty($objEx->id))
|
|
) {
|
|
$form->addElement('submit', 'lessAnswers', get_lang('Remove answer option'), 'class="btn btn-danger minus"');
|
|
$form->addElement('submit', 'moreAnswers', get_lang('Add answer option'), '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);
|
|
}
|
|
|
|
public function return_header(Exercise $exercise, $counter = null, $score = [])
|
|
{
|
|
$header = parent::return_header($exercise, $counter, $score);
|
|
$header .= '<table class="'.$this->question_table_class.'"><tr>';
|
|
$header .= '<th>'.get_lang('Your choice').'</th>';
|
|
|
|
if ($exercise->showExpectedChoiceColumn()) {
|
|
$header .= '<th>'.get_lang('ExpectedYour choice').'</th>';
|
|
}
|
|
|
|
$header .= '<th>'
|
|
.get_lang('Answer')
|
|
.'</th><th colspan="2" style="text-align:center;">'
|
|
.get_lang('Your degree of certainty')
|
|
.'</th>'
|
|
;
|
|
if (EXERCISE_FEEDBACK_TYPE_EXAM != $exercise->getFeedbackType()) {
|
|
$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 (3 == $studentDegreeChoicePosition) {
|
|
$result = [
|
|
'color' => '#000000',
|
|
'background-color' => '#F6BA2A',
|
|
'status' => self::LEVEL_WHITE,
|
|
'label' => get_lang('Declared ignorance'),
|
|
'description' => get_lang('Declared ignoranceDescription'),
|
|
];
|
|
} else {
|
|
$checkResult = $studentAnswer == $expectedAnswer ? true : false;
|
|
if ($checkResult) {
|
|
if ($studentDegreeChoicePosition >= 6) {
|
|
$result = [
|
|
'color' => '#FFFFFF',
|
|
'background-color' => '#1E9C55',
|
|
'status' => self::LEVEL_DARKGREEN,
|
|
'label' => get_lang('Very sure'),
|
|
'description' => get_lang('Very sureDescription'),
|
|
];
|
|
} elseif ($studentDegreeChoicePosition >= 4 && $studentDegreeChoicePosition <= 5) {
|
|
$result = [
|
|
'color' => '#000000',
|
|
'background-color' => '#B1E183',
|
|
'status' => self::LEVEL_LIGHTGREEN,
|
|
'label' => get_lang('Pretty sure'),
|
|
'description' => get_lang('Pretty sureDescription'),
|
|
];
|
|
}
|
|
} else {
|
|
if ($studentDegreeChoicePosition >= 6) {
|
|
$result = [
|
|
'color' => '#FFFFFF',
|
|
'background-color' => '#ED4040',
|
|
'status' => self::LEVEL_DARKRED,
|
|
'label' => get_lang('Very unsure'),
|
|
'description' => get_lang('Very unsureDescription'),
|
|
];
|
|
} elseif ($studentDegreeChoicePosition >= 4 && $studentDegreeChoicePosition <= 5) {
|
|
$result = [
|
|
'color' => '#000000',
|
|
'background-color' => '#F79B88',
|
|
'status' => self::LEVEL_LIGHTRED,
|
|
'label' => get_lang('Unsure'),
|
|
'description' => get_lang('UnsureDescription'),
|
|
];
|
|
}
|
|
}
|
|
}
|
|
|
|
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('Very sure'); ?> :</b>
|
|
<?php echo get_lang('Very sureDescription'); ?>
|
|
</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('Pretty sure'); ?> :</b>
|
|
<?php echo get_lang('Pretty sureDescription'); ?>
|
|
</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('Declared ignorance'); ?> :</b>
|
|
<?php echo get_lang('Declared ignoranceDescription'); ?>
|
|
</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('Unsure'); ?> :</b>
|
|
<?php echo get_lang('UnsureDescription'); ?>
|
|
</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('Very unsure'); ?> :</b>
|
|
<?php echo get_lang('Very unsureDescription'); ?>
|
|
</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::displayDegreeChartChildren(
|
|
$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 class="row-chart">';
|
|
$html .= '<h4 class="chart-title">'.$title.'</h4>';
|
|
|
|
$legendTitle = [
|
|
'Very unsure',
|
|
'Unsure',
|
|
'Declared ignorance',
|
|
'Pretty sure',
|
|
'Very sure',
|
|
];
|
|
$html .= '<ul class="chart-legend">';
|
|
foreach ($legendTitle as $i => $item) {
|
|
$html .= '<li><i class="fa fa-square square_color'.$i.'" aria-hidden="true"></i> '.get_lang($item).'</li>';
|
|
}
|
|
$html .= '</ul>';
|
|
|
|
// 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('Without category');
|
|
} else {
|
|
$categoryName = $categoryQuestionName;
|
|
}
|
|
|
|
$html .= '<div class="col-md-4">';
|
|
$html .= self::displayDegreeChartChildren(
|
|
$scoreListForCategory,
|
|
300,
|
|
$categoryName,
|
|
1,
|
|
$maxHeight,
|
|
false,
|
|
false,
|
|
$groupCategoriesByBracket
|
|
);
|
|
$html .= '</div>';
|
|
|
|
if (2 == $i) {
|
|
$html .= '<div style="clear:both; height: 10px;"> </div>';
|
|
$i = 0;
|
|
} else {
|
|
$i++;
|
|
}
|
|
}
|
|
$html .= '</div>';
|
|
|
|
return $html.'<div style="clear:both; height: 10px;" > </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 = '';
|
|
|
|
if ($groupCategoriesByBracket) {
|
|
$title = api_preg_replace('/[^]]*$/', '', $title);
|
|
$title = ucfirst(api_preg_replace("/[\[\]]/", '', $title));
|
|
}
|
|
|
|
$titleDisplay = 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;
|
|
|
|
$html .= '<div class="row-chart">';
|
|
$html .= '<h4 class="chart-title">'.$titleDisplay.'</h4>';
|
|
|
|
$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];
|
|
}
|
|
|
|
$IncorrectAnswers = sprintf(get_lang('Incorrect answers: %s'), $nbResponsesInc);
|
|
$IgnoranceAnswers = sprintf(get_lang('Ignorance: %s'), $nbResponsesIng);
|
|
$CorrectAnswers = sprintf(get_lang('Correct answers: %s'), $nbResponsesCor);
|
|
|
|
$html .= '<div class="chart-grid">';
|
|
|
|
$explainHistoList = null;
|
|
if ($displayExplanationText) {
|
|
// Display of histogram text
|
|
$explainHistoList = [
|
|
'Very unsure',
|
|
'Unsure',
|
|
'Declared ignorance',
|
|
'Pretty sure',
|
|
'Very sure',
|
|
];
|
|
}
|
|
|
|
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;
|
|
}
|
|
$sizeBar = ($scoreOnBottom * $sizeRatio * 2).'px;';
|
|
|
|
if (0 == $i) {
|
|
$html .= '<div class="item">';
|
|
$html .= '<div class="panel-certaint" style="min-height:'.$verticalLineHeight.'px; position: relative;">';
|
|
$html .= '<div class="answers-title">'.$IncorrectAnswers.'</div>';
|
|
$html .= '<ul class="certaint-list-two">';
|
|
} elseif (3 == $i) {
|
|
$html .= '<div class="item">';
|
|
$html .= '<div class="panel-certaint" style="height:'.$verticalLineHeight.'px; position: relative;">';
|
|
$html .= '<div class="answers-title">'.$CorrectAnswers.'</div>';
|
|
$html .= '<ul class="certaint-list-two">';
|
|
} elseif (2 == $i) {
|
|
$html .= '<div class="item">';
|
|
$html .= '<div class="panel-certaint" style="height:'.$verticalLineHeight.'px; position: relative;">';
|
|
$html .= '<div class="answers-title">'.$IgnoranceAnswers.'</div>';
|
|
$html .= '<ul class="certaint-list">';
|
|
}
|
|
$html .= '<li>';
|
|
$html .= '<div class="certaint-score">';
|
|
$html .= $scoreOnBottom;
|
|
$html .= '</div>';
|
|
$html .= '<div class="levelbar_'.$color.'" style="height:'.$sizeBar.'"> </div>';
|
|
$html .= '<div class="certaint-text">'.get_lang($explainHistoList[$i]).'</div>';
|
|
$html .= '</li>';
|
|
|
|
if (1 == $i || 2 == $i || 4 == $i) {
|
|
$html .= '</ul>';
|
|
$html .= '</div>';
|
|
$html .= '</div>';
|
|
}
|
|
}
|
|
|
|
$html .= '</div>';
|
|
$html .= '</div>';
|
|
|
|
if ($returnHeight) {
|
|
return [$html, $verticalLineHeight];
|
|
} else {
|
|
return $html;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* 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 displayDegreeChartChildren(
|
|
$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 = '';
|
|
|
|
if ($groupCategoriesByBracket) {
|
|
$title = api_preg_replace('/[^]]*$/', '', $title);
|
|
$title = ucfirst(api_preg_replace("/[\[\]]/", '', $title));
|
|
}
|
|
|
|
$textSize = 80;
|
|
|
|
$classGlobalChart = '';
|
|
if ($displayExplanationText) {
|
|
// global chart
|
|
$classGlobalChart = 'globalChart';
|
|
}
|
|
|
|
$html .= '<table class="certaintyTable" style="height :'.$verticalLineHeight.'px; margin-bottom: 10px;" >';
|
|
$html .= '<tr><th colspan="5" class="'.$classGlobalChart.'">'
|
|
.$title
|
|
.'</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; line-height: 15px; font-size:'.$textSize.'%;">'.
|
|
sprintf(get_lang('Incorrect answers: %s'), $nbResponsesInc).'
|
|
</td>
|
|
<td class="firstLine borderRight '.$classGlobalChart.'"
|
|
style="width:'.$colWidth.'px; line-height: 15px; font-size :'.$textSize.'%;">'.
|
|
sprintf(get_lang('Ignorance: %s'), $nbResponsesIng).'
|
|
</td>
|
|
<td class="firstLine '.$classGlobalChart.'"
|
|
colspan="2"
|
|
style="width:'.($colWidth * 2).'px; line-height: 15px; font-size:'.$textSize.'%;">'.
|
|
sprintf(get_lang('Correct answers: %s'), $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 (1 == $i || 2 == $i) {
|
|
$html .= '<td width="'
|
|
.$colWidth
|
|
.'px" style="border-right: 1px dotted #7FC5FF; vertical-align: bottom;font-size: '
|
|
.$textSize
|
|
.'%;">'
|
|
;
|
|
} else {
|
|
$html .= '<td width="'
|
|
.$colWidth
|
|
.'px" style="vertical-align: bottom;font-size: '
|
|
.$textSize
|
|
.'%;">'
|
|
;
|
|
}
|
|
$html .= '<div class="certaint-score">'
|
|
.$scoreOnBottom
|
|
.'</div><div class="levelbar_'
|
|
.$color
|
|
.'" style="height: '
|
|
.$sizeOnBottom
|
|
.'px;"> </div>'
|
|
;
|
|
$html .= '</td>';
|
|
}
|
|
|
|
$html .= '</tr>';
|
|
|
|
if ($displayExplanationText) {
|
|
// Display of histogram text
|
|
$explainHistoList = [
|
|
'Very unsure',
|
|
'Unsure',
|
|
'Declared ignorance',
|
|
'Pretty sure',
|
|
'Very sure',
|
|
];
|
|
$html .= '<tr>';
|
|
$i = 0;
|
|
foreach ($explainHistoList as $explain) {
|
|
if (1 == $i || 2 == $i) {
|
|
$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 ('0000-00-00 00:00:00' == $attemptDate) {
|
|
// 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 (0 == Database::num_rows($res)) {
|
|
// 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 self();
|
|
$oQuestion->read($attemptInfo['question_id']);
|
|
if (MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY == $oQuestion->type) {
|
|
$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 self();
|
|
$oQuestion->read($attemptInfo['question_id']);
|
|
if (MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY == $oQuestion->type) {
|
|
$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 (1 != count($attemptInfoList)) {
|
|
// 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 (0 == $correctAnswerOptionId) {
|
|
// error returning the correct answer option id
|
|
return 0;
|
|
}
|
|
|
|
// get student answer option id
|
|
$studentAnswerOptionId = isset($splitAnswer[1]) ? $splitAnswer[1] : null;
|
|
|
|
// we got the correct answer option id, let's compare ti with the student answer
|
|
$percentage = null;
|
|
if (isset($splitAnswer[2])) {
|
|
$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 (0 == Database::num_rows($res)) {
|
|
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;
|
|
$exeId = (int) $exeId;
|
|
|
|
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->getQuestionCount();
|
|
}
|
|
|
|
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('Your overall results for the test'),
|
|
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('In comparison, your latest results for this test'),
|
|
2
|
|
);
|
|
$html .= '<br/>';
|
|
}
|
|
|
|
$list = self::getColorNumberListForAttemptByCategory($exeId);
|
|
$html .= self::displayDegreeChartByCategory(
|
|
$list,
|
|
get_lang('Your results by discipline'),
|
|
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('Please do not reply').'] '
|
|
.html_entity_decode(get_lang('Results for the accomplished test').' "'.$objExercise->title.'"');
|
|
|
|
// message sended to the student
|
|
$message = get_lang('Dear').' '.$recipientName.',<br /><br />';
|
|
$exerciseLink = "<a href='".api_get_path(WEB_CODE_PATH).'/exercise/result.php?show_headers=1&'
|
|
.api_get_cidreq()
|
|
."&id=$exeId'>";
|
|
$exerciseTitle = $objExercise->title;
|
|
|
|
$message .= sprintf(
|
|
get_lang('Please follow the instructions below to check your results for test %s.<br /><br />'),
|
|
$exerciseTitle,
|
|
api_get_path(WEB_PATH),
|
|
$exerciseLink
|
|
);
|
|
|
|
// show histogram
|
|
$message .= self::displayStudentsChartResults($exeId, $objExercise);
|
|
$message .= get_lang('Kind regards,');
|
|
$message = api_preg_replace("/\\\n/", '', $message);
|
|
|
|
MessageManager::send_message_simple($userId, $subject, $message);
|
|
}
|
|
}
|
|
|