Fix fill in blanks errors see #2053

- Use a hash of the value (sha1) instead of doing value="my text "
pull/2487/head
jmontoyaa 8 years ago
parent 8400dd6f8c
commit b9b2f12066
  1. 3
      main/exercise/answer.class.php
  2. 84
      main/exercise/exercise.class.php
  3. 351
      main/exercise/fill_blanks.class.php
  4. 39
      main/inc/ajax/exercise.ajax.php
  5. 73
      main/inc/lib/exercise.lib.php

@ -345,6 +345,7 @@ class Answer
/**
* return array answer by id else return a bool
* @param integer $auto_id
* @return array
*/
public function selectAnswerByAutoId($auto_id)
{
@ -492,7 +493,7 @@ class Answer
*
* @author Olivier Brouckaert
* @param - integer $id - answer ID
* @param integer $id
* @return integer - answer weighting
*/
public function selectWeighting($id)

@ -3779,7 +3779,7 @@ class Exercise
$str = $answerFromDatabase = Database::result($result, 0, 'answer');
}
if ($saved_results == false && strpos($str, 'font color') !== false) {
if ($saved_results == false && strpos($answerFromDatabase, 'font color') !== false) {
// the question is encoded like this
// [A] B [C] D [E] F::10,10,10@1
// number 1 before the "@" means that is a switchable fill in blank question
@ -3891,7 +3891,7 @@ class Exercise
$totalScore += $answerWeighting[$i];
// adds the word in green at the end of the string
$answer .= $user_tags[$i];
} elseif (!empty ($user_tags[$i])) {
} elseif (!empty($user_tags[$i])) {
// else if the word entered by the student IS NOT the same as the one defined by the professor
// adds the word in red at the end of the string, and strikes it
$answer .= '<font color="red"><s>'.$user_tags[$i].'</s></font>';
@ -3916,7 +3916,7 @@ class Exercise
);
$switchableAnswerSet = $listCorrectAnswers['switchable'];
$answerWeighting = $listCorrectAnswers['tabweighting'];
$answerWeighting = $listCorrectAnswers['weighting'];
// user choices is an array $choice
// get existing user data in n the BDD
@ -3925,15 +3925,15 @@ class Exercise
$answerFromDatabase,
true
);
$choice = $listStudentResults['studentanswer'];
$choice = $listStudentResults['student_answer'];
}
// loop other all blanks words
if (!$switchableAnswerSet) {
// not switchable answer, must be in the same place than teacher order
for ($i = 0; $i < count($listCorrectAnswers['tabwords']); $i++) {
for ($i = 0; $i < count($listCorrectAnswers['words']); $i++) {
$studentAnswer = isset($choice[$i]) ? $choice[$i] : '';
$correctAnswer = $listCorrectAnswers['tabwords'][$i];
$correctAnswer = $listCorrectAnswers['words'][$i];
// This value is the user input, not escaped while correct answer is escaped by fckeditor
// Works with cyrillic alphabet and when using ">" chars see #7718 #7610 #7618
@ -3943,30 +3943,62 @@ class Exercise
}
$isAnswerCorrect = 0;
if (FillBlanks::isGoodStudentAnswer($studentAnswer, $correctAnswer)) {
if (FillBlanks::isStudentAnswerGood($studentAnswer, $correctAnswer, $from_database)) {
// gives the related weighting to the student
$questionScore += $answerWeighting[$i];
// increments total score
$totalScore += $answerWeighting[$i];
$isAnswerCorrect = 1;
}
$listCorrectAnswers['studentanswer'][$i] = $studentAnswer;
$listCorrectAnswers['studentscore'][$i] = $isAnswerCorrect;
$studentAnswerToShow = $studentAnswer;
$type = FillBlanks::getFillTheBlankAnswerType($correctAnswer);
if ($type == FillBlanks::FILL_THE_BLANK_MENU) {
$listMenu = FillBlanks::getFillTheBlankMenuAnswers($correctAnswer, false);
if ($studentAnswer != '') {
foreach ($listMenu as $item) {
if (sha1($item) == $studentAnswer) {
$studentAnswerToShow = $item;
}
}
}
}
$listCorrectAnswers['student_answer'][$i] = $studentAnswerToShow;
$listCorrectAnswers['student_score'][$i] = $isAnswerCorrect;
}
} else {
// switchable answer
$listStudentAnswerTemp = $choice;
$listTeacherAnswerTemp = $listCorrectAnswers['tabwords'];
$listTeacherAnswerTemp = $listCorrectAnswers['words'];
// for every teacher answer, check if there is a student answer
for ($i = 0; $i < count($listStudentAnswerTemp); $i++) {
$studentAnswer = trim($listStudentAnswerTemp[$i]);
$studentAnswerToShow = $studentAnswer;
$found = false;
for ($j = 0; $j < count($listTeacherAnswerTemp); $j++) {
$correctAnswer = $listTeacherAnswerTemp[$j];
$type = FillBlanks::getFillTheBlankAnswerType($correctAnswer);
if ($type == FillBlanks::FILL_THE_BLANK_MENU) {
$listMenu = FillBlanks::getFillTheBlankMenuAnswers($correctAnswer, false);
if (!empty($studentAnswer)) {
//var_dump($listMenu, $correctAnswer);
foreach ($listMenu as $key => $item) {
if ($key == $correctAnswer) {
$studentAnswerToShow = $item;
break;
}
}
}
}
if (!$found) {
if (FillBlanks::isGoodStudentAnswer(
if (FillBlanks::isStudentAnswerGood(
$studentAnswer,
$correctAnswer
$correctAnswer,
$from_database
)
) {
$questionScore += $answerWeighting[$i];
@ -3976,17 +4008,21 @@ class Exercise
}
}
}
$listCorrectAnswers['studentanswer'][$i] = $studentAnswer;
$listCorrectAnswers['student_answer'][$i] = $studentAnswerToShow;
if (!$found) {
$listCorrectAnswers['studentscore'][$i] = 0;
$listCorrectAnswers['student_score'][$i] = 0;
} else {
$listCorrectAnswers['studentscore'][$i] = 1;
$listCorrectAnswers['student_score'][$i] = 1;
}
}
}
$answer = FillBlanks::getAnswerInStudentAttempt(
$listCorrectAnswers
);
if( $saved_results) {
//var_dump($listCorrectAnswers);
}
}
break;
case CALCULATED_ANSWER:
@ -4025,13 +4061,15 @@ class Exercise
$answer .= $temp;
break;
}
if ($from_database) {
$queryfill = "SELECT answer FROM ".$TBL_TRACK_ATTEMPT."
WHERE
exe_id = '".$exeId."' AND
question_id= ".intval($questionId);
$resfill = Database::query($queryfill);
$str = Database::result($resfill, 0, 'answer');
$sql = "SELECT answer FROM ".$TBL_TRACK_ATTEMPT."
WHERE
exe_id = '".$exeId."' AND
question_id = ".intval($questionId);
$result = Database::query($sql);
$str = Database::result($result, 0, 'answer');
api_preg_match_all('#\[([^[]*)\]#', $str, $arr);
$str = str_replace('\r\n', '', $str);
$choice = $arr[1];
@ -4103,8 +4141,8 @@ class Exercise
WHERE
exe_id = $exeId AND
question_id= ".$questionId;
$resq = Database::query($sql);
$data = Database::fetch_array($resq);
$result = Database::query($sql);
$data = Database::fetch_array($result);
$choice = $data['answer'];
$choice = str_replace('\r\n', '', $choice);

@ -10,13 +10,13 @@
**/
class FillBlanks extends Question
{
public static $typePicture = 'fill_in_blanks.png';
public static $explanationLangVar = 'FillBlanks';
const FILL_THE_BLANK_STANDARD = 0;
const FILL_THE_BLANK_MENU = 1;
const FILL_THE_BLANK_SEVERAL_ANSWER = 2;
public static $typePicture = 'fill_in_blanks.png';
public static $explanationLangVar = 'FillBlanks';
/**
* Constructor
*/
@ -32,7 +32,7 @@ class FillBlanks extends Question
*/
public function createAnswersForm($form)
{
$defaults = array();
$defaults = [];
if (!empty($this->id)) {
$objectAnswer = new Answer($this->id);
$answer = $objectAnswer->selectAnswer(1);
@ -42,10 +42,10 @@ class FillBlanks extends Question
} else {
$defaults['multiple_answer'] = 0;
}
//take the complete string except after the last '::'
// Take the complete string except after the last '::'
$defaults['answer'] = $listAnswersInfo['text'];
$defaults['select_separator'] = $listAnswersInfo['blankseparatornumber'];
$blankSeparatorNumber = $listAnswersInfo['blankseparatornumber'];
$defaults['select_separator'] = $listAnswersInfo['blank_separator_number'];
$blankSeparatorNumber = $listAnswersInfo['blank_separator_number'];
} else {
$defaults['answer'] = get_lang('DefaultTextInBlanks');
$defaults['select_separator'] = 0;
@ -54,20 +54,18 @@ class FillBlanks extends Question
$blankSeparatorStart = self::getStartSeparator($blankSeparatorNumber);
$blankSeparatorEnd = self::getEndSeparator($blankSeparatorNumber);
$setWeightAndSize = '';
if (isset($listAnswersInfo) && count($listAnswersInfo['tabweighting']) > 0) {
foreach ($listAnswersInfo['tabweighting'] as $i => $weighting) {
if (isset($listAnswersInfo) && count($listAnswersInfo['weighting']) > 0) {
foreach ($listAnswersInfo['weighting'] as $i => $weighting) {
$setWeightAndSize .= 'document.getElementById("weighting['.$i.']").value = "'.$weighting.'";';
}
foreach ($listAnswersInfo['tabinputsize'] as $i => $sizeOfInput) {
foreach ($listAnswersInfo['input_size'] as $i => $sizeOfInput) {
$setWeightAndSize .= 'document.getElementById("sizeofinput['.$i.']").value = "'.$sizeOfInput.'";';
$setWeightAndSize .= 'document.getElementById("samplesize['.$i.']").style.width = "'.$sizeOfInput.'px";';
}
}
echo '<script>
echo '<script>
var firstTime = true;
var originalOrder = new Array();
var blankSeparatorStart = "'.$blankSeparatorStart.'";
@ -97,13 +95,16 @@ class FillBlanks extends Question
// disable the save button, if not blanks have been created
$("button").attr("disabled", "disabled");
$("#defineoneblank").show();
$("#defineoneblank").show();
var blanks = answer.match(eval(blanksRegexp));
var fields = "<div class=\"form-group \">";
fields += "<label class=\"col-sm-2 control-label\">'.get_lang('Weighting').'</label>";
fields += "<label class=\"col-sm-2 control-label\"></label>";
fields += "<div class=\"col-sm-8\">";
fields += "<table>";
fields += "<tr><th style=\"padding:0 20px\">'.get_lang("WordTofind").'</th><th style=\"padding:0 20px\">'.get_lang("QuestionWeighting").'</th><th style=\"padding:0 20px\">'.get_lang("BlankInputSize").'</th></tr>";
fields += "<table class=\"data_table\">";
fields += "<tr><th style=\"width:220px\">'.get_lang("WordTofind").'</th>";
fields += "<th style=\"width:50px\">'.get_lang("QuestionWeighting").'</th>";
fields += "<th>'.get_lang("BlankInputSize").'</th></tr>";
if (blanks != null) {
for (var i=0; i < blanks.length; i++) {
@ -133,15 +134,16 @@ class FillBlanks extends Question
var value = document.getElementById("weighting["+i+"]").value;
} else {
var value = "1";
}
}
var blanksWithColor = trimBlanksBetweenSeparator(blanks[i], blankSeparatorStart, blankSeparatorEnd, 1);
fields += "<tr>";
fields += "<td>"+blanks[i]+"</td>";
fields += "<td><input style=\"width:35px\" value=\""+value+"\" type=\"text\" id=\"weighting["+i+"]\" name=\"weighting["+i+"]\" /></td>";
fields += "<td>"+blanksWithColor+"</td>";
fields += "<td><input class=\"form-control\" style=\"width:60px\" value=\""+value+"\" type=\"text\" id=\"weighting["+i+"]\" name=\"weighting["+i+"]\" /></td>";
fields += "<td>";
fields += "<input class=\"btn btn-default\" type=\"button\" value=\"-\" onclick=\"changeInputSize(-1, "+i+")\">&nbsp;";
fields += "<input class=\"btn btn-default\" type=\"button\" value=\"+\" onclick=\"changeInputSize(1, "+i+")\">&nbsp;";
fields += "<input class=\"sample\" id=\"samplesize["+i+"]\" data-btoa=\""+btoaValue+"\" type=\"text\" value=\""+textValue+"\" style=\"width:"+inputSize+"px\" disabled=disabled />";
fields += "&nbsp;&nbsp;<input class=\"sample\" id=\"samplesize["+i+"]\" data-btoa=\""+btoaValue+"\" type=\"text\" value=\""+textValue+"\" style=\"width:"+inputSize+"px\" disabled=disabled />";
fields += "<input id=\"sizeofinput["+i+"]\" type=\"hidden\" value=\""+inputSize+"\" name=\"sizeofinput["+i+"]\" />";
fields += "</td>";
fields += "</tr>";
@ -152,7 +154,8 @@ class FillBlanks extends Question
}
}
document.getElementById("blanks_weighting").innerHTML = fields + "</table></div></div>";
document.getElementById("blanks_weighting").innerHTML = fields + "</table></div></div>";
$(originalOrder).each(function(i, data) {
if (firstTime == false) {
value = data.value;
@ -235,7 +238,7 @@ class FillBlanks extends Question
$("#samplesize\\\["+inIdNum+"\\\]").outerWidth(newWidth);
$("#sizeofinput\\\["+inIdNum+"\\\]").attr("value", newWidth);
updateOrder(blanks);
updateOrder(blanks);
}
function removeForbiddenChars(inTxt)
@ -279,27 +282,44 @@ class FillBlanks extends Question
// this function is the same than the PHP one
// if modify it modify the php one getAllowedSeparator
function getSeparatorFromNumber(innumber)
function getSeparatorFromNumber(number)
{
tabSeparator = new Array();
tabSeparator[0] = new Array("[", "]");
tabSeparator[1] = new Array("{", "}");
tabSeparator[2] = new Array("(", ")");
tabSeparator[3] = new Array("*", "*");
tabSeparator[4] = new Array("#", "#");
tabSeparator[5] = new Array("%", "%");
tabSeparator[6] = new Array("$", "$");
return tabSeparator[innumber];
var separator = new Array();
separator[0] = new Array("[", "]");
separator[1] = new Array("{", "}");
separator[2] = new Array("(", ")");
separator[3] = new Array("*", "*");
separator[4] = new Array("#", "#");
separator[5] = new Array("%", "%");
separator[6] = new Array("$", "$");
return separator[number];
}
function trimBlanksBetweenSeparator(inTxt, inSeparatorStart, inSeparatorEnd)
function trimBlanksBetweenSeparator(inTxt, inSeparatorStart, inSeparatorEnd, addColor)
{
var result = inTxt
result = result.replace(inSeparatorStart, "");
result = result.replace(inSeparatorEnd, "");
result = result.trim();
if (addColor == 1) {
var resultParts = result.split("|");
var partsToString = "";
resultParts.forEach(function(item, index) {
if (index == 0) {
item = "<b><font style=\"color:green\"> " + item +"</font></b>";
}
if (index < resultParts.length - 1) {
item = item + " | ";
}
partsToString += item;
});
result = partsToString;
}
return inSeparatorStart+result+inSeparatorEnd;
}
</script>';
// answer
@ -321,14 +341,15 @@ class FillBlanks extends Question
$form->addElement(
'select',
'select_separator',
get_lang("SelectFillTheBlankSeparator"),
get_lang('SelectFillTheBlankSeparator'),
self::getAllowedSeparatorForSelect(),
' id="select_separator" style="width:150px" onchange="changeBlankSeparator()" '
' id="select_separator" style="width:150px" class="selectpicker" onchange="changeBlankSeparator()" '
);
$form->addLabel(
null,
'<input type="button" onclick="updateBlanks()" value="'.get_lang('RefreshBlanks').'" class="btn btn-default" />'
);
$form->addHtml('<div id="blanks_weighting"></div>');
global $text;
@ -424,7 +445,7 @@ class FillBlanks extends Question
$answer .= ",";
}
// calculate the global weighting for the question
$this -> weighting += $form->getSubmitValue('weighting['.$i.']');
$this->weighting += (float) $form->getSubmitValue('weighting['.$i.']');
}
// input width
@ -497,8 +518,9 @@ class FillBlanks extends Question
$displayForStudent,
$inBlankNumber
) {
$inTabTeacherSolution = $listAnswersInfo['tabwords'];
$inTabTeacherSolution = $listAnswersInfo['words'];
$inTeacherSolution = $inTabTeacherSolution[$inBlankNumber];
switch (self::getFillTheBlankAnswerType($inTeacherSolution)) {
case self::FILL_THE_BLANK_MENU:
$selected = '';
@ -512,19 +534,14 @@ class FillBlanks extends Question
$resultOptions = ['' => '--'];
foreach ($listMenu as $item) {
$item = self::trimOption($item);
$resultOptions[$item] = $item;
$resultOptions[sha1($item)] = $item;
}
for ($k = 0; $k < count($listMenu); $k++) {
if ($correctItem == $listMenu[$k]) {
$selected = $k;
//var_dump($resultOptions, $correctItem);
break;
}
// if in teacher view, display the first item by default, which is the right answer
if ($k == 0 && !$displayForStudent) {
$selected = $k;
foreach ($resultOptions as $key => $value) {
if ($correctItem == $value) {
$selected = $key;
break;
}
@ -555,12 +572,17 @@ class FillBlanks extends Question
return $result;
}
/**
* Removes double spaces between words
* @param string $text
* @return string
*/
private static function trimOption($text)
{
$converted = strtr($text, array_flip(get_html_translation_table(HTML_ENTITIES, ENT_QUOTES)));
$trimmed = trim($converted, chr(0xC2).chr(0xA0).' ');
$text = trim($text);
$text = preg_replace("/\s+/", " ", $text);
return $trimmed;
return $text;
}
/**
@ -574,8 +596,13 @@ class FillBlanks extends Question
public static function getFillTheBlankMenuAnswers($correctAnswer, $displayForStudent)
{
$list = api_preg_split("/\|/", $correctAnswer);
foreach ($list as &$item) {
$item = self::trimOption($item);
$item = api_html_entity_decode($item);
}
// The list is always in the same order, there's no option to allow or disable shuffle options.
if ($displayForStudent) {
shuffle($list);
shuffle_assoc($list);
}
return $list;
@ -620,33 +647,49 @@ class FillBlanks extends Question
* Return true if student answer is right according to the correctAnswer
* it is not as simple as equality, because of the type of Fill The Blank question
* eg : studentAnswer = 'Un' and correctAnswer = 'Un||1||un'
* @param string $studentAnswer [studentanswer] of the info array of the answer field
* @param string $correctAnswer [tabwords] of the info array of the answer field
*
* @param string $studentAnswer [student_answer] of the info array of the answer field
* @param string $correctAnswer [words] of the info array of the answer field
* @param bool $fromDatabase
* @return bool
*/
public static function isGoodStudentAnswer($studentAnswer, $correctAnswer)
public static function isStudentAnswerGood($studentAnswer, $correctAnswer, $fromDatabase = false)
{
$result = false;
switch (self::getFillTheBlankAnswerType($correctAnswer)) {
case self::FILL_THE_BLANK_MENU:
$listMenu = self::getFillTheBlankMenuAnswers($correctAnswer, false);
$result = self::trimOption($listMenu[0]) == $studentAnswer;
if ($studentAnswer != '' && isset($listMenu[0])) {
// First item is always the correct one.
$item = $listMenu[0];
if (!$fromDatabase) {
$item = sha1($item);
}
if ($item === $studentAnswer) {
$result = true;
}
}
break;
case self::FILL_THE_BLANK_SEVERAL_ANSWER:
// the answer must be one of the choice made
$listSeveral = self::getFillTheBlankSeveralAnswers($correctAnswer);
$listSeveral = array_map(function($item) {
return self::trimOption($item);
}, $listSeveral);
$listSeveral = array_map(
function ($item) {
return self::trimOption($item);
},
$listSeveral
);
$result = in_array($studentAnswer, $listSeveral);
break;
case self::FILL_THE_BLANK_STANDARD:
default:
$correctAnswer = api_html_entity_decode($correctAnswer);
$studentAnswer = htmlspecialchars($studentAnswer);
$result = $studentAnswer == self::trimOption($correctAnswer);
break;
}
//var_dump($result);
return $result;
}
@ -658,9 +701,9 @@ class FillBlanks extends Question
*/
public static function getFillTheBlankAnswerType($correctAnswer)
{
if (api_strpos($correctAnswer, "|") && !api_strpos($correctAnswer, "||")) {
if (api_strpos($correctAnswer, '|') && !api_strpos($correctAnswer, '||')) {
return self::FILL_THE_BLANK_MENU;
} elseif (api_strpos($correctAnswer, "||")) {
} elseif (api_strpos($correctAnswer, '||')) {
return self::FILL_THE_BLANK_SEVERAL_ANSWER;
} else {
return self::FILL_THE_BLANK_STANDARD;
@ -674,20 +717,20 @@ class FillBlanks extends Question
*
* @return array of information about the answer
*/
public static function getAnswerInfo($userAnswer = "", $isStudentAnswer = false)
public static function getAnswerInfo($userAnswer = '', $isStudentAnswer = false)
{
$listAnswerResults = array();
$listAnswerResults = [];
$listAnswerResults['text'] = '';
$listAnswerResults['wordsCount'] = 0;
$listAnswerResults['tabwordsbracket'] = array();
$listAnswerResults['tabwords'] = array();
$listAnswerResults['tabweighting'] = array();
$listAnswerResults['tabinputsize'] = array();
$listAnswerResults['words_count'] = 0;
$listAnswerResults['words_with_bracket'] = [];
$listAnswerResults['words'] = [];
$listAnswerResults['weighting'] = [];
$listAnswerResults['input_size'] = [];
$listAnswerResults['switchable'] = '';
$listAnswerResults['studentanswer'] = array();
$listAnswerResults['studentscore'] = array();
$listAnswerResults['blankseparatornumber'] = 0;
$listDoubleColon = array();
$listAnswerResults['student_answer'] = [];
$listAnswerResults['student_score'] = [];
$listAnswerResults['blank_separator_number'] = 0;
$listDoubleColon = [];
api_preg_match("/(.*)::(.*)$/s", $userAnswer, $listResult);
@ -699,22 +742,22 @@ class FillBlanks extends Question
$listDoubleColon[] = $listResult[2];
}
$listAnswerResults['systemstring'] = $listDoubleColon[1];
$listAnswerResults['system_string'] = $listDoubleColon[1];
// make sure we only take the last bit to find special marks
// Make sure we only take the last bit to find special marks
$listArobaseSplit = explode('@', $listDoubleColon[1]);
if (count($listArobaseSplit) < 2) {
$listArobaseSplit[1] = '';
}
// take the complete string except after the last '::'
// Take the complete string except after the last '::'
$listDetails = explode(":", $listArobaseSplit[0]);
// < number of item after the ::[score]:[size]:[separator_id]@ , here there are 3
if (count($listDetails) < 3) {
$listWeightings = explode(',', $listDetails[0]);
$listSizeOfInput = array();
$listSizeOfInput = [];
for ($i = 0; $i < count($listWeightings); $i++) {
$listSizeOfInput[] = 200;
}
@ -726,27 +769,27 @@ class FillBlanks extends Question
}
$listAnswerResults['text'] = $listDoubleColon[0];
$listAnswerResults['tabweighting'] = $listWeightings;
$listAnswerResults['tabinputsize'] = $listSizeOfInput;
$listAnswerResults['weighting'] = $listWeightings;
$listAnswerResults['input_size'] = $listSizeOfInput;
$listAnswerResults['switchable'] = $listArobaseSplit[1];
$listAnswerResults['blankseparatorstart'] = self::getStartSeparator($blankSeparatorNumber);
$listAnswerResults['blankseparatorend'] = self::getEndSeparator($blankSeparatorNumber);
$listAnswerResults['blankseparatornumber'] = $blankSeparatorNumber;
$listAnswerResults['blank_separator_start'] = self::getStartSeparator($blankSeparatorNumber);
$listAnswerResults['blank_separator_end'] = self::getEndSeparator($blankSeparatorNumber);
$listAnswerResults['blank_separator_number'] = $blankSeparatorNumber;
$blankCharStart = self::getStartSeparator($blankSeparatorNumber);
$blankCharEnd = self::getEndSeparator($blankSeparatorNumber);
$blankCharStartForRegexp = self::escapeForRegexp($blankCharStart);
$blankCharEndForRegexp = self::escapeForRegexp($blankCharEnd);
// get all blanks words
$listAnswerResults['wordsCount'] = api_preg_match_all(
// Get all blanks words
$listAnswerResults['words_count'] = api_preg_match_all(
'/'.$blankCharStartForRegexp.'[^'.$blankCharEndForRegexp.']*'.$blankCharEndForRegexp.'/',
$listDoubleColon[0],
$listWords
);
if ($listAnswerResults['wordsCount'] > 0) {
$listAnswerResults['tabwordsbracket'] = $listWords[0];
if ($listAnswerResults['words_count'] > 0) {
$listAnswerResults['words_with_bracket'] = $listWords[0];
// remove [ and ] in string
array_walk(
$listWords[0],
@ -759,10 +802,10 @@ class FillBlanks extends Question
},
array($blankCharStart, $blankCharEnd)
);
$listAnswerResults['tabwords'] = $listWords[0];
$listAnswerResults['words'] = $listWords[0];
}
// get all common words
// Get all common words
$commonWords = api_preg_replace(
'/'.$blankCharStartForRegexp.'[^'.$blankCharEndForRegexp.']*'.$blankCharEndForRegexp.'/',
"::",
@ -771,32 +814,31 @@ class FillBlanks extends Question
// if student answer, the second [] is the student answer,
// the third is if student scored or not
$listBrackets = array();
$listWords = array();
$listBrackets = [];
$listWords = [];
if ($isStudentAnswer) {
for ($i = 0; $i < count($listAnswerResults['tabwords']); $i++) {
$listBrackets[] = $listAnswerResults['tabwordsbracket'][$i];
$listWords[] = $listAnswerResults['tabwords'][$i];
if ($i + 1 < count($listAnswerResults['tabwords'])) {
for ($i = 0; $i < count($listAnswerResults['words']); $i++) {
$listBrackets[] = $listAnswerResults['words_with_bracket'][$i];
$listWords[] = $listAnswerResults['words'][$i];
if ($i + 1 < count($listAnswerResults['words'])) {
// should always be
$i++;
}
$listAnswerResults['studentanswer'][] = $listAnswerResults['tabwords'][$i];
if ($i + 1 < count($listAnswerResults['tabwords'])) {
$listAnswerResults['student_answer'][] = $listAnswerResults['words'][$i];
if ($i + 1 < count($listAnswerResults['words'])) {
// should always be
$i++;
}
$listAnswerResults['studentscore'][] = $listAnswerResults['tabwords'][$i];
$listAnswerResults['student_score'][] = $listAnswerResults['words'][$i];
}
$listAnswerResults['tabwords'] = $listWords;
$listAnswerResults['tabwordsbracket'] = $listBrackets;
$listAnswerResults['words'] = $listWords;
$listAnswerResults['words_with_bracket'] = $listBrackets;
// if we are in student view, we've got 3 times :::::: for common words
$commonWords = api_preg_replace("/::::::/", "::", $commonWords);
}
$listAnswerResults['commonwords'] = explode("::", $commonWords);
$listAnswerResults['common_words'] = explode("::", $commonWords);
return $listAnswerResults;
}
@ -838,7 +880,7 @@ class FillBlanks extends Question
$courseId = api_get_course_int_id();
// If no user has answered questions, no need to go further. Return empty array.
if (empty($studentsIdList)) {
return array();
return [];
}
// request to have all the answers of student for this question
// student may have doing it several time
@ -860,29 +902,29 @@ class FillBlanks extends Question
';
$res = Database::query($sql);
$tabUserResult = array();
$tabUserResult = [];
// foreach attempts for all students starting with his older attempt
while ($data = Database::fetch_array($res)) {
$tabAnswer = self::getAnswerInfo($data['answer'], true);
// for each bracket to find in this question
foreach ($tabAnswer['studentanswer'] as $bracketNumber => $studentAnswer) {
if ($tabAnswer['studentanswer'][$bracketNumber] != '') {
foreach ($tabAnswer['student_answer'] as $bracketNumber => $studentAnswer) {
if ($tabAnswer['student_answer'][$bracketNumber] != '') {
// student has answered this bracket, cool
switch (self::getFillTheBlankAnswerType($tabAnswer['tabwords'][$bracketNumber])) {
switch (self::getFillTheBlankAnswerType($tabAnswer['words'][$bracketNumber])) {
case self::FILL_THE_BLANK_MENU:
// get the indice of the choosen answer in the menu
// we know that the right answer is the first entry of the menu, ie 0
// (remember, menu entries are shuffled when taking the test)
$tabUserResult[$data['user_id']][$bracketNumber] = self::getFillTheBlankMenuAnswerNum(
$tabAnswer['tabwords'][$bracketNumber],
$tabAnswer['studentanswer'][$bracketNumber]
$tabAnswer['words'][$bracketNumber],
$tabAnswer['student_answer'][$bracketNumber]
);
break;
default:
if (self::isGoodStudentAnswer(
$tabAnswer['studentanswer'][$bracketNumber],
$tabAnswer['tabwords'][$bracketNumber]
if (self::isStudentAnswerGood(
$tabAnswer['student_answer'][$bracketNumber],
$tabAnswer['words'][$bracketNumber]
)
) {
$tabUserResult[$data['user_id']][$bracketNumber] = 0; // right answer
@ -939,20 +981,20 @@ class FillBlanks extends Question
*/
public static function getAnswerInStudentAttempt($listWithStudentAnswer)
{
$separatorStart = $listWithStudentAnswer['blankseparatorstart'];
$separatorEnd = $listWithStudentAnswer['blankseparatorend'];
$separatorStart = $listWithStudentAnswer['blank_separator_start'];
$separatorEnd = $listWithStudentAnswer['blank_separator_end'];
// lets rebuild the sentence with [correct answer][student answer][answer is correct]
$result = '';
for ($i = 0; $i < count($listWithStudentAnswer['commonwords']) - 1; $i++) {
$result .= $listWithStudentAnswer['commonwords'][$i];
$result .= $listWithStudentAnswer['tabwordsbracket'][$i];
$result .= $separatorStart.$listWithStudentAnswer['studentanswer'][$i].$separatorEnd;
$result .= $separatorStart.$listWithStudentAnswer['studentscore'][$i].$separatorEnd;
for ($i = 0; $i < count($listWithStudentAnswer['common_words']) - 1; $i++) {
$result .= $listWithStudentAnswer['common_words'][$i];
$result .= $listWithStudentAnswer['words_with_bracket'][$i];
$result .= $separatorStart.$listWithStudentAnswer['student_answer'][$i].$separatorEnd;
$result .= $separatorStart.$listWithStudentAnswer['student_score'][$i].$separatorEnd;
}
$result .= $listWithStudentAnswer['commonwords'][$i];
$result .= $listWithStudentAnswer['common_words'][$i];
$result .= "::";
// add the system string
$result .= $listWithStudentAnswer['systemstring'];
$result .= $listWithStudentAnswer['system_string'];
return $result;
}
@ -1038,17 +1080,15 @@ class FillBlanks extends Question
*/
public static function getAllowedSeparator()
{
$fillBlanksAllowedSeparator = array(
array('[', ']'),
array('{', '}'),
array('(', ')'),
array('*', '*'),
array('#', '#'),
array('%', '%'),
array('$', '$'),
);
return $fillBlanksAllowedSeparator;
return [
['[', ']'],
['{', '}'],
['(', ')'],
['*', '*'],
['#', '#'],
['%', '%'],
['$', '$'],
];
}
/**
@ -1084,10 +1124,10 @@ class FillBlanks extends Question
*/
public static function getAllowedSeparatorForSelect()
{
$listResults = array();
$fillBlanksAllowedSeparator = self::getAllowedSeparator();
for ($i = 0; $i < count($fillBlanksAllowedSeparator); $i++) {
$listResults[] = $fillBlanksAllowedSeparator[$i][0]."...".$fillBlanksAllowedSeparator[$i][1];
$listResults = [];
$allowedSeparator = self::getAllowedSeparator();
foreach ($allowedSeparator as $part) {
$listResults[] = $part[0]."...".$part[1];
}
return $listResults;
@ -1142,19 +1182,19 @@ class FillBlanks extends Question
// rebuild the answer with good HTML style
// this is the student answer, right or wrong
for ($i = 0; $i < count($listStudentAnswerInfo['studentanswer']); $i++) {
if ($listStudentAnswerInfo['studentscore'][$i] == 1) {
$listStudentAnswerInfo['studentanswer'][$i] = self::getHtmlRightAnswer(
$listStudentAnswerInfo['studentanswer'][$i],
$listStudentAnswerInfo['tabwords'][$i],
for ($i = 0; $i < count($listStudentAnswerInfo['student_answer']); $i++) {
if ($listStudentAnswerInfo['student_score'][$i] == 1) {
$listStudentAnswerInfo['student_answer'][$i] = self::getHtmlRightAnswer(
$listStudentAnswerInfo['student_answer'][$i],
$listStudentAnswerInfo['words'][$i],
$feedbackType,
$resultsDisabled,
$showTotalScoreAndUserChoices
);
} else {
$listStudentAnswerInfo['studentanswer'][$i] = self::getHtmlWrongAnswer(
$listStudentAnswerInfo['studentanswer'][$i],
$listStudentAnswerInfo['tabwords'][$i],
$listStudentAnswerInfo['student_answer'][$i] = self::getHtmlWrongAnswer(
$listStudentAnswerInfo['student_answer'][$i],
$listStudentAnswerInfo['words'][$i],
$feedbackType,
$resultsDisabled,
$showTotalScoreAndUserChoices
@ -1163,13 +1203,13 @@ class FillBlanks extends Question
}
// rebuild the sentence with student answer inserted
for ($i = 0; $i < count($listStudentAnswerInfo['commonwords']); $i++) {
$result .= isset($listStudentAnswerInfo['commonwords'][$i]) ? $listStudentAnswerInfo['commonwords'][$i] : '';
$result .= isset($listStudentAnswerInfo['studentanswer'][$i]) ? $listStudentAnswerInfo['studentanswer'][$i] : '';
for ($i = 0; $i < count($listStudentAnswerInfo['common_words']); $i++) {
$result .= isset($listStudentAnswerInfo['common_words'][$i]) ? $listStudentAnswerInfo['common_words'][$i] : '';
$result .= isset($listStudentAnswerInfo['student_answer'][$i]) ? $listStudentAnswerInfo['student_answer'][$i] : '';
}
// the last common word (should be </p>)
$result .= isset($listStudentAnswerInfo['commonwords'][$i]) ? $listStudentAnswerInfo['commonwords'][$i] : '';
$result .= isset($listStudentAnswerInfo['common_words'][$i]) ? $listStudentAnswerInfo['common_words'][$i] : '';
return $result;
}
@ -1254,7 +1294,9 @@ class FillBlanks extends Question
* return HTML code for correct answer
* @param string $answer
* @param string $correct
* @param bool $resultsDisabled
* @param string $feedbackType
* @param bool $resultsDisabled
* @param bool $showTotalScoreAndUserChoices
*
* @return string
*/
@ -1279,6 +1321,7 @@ class FillBlanks extends Question
* return HTML code for wrong answer
* @param string $answer
* @param string $correct
* @param string $feedbackType
* @param bool $resultsDisabled
*
* @return string
@ -1308,13 +1351,13 @@ class FillBlanks extends Question
public static function isCorrect($answerText)
{
$answerInfo = self::getAnswerInfo($answerText, true);
$correctAnswerList = $answerInfo['tabwords'];
$studentAnswer = $answerInfo['studentanswer'];
$correctAnswerList = $answerInfo['words'];
$studentAnswer = $answerInfo['student_answer'];
$isCorrect = true;
foreach ($correctAnswerList as $i => $correctAnswer) {
$isGoodStudentAnswer = self::isGoodStudentAnswer($studentAnswer[$i], $correctAnswer);
$isCorrect = $isCorrect && $isGoodStudentAnswer;
$value = self::isStudentAnswerGood($studentAnswer[$i], $correctAnswer);
$isCorrect = $isCorrect && $value;
}
return $isCorrect;

@ -270,10 +270,10 @@ switch ($action) {
$learnpath_item_id = isset($_REQUEST['learnpath_item_id']) ? intval($_REQUEST['learnpath_item_id']) : 0;
// Attempt id.
$exe_id = $_REQUEST['exe_id'];
$exeId = $_REQUEST['exe_id'];
if ($debug) {
error_log("exe_id = $exe_id");
error_log("exe_id = $exeId");
error_log("type = $type");
error_log("choice = ".print_r($choice, 1)." ");
error_log("hot_spot_coordinates = ".print_r($hot_spot_coordinates, 1));
@ -303,18 +303,18 @@ switch ($action) {
}
// Getting information of the current exercise.
$exercise_stat_info = $objExercise->get_stat_track_exercise_info_by_exe_id($exe_id);
$exercise_stat_info = $objExercise->get_stat_track_exercise_info_by_exe_id($exeId);
$exercise_id = $exercise_stat_info['exe_exo_id'];
$attemptList = array();
$attemptList = [];
// First time here we create an attempt (getting the exe_id).
if (!empty($exercise_stat_info)) {
// We know the user we get the exe_id.
$exe_id = $exercise_stat_info['exe_id'];
$exeId = $exercise_stat_info['exe_id'];
$total_score = $exercise_stat_info['exe_result'];
// Getting the list of attempts
$attemptList = Event::getAllExerciseEventByExeId($exe_id);
$attemptList = Event::getAllExerciseEventByExeId($exeId);
}
// Updating Reminder algorithm.
@ -341,7 +341,7 @@ switch ($action) {
}
// No exe id? Can't save answer.
if (empty($exe_id)) {
if (empty($exeId)) {
// Fires an error.
echo 'error';
if ($debug) {
@ -350,7 +350,7 @@ switch ($action) {
exit;
}
Session::write('exe_id', $exe_id);
Session::write('exe_id', $exeId);
// Getting the total weight if the request is simple
$total_weight = 0;
@ -383,9 +383,7 @@ switch ($action) {
$objQuestionTmp = Question::read($my_question_id, $course_id);
// Getting free choice data.
if (($objQuestionTmp->type == FREE_ANSWER || $objQuestionTmp->type == ORAL_EXPRESSION) &&
$type == 'all'
) {
if (in_array($objQuestionTmp->type, [FREE_ANSWER, ORAL_EXPRESSION]) && $type == 'all') {
$my_choice = isset($_REQUEST['free_choice'][$my_question_id]) && !empty($_REQUEST['free_choice'][$my_question_id])
? $_REQUEST['free_choice'][$my_question_id]
: null;
@ -402,20 +400,19 @@ switch ($action) {
) {
$hotspot_delineation_result = $_SESSION['hotspot_delineation_result'][$objExercise->selectId()][$my_question_id];
}
if ($type == 'simple') {
// Getting old attempt in order to decrees the total score.
$old_result = $objExercise->manage_answer(
$exe_id,
$exeId,
$my_question_id,
null,
'exercise_show',
array(),
[],
false,
true,
false,
$objExercise->selectPropagateNeg(),
array()
[]
);
// Removing old score.
@ -425,10 +422,10 @@ switch ($action) {
// Deleting old attempt
if (isset($attemptList) && !empty($attemptList[$my_question_id])) {
if ($debug) {
error_log("delete_attempt exe_id : $exe_id, my_question_id: $my_question_id");
error_log("delete_attempt exe_id : $exeId, my_question_id: $my_question_id");
}
Event::delete_attempt(
$exe_id,
$exeId,
api_get_user_id(),
$course_id,
$session_id,
@ -436,7 +433,7 @@ switch ($action) {
);
if ($objQuestionTmp->type == HOT_SPOT) {
Event::delete_attempt_hotspot(
$exe_id,
$exeId,
api_get_user_id(),
$course_id,
$session_id,
@ -453,7 +450,7 @@ switch ($action) {
// We're inside *one* question. Go through each possible answer for this question
$result = $objExercise->manage_answer(
$exe_id,
$exeId,
$my_question_id,
$my_choice,
'exercise_result',
@ -477,7 +474,7 @@ switch ($action) {
$now = time();
if ($type == 'all') {
$exercise_stat_info = $objExercise->get_stat_track_exercise_info_by_exe_id($exe_id);
$exercise_stat_info = $objExercise->get_stat_track_exercise_info_by_exe_id($exeId);
}
$key = ExerciseLib::get_time_control_key(
@ -501,7 +498,7 @@ switch ($action) {
$_SESSION['duration_time'][$key] = time();
Event::updateEventExercise(
$exe_id,
$exeId,
$objExercise->selectId(),
$total_score,
$total_weight,

@ -132,8 +132,8 @@ class ExerciseLib
$x = 1;
//mark letters for each answer
$letter = 'A';
$answer_matching = array();
$cpt1 = array();
$answer_matching = [];
$cpt1 = [];
for ($answerId = 1; $answerId <= $nbrAnswers; $answerId++) {
$answerCorrect = $objAnswerTmp->isCorrect($answerId);
$numAnswer = $objAnswerTmp->selectAutoId($answerId);
@ -292,8 +292,6 @@ class ExerciseLib
$objQuestionTmp->selectDescription()
);
$hidingClass = 'hide-reading-answers';
}
if ($answerType == READING_COMPREHENSION) {
$s .= Display::div(
$objQuestionTmp->selectTitle(),
['class' => 'question_title '.$hidingClass]
@ -305,7 +303,7 @@ class ExerciseLib
$answerCorrect = $objAnswerTmp->isCorrect($answerId);
$numAnswer = $objAnswerTmp->selectAutoId($answerId);
$comment = $objAnswerTmp->selectComment($answerId);
$attributes = array();
$attributes = [];
switch ($answerType) {
case UNIQUE_ANSWER:
@ -592,16 +590,16 @@ class ExerciseLib
$listAnswerInfo = FillBlanks::getAnswerInfo($answer);
// Correct answers
$correctAnswerList = $listAnswerInfo['tabwords'];
$correctAnswerList = $listAnswerInfo['words'];
// Student's answer
$studentAnswerList = array();
$studentAnswerList = [];
if (isset($user_choice[0]['answer'])) {
$arrayStudentAnswer = FillBlanks::getAnswerInfo(
$user_choice[0]['answer'],
true
);
$studentAnswerList = $arrayStudentAnswer['studentanswer'];
$studentAnswerList = $arrayStudentAnswer['student_answer'];
}
// If the question must be shown with the answer (in page exercise/admin.php) for teacher preview
@ -613,18 +611,18 @@ class ExerciseLib
if (!empty($correctAnswerList) && !empty($studentAnswerList)) {
$answer = '';
for ($i = 0; $i < count($listAnswerInfo['commonwords']) - 1; $i++) {
for ($i = 0; $i < count($listAnswerInfo['common_words']) - 1; $i++) {
// display the common word
$answer .= $listAnswerInfo['commonwords'][$i];
$answer .= $listAnswerInfo['common_words'][$i];
// display the blank word
$correctItem = $listAnswerInfo['tabwords'][$i];
$correctItem = $listAnswerInfo['words'][$i];
if (isset($studentAnswerList[$i])) {
// If student already started this test and answered this question,
// fill the blank with his previous answers
// may be "" if student viewed the question, but did not fill the blanks
$correctItem = $studentAnswerList[$i];
}
$attributes['style'] = "width:".$listAnswerInfo['tabinputsize'][$i]."px";
$attributes['style'] = "width:".$listAnswerInfo['input_size'][$i]."px";
$answer .= FillBlanks::getFillTheBlankHtml(
$current_item,
$questionId,
@ -637,15 +635,15 @@ class ExerciseLib
);
}
// display the last common word
$answer .= $listAnswerInfo['commonwords'][$i];
$answer .= $listAnswerInfo['common_words'][$i];
} else {
// display empty [input] with the right width for student to fill it
$answer = '';
for ($i = 0; $i < count($listAnswerInfo['commonwords']) - 1; $i++) {
for ($i = 0; $i < count($listAnswerInfo['common_words']) - 1; $i++) {
// display the common words
$answer .= $listAnswerInfo['commonwords'][$i];
$answer .= $listAnswerInfo['common_words'][$i];
// display the blank word
$attributes["style"] = "width:".$listAnswerInfo['tabinputsize'][$i]."px";
$attributes['style'] = "width:".$listAnswerInfo['input_size'][$i]."px";
$answer .= FillBlanks::getFillTheBlankHtml(
$current_item,
$questionId,
@ -658,7 +656,7 @@ class ExerciseLib
);
}
// display the last common word
$answer .= $listAnswerInfo['commonwords'][$i];
$answer .= $listAnswerInfo['common_words'][$i];
}
$s .= $answer;
break;
@ -3946,25 +3944,23 @@ EOT;
/**
* Display the exercise results
* @param Exercise $objExercise
* @param int $exe_id
* @param int $exeId
* @param bool $save_user_result save users results (true) or just show the results (false)
* @param string $remainingMessage
*/
public static function displayQuestionListByAttempt(
$objExercise,
$exe_id,
$exeId,
$save_user_result = false,
$remainingMessage = ''
) {
$origin = api_get_origin();
// Getting attempt info
$exercise_stat_info = $objExercise->get_stat_track_exercise_info_by_exe_id(
$exe_id
);
$exercise_stat_info = $objExercise->get_stat_track_exercise_info_by_exe_id($exeId);
// Getting question list
$question_list = array();
$question_list = [];
if (!empty($exercise_stat_info['data_tracking'])) {
$question_list = explode(',', $exercise_stat_info['data_tracking']);
} else {
@ -4078,6 +4074,7 @@ EOT;
$exerciseResult = null;
$exerciseResultCoordinates = null;
$delineationResults = null;
if ($objExercise->selectFeedbackType() == EXERCISE_FEEDBACK_TYPE_DIRECT) {
$loadChoiceFromSession = true;
$fromDatabase = false;
@ -4091,9 +4088,8 @@ EOT;
// Loop over all question to show results for each of them, one by one
if (!empty($question_list)) {
foreach ($question_list as $questionId) {
// creates a temporary Question object
// Creates a temporary Question object
$objQuestionTmp = Question::read($questionId);
// This variable came from exercise_submit_modal.php
ob_start();
$choice = null;
@ -4105,7 +4101,7 @@ EOT;
// We're inside *one* question. Go through each possible answer for this question
$result = $objExercise->manage_answer(
$exe_id,
$exeId,
$questionId,
$choice,
'exercise_result',
@ -4169,17 +4165,16 @@ EOT;
$category_list['none']['total'] += $my_total_weight;
}
if ($objExercise->selectPropagateNeg() == 0 &&
$my_total_score < 0
) {
if ($objExercise->selectPropagateNeg() == 0 && $my_total_score < 0) {
$my_total_score = 0;
}
$comnt = null;
if ($show_results) {
$comnt = Event::get_comments($exe_id, $questionId);
$comnt = Event::get_comments($exeId, $questionId);
$teacherAudio = ExerciseLib::getOralFeedbackAudio(
$exe_id,
$exeId,
$questionId,
api_get_user_id()
);
@ -4197,6 +4192,7 @@ EOT;
}
}
$score = [];
if ($show_results) {
$score = [
'result' => self::show_score(
@ -4208,10 +4204,8 @@ EOT;
'pass' => $my_total_score >= $my_total_weight ? true : false,
'score' => $my_total_score,
'weight' => $my_total_weight,
'comments' => $comnt,
'comments' => $comnt
];
} else {
$score = [];
}
if (in_array($objQuestionTmp->type, [FREE_ANSWER, ORAL_EXPRESSION, ANNOTATION])) {
@ -4238,7 +4232,10 @@ EOT;
$question_content .= '</div>';
}
if (!$show_only_score) {
$exercise_content .= Display::div(Display::panel($question_content),array('class' => 'question-panel'));
$exercise_content .= Display::div(
Display::panel($question_content),
['class' => 'question-panel']
);
}
} // end foreach() block that loops over all questions
}
@ -4258,10 +4255,10 @@ EOT;
if (!empty($category_list) && ($show_results || $show_only_score)) {
// Adding total
$category_list['total'] = array(
$category_list['total'] = [
'score' => $total_score,
'total' => $total_weight
);
];
echo TestCategory::get_stats_table_by_attempt(
$objExercise->id,
$category_list
@ -4323,7 +4320,7 @@ EOT;
'end',
$question_list_answers,
$origin,
$exe_id,
$exeId,
$total_score,
$total_weight
);

Loading…
Cancel
Save