Exercises: allow_time_per_question requires DB change to save spent time

BT#17791
pull/3600/head^2
Julio Montoya 5 years ago
parent ca559e154b
commit 8dfc932016
  1. 90
      main/exercise/exercise.class.php
  2. 211
      main/exercise/exercise_submit.php
  3. 46
      main/inc/ajax/exercise.ajax.php
  4. 155
      main/inc/lib/events.lib.php
  5. 4
      main/install/configuration.dist.php

@ -3577,6 +3577,7 @@ class Exercise
* @param bool $showTotalScoreAndUserChoicesInLastAttempt
* @param bool $updateResults
* @param bool $showHotSpotDelineationTable
* @param int $questionDuration seconds
*
* @todo reduce parameters of this function
*
@ -3595,7 +3596,8 @@ class Exercise
$hotspot_delineation_result = [],
$showTotalScoreAndUserChoicesInLastAttempt = true,
$updateResults = false,
$showHotSpotDelineationTable = false
$showHotSpotDelineationTable = false,
$questionDuration = 0
) {
$debug = false;
//needed in order to use in the exercise_attempt() for the time
@ -3604,6 +3606,7 @@ class Exercise
$em = Database::getManager();
$feedback_type = $this->getFeedbackType();
$results_disabled = $this->selectResultsDisabled();
$questionDuration = (int) $questionDuration;
if ($debug) {
error_log("<------ manage_answer ------> ");
@ -5694,10 +5697,6 @@ class Exercise
</tr>
</table>';
if ($next == 0) {
/*$try = $try_hotspot;
$lp = $lp_hotspot;
$destinationid = $select_question_hotspot;
$url = $url_hotspot;*/
} else {
$comment = $answerComment = $objAnswerTmp->selectComment($nbrAnswers);
$answerDestination = $objAnswerTmp->selectDestination($nbrAnswers);
@ -5862,7 +5861,8 @@ class Exercise
$exeId,
$i,
$this->id,
$updateResults
$updateResults,
$questionDuration
);
}
} else {
@ -5873,7 +5873,8 @@ class Exercise
$exeId,
$i,
$this->id,
$updateResults
$updateResults,
$questionDuration
);
}
if ($debug) {
@ -5887,7 +5888,9 @@ class Exercise
$quesId,
$exeId,
0,
$this->id
$this->id,
false,
$questionDuration
);
}
} elseif ($answerType == MULTIPLE_ANSWER || $answerType == GLOBAL_MULTIPLE_ANSWER) {
@ -5895,17 +5898,44 @@ class Exercise
$reply = array_keys($choice);
for ($i = 0; $i < count($reply); $i++) {
$ans = $reply[$i];
Event::saveQuestionAttempt($questionScore, $ans, $quesId, $exeId, $i, $this->id);
Event::saveQuestionAttempt(
$questionScore,
$ans,
$quesId,
$exeId,
$i,
$this->id,
false,
$questionDuration
);
}
} else {
Event::saveQuestionAttempt($questionScore, 0, $quesId, $exeId, 0, $this->id);
Event::saveQuestionAttempt(
$questionScore,
0,
$quesId,
$exeId,
0,
$this->id,
false,
$questionDuration
);
}
} elseif ($answerType == MULTIPLE_ANSWER_COMBINATION) {
if ($choice != 0) {
$reply = array_keys($choice);
for ($i = 0; $i < count($reply); $i++) {
$ans = $reply[$i];
Event::saveQuestionAttempt($questionScore, $ans, $quesId, $exeId, $i, $this->id);
Event::saveQuestionAttempt(
$questionScore,
$ans,
$quesId,
$exeId,
$i,
$this->id,
false,
$questionDuration
);
}
} else {
Event::saveQuestionAttempt(
@ -5914,7 +5944,9 @@ class Exercise
$quesId,
$exeId,
0,
$this->id
$this->id,
false,
$questionDuration
);
}
} elseif (in_array($answerType, [MATCHING, DRAGGABLE, MATCHING_DRAGGABLE])) {
@ -5926,7 +5958,9 @@ class Exercise
$quesId,
$exeId,
$j,
$this->id
$this->id,
false,
$questionDuration
);
}
}
@ -5938,7 +5972,9 @@ class Exercise
$quesId,
$exeId,
0,
$this->id
$this->id,
false,
$questionDuration
);
} elseif ($answerType == ORAL_EXPRESSION) {
$answer = $choice;
@ -5950,6 +5986,7 @@ class Exercise
0,
$this->id,
false,
$questionDuration,
$objQuestionTmp->getAbsoluteFilePath()
);
} elseif (
@ -5959,7 +5996,7 @@ class Exercise
)
) {
$answer = $choice;
Event::saveQuestionAttempt($questionScore, $answer, $quesId, $exeId, 0, $this->id);
Event::saveQuestionAttempt($questionScore, $answer, $quesId, $exeId, 0, $this->id, false, $questionDuration);
} elseif ($answerType == HOT_SPOT || $answerType == ANNOTATION) {
$answer = [];
if (isset($exerciseResultCoordinates[$questionId]) && !empty($exerciseResultCoordinates[$questionId])) {
@ -5998,9 +6035,27 @@ class Exercise
error_log('Empty: exerciseResultCoordinates');
}
}
Event::saveQuestionAttempt($questionScore, implode('|', $answer), $quesId, $exeId, 0, $this->id);
Event::saveQuestionAttempt(
$questionScore,
implode('|', $answer),
$quesId,
$exeId,
0,
$this->id,
false,
$questionDuration
);
} else {
Event::saveQuestionAttempt($questionScore, $answer, $quesId, $exeId, 0, $this->id);
Event::saveQuestionAttempt(
$questionScore,
$answer,
$quesId,
$exeId,
0,
$this->id,
false,
$questionDuration
);
}
}
@ -9952,6 +10007,7 @@ class Exercise
Session::erase('exerciseResult');
Session::erase('firstTime');
Session::erase('time_per_question');
Session::erase('question_start');
Session::erase('exerciseResultCoordinates');
Session::erase('hotspot_coord');
Session::erase('hotspot_dest');

@ -62,8 +62,6 @@ $htmlHeadXtra[] = $js;
$htmlHeadXtra[] = api_get_js('jqueryui-touch-punch/jquery.ui.touch-punch.min.js');
$htmlHeadXtra[] = api_get_js('jquery.jsPlumb.all.js');
$htmlHeadXtra[] = api_get_js('d3/jquery.xcolor.js');
// This library is necessary for the time control feature.
$htmlHeadXtra[] = api_get_css(api_get_path(WEB_LIBRARY_PATH).'javascript/epiclock/renderers/minute/epiclock.minute.css');
$htmlHeadXtra[] = api_get_js('epiclock/javascript/jquery.dateformat.min.js');
$htmlHeadXtra[] = api_get_js('epiclock/javascript/jquery.epiclock.min.js');
@ -109,8 +107,7 @@ if (isset($zoomOptions['options']) && !in_array($origin, ['embeddable', 'mobilea
$(document).contextmenu(function() {
return false;
})
});
});
</script>';
}
@ -130,7 +127,7 @@ $exerciseResultCoordinates = isset($_REQUEST['exerciseResultCoordinates']) ? $_R
$choice = isset($_REQUEST['choice']) ? $_REQUEST['choice'] : null;
$choice = empty($choice) ? isset($_REQUEST['choice2']) ? $_REQUEST['choice2'] : null : null;
$questionCategoryId = isset($_REQUEST['category_id']) ? (int) $_REQUEST['category_id'] : 0;
$current_question = isset($_REQUEST['num']) ? (int) $_REQUEST['num'] : null;
$current_question = $currentQuestionFromUrl = isset($_REQUEST['num']) ? (int) $_REQUEST['num'] : null;
$currentAnswer = isset($_REQUEST['num_answer']) ? (int) $_REQUEST['num_answer'] : null;
$logInfo = [
@ -189,13 +186,13 @@ if (!isset($objExercise) && isset($exerciseInSession)) {
$exerciseInSession = Session::read('objExercise');
//3. $objExercise is not set, then return to the exercise list.
// 3. $objExercise is not set, then return to the exercise list.
if (!is_object($objExercise)) {
header('Location: exercise.php');
exit;
}
// if the user has submitted the form
// if the user has submitted the form.
$exercise_title = $objExercise->selectTitle();
$exercise_sound = $objExercise->selectSound();
@ -307,7 +304,6 @@ if ($objExercise->selectAttempts() > 0) {
);
$attempt_html .= $messageReachedMax;
if (!empty($last_attempt_info['question_list'])) {
foreach ($last_attempt_info['question_list'] as $questions) {
foreach ($questions as $question_data) {
@ -495,10 +491,6 @@ if (!empty($exercise_stat_info['questions_to_check'])) {
}
$params = "exe_id=$exe_id&exerciseId=$exerciseId&learnpath_id=$learnpath_id&learnpath_item_id=$learnpath_item_id&learnpath_item_view_id=$learnpath_item_view_id&".api_get_cidreq().'&reminder='.$reminder;
if ($debug) {
error_log("6.1 params: -> $params");
}
if (2 == $reminder && empty($myRemindList)) {
if ($debug) {
error_log('6.2 calling the exercise_reminder.php');
@ -522,7 +514,7 @@ if ($time_control) {
}
if (!isset($_SESSION['expired_time'][$current_expired_time_key])) {
//Timer - Get expired_time for a student
// Timer - Get expired_time for a student.
if (!empty($exercise_stat_info)) {
$expired_time_of_this_attempt = $exercise_stat_info['expired_time_control'];
if ($debug) {
@ -585,7 +577,8 @@ $time_left = api_strtotime($clock_expired_time, 'UTC') - time();
* The time control feature is enable here - this feature is enable for a jquery plugin called epiclock
* for more details of how it works see this link : http://eric.garside.name/docs.html?p=epiclock
*/
if ($time_control) { //Sends the exercise form when the expired time is finished
if ($time_control) {
//Sends the exercise form when the expired time is finished.
$htmlHeadXtra[] = $objExercise->showTimeControlJS($time_left);
}
@ -604,8 +597,7 @@ if (!isset($_SESSION['questionList'])) {
}
$isLastQuestionInCategory = 0;
if (
api_get_configuration_value('block_category_questions') &&
if (api_get_configuration_value('block_category_questions') &&
ONE_PER_PAGE == $objExercise->type &&
EX_Q_SELECTION_CATEGORIES_ORDERED_QUESTIONS_RANDOM == $selectionType
) {
@ -659,7 +651,6 @@ if (
$count = 1;
$total = count($categoryList[$categoryId]);
//var_dump($categoryList[$categoryId]);
foreach ($categoryList[$categoryId] as $checkQuestionId) {
if ((int) $checkQuestionId === $questionCheck->iid) {
break;
@ -670,14 +661,11 @@ if (
if ($count === $total) {
$isLastQuestionInCategory = $categoryId;
}
//var_dump($isLastQuestionInCategory, $blockedCategories);
if (0 === $isLastQuestionInCategory) {
$showPreviousButton = false;
}
}
//var_dump($categoryInfo, $count, $total);
// Blocked if category was already answered
if ($categoryId && in_array($categoryId, $blockedCategories)) {
// Redirect to category intro
@ -928,13 +916,98 @@ if (api_is_in_gradebook()) {
];
}
$interbreadcrumb[] = [
'url' => 'exercise.php?'.api_get_cidreq(),
'name' => get_lang('Exercises'),
];
$interbreadcrumb[] = ['url' => 'exercise.php?'.api_get_cidreq(), 'name' => get_lang('Exercises')];
$interbreadcrumb[] = ['url' => '#', 'name' => $objExercise->selectTitle(true)];
if (!in_array($origin, ['learnpath', 'embeddable', 'mobileapp'])) { //so we are not in learnpath tool
// Time per question.
$questionTimeCondition = '';
if ($allowTimePerQuestion && $objExercise->type == ONE_PER_PAGE) {
$objQuestionTmp = null;
$previousQuestion = null;
if (!empty($questionList)) {
$i = 0;
foreach ($questionList as $questionId) {
$i++;
$objQuestionTmp = Question::read($questionId);
// if it is not the right question, goes to the next loop iteration
if ($current_question == $i) {
break;
}
$previousQuestion = $objQuestionTmp;
}
}
$extraFieldValue = new ExtraFieldValue('question');
$value = $extraFieldValue->get_values_by_handler_and_field_variable($objQuestionTmp->iid, 'time');
if (!empty($value) && isset($value['value']) && !empty($value['value'])) {
$seconds = (int) $value['value'];
$now = time();
$loadedQuestions = array_keys(Session::read('question_start', []));
if (!empty($loadedQuestions)) {
if (!in_array($objQuestionTmp->iid, $loadedQuestions)) {
api_not_allowed(true);
}
}
$timeSpent = Event::getAttemptQuestionDuration($exe_id, $objQuestionTmp->iid);
$loadedQuestions = array_keys(Session::read('question_start'));
// Block if another question is loaded at the same time.
if (!in_array($objQuestionTmp->iid, $loadedQuestions)) {
api_not_allowed(true);
}
/*$timePerQuestion = Session::read('time_per_question', []);
if (!empty($timePerQuestion) && isset($timePerQuestion[$objQuestionTmp->iid])) {
$time = $timePerQuestion[$objQuestionTmp->iid];
} else {
$time = $timePerQuestion[$objQuestionTmp->iid] = $now;
Session::write('time_per_question', $timePerQuestion);
}
$timeSpent = $now - $time;*/
/*if (!empty($questionAttempt) && isset($questionAttempt['tms'])) {
var_dump('from DB');
var_dump($questionAttempt['tms']);
$time = api_strtotime($questionAttempt['tms'], 'UTC');
$timeSpent = $now - $time;
} else {
var_dump('from session');
$timePerQuestion = Session::read('time_per_question', []);
if (!empty($timePerQuestion) && isset($timePerQuestion[$objQuestionTmp->iid])) {
$time = $timePerQuestion[$objQuestionTmp->iid];
} else {
$time = $timePerQuestion[$objQuestionTmp->iid] = $now;
Session::write('time_per_question', $timePerQuestion);
}
var_dump($timePerQuestion);
$timeSpent = $now - $time;
}*/
//var_dump($timeSpent);
//var_dump(api_get_utc_datetime($now).' - '.api_get_utc_datetime($time));
// Redirect to next question.
if ($timeSpent > $seconds) {
$nextQuestion = (int) $currentQuestionFromUrl + 1;
$nextQuestionUrl = api_get_path(WEB_CODE_PATH).
"exercise/exercise_submit.php?$params&num=$nextQuestion&remind_question_id=$remind_question_id";
api_location($nextQuestionUrl);
}
$seconds = $seconds - $timeSpent;
//var_dump($seconds);
$questionTimeCondition = "
var timer = new easytimer.Timer();
timer.start({countdown: true, startValues: {seconds: $seconds}});
timer.addEventListener('secondsUpdated', function (e) {
$('#question_timer').html(timer.getTimeValues().toString());
});
timer.addEventListener('targetAchieved', function (e) {
$('.question-validate-btn').click();
});
";
}
}
if (!in_array($origin, ['learnpath', 'embeddable', 'mobileapp'])) {
//so we are not in learnpath tool
SessionManager::addFlashSessionReadOnly();
Display::display_header(null, 'Exercises');
} else {
@ -950,7 +1023,6 @@ if ($origin === 'mobileapp') {
}
$show_quiz_edition = $objExercise->added_in_lp();
// I'm in a preview mode
if (api_is_course_admin() && !in_array($origin, ['learnpath', 'embeddable'])) {
echo '<div class="actions">';
@ -987,7 +1059,6 @@ if (!api_is_allowed_to_session_edit()) {
$exercise_timeover = false;
$limit_time_exists = !empty($objExercise->start_time) || !empty($objExercise->end_time) ? true : false;
if ($limit_time_exists) {
$exercise_start_time = api_strtotime($objExercise->start_time, 'UTC');
$exercise_end_time = api_strtotime($objExercise->end_time, 'UTC');
@ -1116,26 +1187,27 @@ if ($reminder == 2) {
}
}
if (!empty($error)) {
Display::addFlash(Display::return_message($error, 'error', false));
api_not_allowed();
exit;
}
$script_php = 'exercise_result.php';
if ($objExercise->review_answers) {
$script_php = 'exercise_reminder.php';
} else {
$script_php = 'exercise_result.php';
}
if (!empty($error)) {
echo Display::return_message($error, 'error', false);
} else {
if (!empty($exercise_sound)) {
if (!empty($exercise_sound)) {
echo "<a
href=\"../document/download.php?doc_url=%2Faudio%2F".Security::remove_XSS($exercise_sound)."\"
target=\"_blank\">";
echo "<img src=\"../img/sound.gif\" border=\"0\" align=\"absmiddle\" alt=", get_lang('Sound')."\" /></a>";
}
// Get number of hotspot questions for javascript validation
$number_of_hotspot_questions = 0;
$onsubmit = '';
$i = 0;
if (!empty($questionList)) {
}
// Get number of hotspot questions for javascript validation
$number_of_hotspot_questions = 0;
$i = 0;
if (!empty($questionList)) {
foreach ($questionList as $questionId) {
$i++;
$objQuestionTmp = Question::read($questionId);
@ -1157,38 +1229,19 @@ if (!empty($error)) {
}
}
}
}
}
$saveIcon = Display::return_icon(
$saveIcon = Display::return_icon(
'save.png',
get_lang('Saved'),
[],
ICON_SIZE_SMALL,
false,
true
);
);
$questionTimeCondition = '';
if ($allowTimePerQuestion && $objExercise->type == ONE_PER_PAGE) {
$extraFieldValue = new ExtraFieldValue('question');
$value = $extraFieldValue->get_values_by_handler_and_field_variable($objQuestionTmp->iid, 'time');
if (!empty($value) && isset($value['value']) && !empty($value['value'])) {
$seconds = (int) $value['value'];
$questionTimeCondition = "
var timer = new easytimer.Timer();
//timer.start();
timer.start({countdown: true, startValues: {seconds: $seconds}});
timer.addEventListener('secondsUpdated', function (e) {
$('#question_timer').html(timer.getTimeValues().toString());
});
timer.addEventListener('targetAchieved', function (e) {
$('.question-validate-btn').click();
});
";
}
}
echo '<script>
echo '<script>
function addExerciseEvent(elm, evType, fn, useCapture) {
if (elm.addEventListener) {
elm.addEventListener(evType, fn, useCapture);
@ -1479,11 +1532,11 @@ if (!empty($error)) {
}
window.quizTimeEnding = false;
</script>';
</script>';
echo '<form id="exercise_form" method="post" action="'.
echo '<form id="exercise_form" method="post" action="'.
api_get_self().'?'.api_get_cidreq().'&reminder='.$reminder.
'&autocomplete=off&exerciseId='.$exerciseId.'" name="frm_exercise" '.$onsubmit.'>
'&autocomplete=off&exerciseId='.$exerciseId.'" name="frm_exercise">
<input type="hidden" name="formSent" value="1" />
<input type="hidden" name="exerciseId" value="'.$exerciseId.'" />
<input type="hidden" name="num" value="'.$current_question.'" id="num_current_id" />
@ -1495,21 +1548,21 @@ if (!empty($error)) {
<input type="hidden" name="learnpath_item_id" value="'.$learnpath_item_id.'" />
<input type="hidden" name="learnpath_item_view_id" value="'.$learnpath_item_view_id.'" />';
// Show list of questions
$i = 1;
$attempt_list = [];
if (isset($exe_id)) {
// Show list of questions
$i = 1;
$attempt_list = [];
if (isset($exe_id)) {
$attempt_list = Event::getAllExerciseEventByExeId($exe_id);
}
}
$remind_list = [];
if (isset($exercise_stat_info['questions_to_check']) &&
$remind_list = [];
if (isset($exercise_stat_info['questions_to_check']) &&
!empty($exercise_stat_info['questions_to_check'])
) {
) {
$remind_list = explode(',', $exercise_stat_info['questions_to_check']);
}
}
foreach ($questionList as $questionId) {
foreach ($questionList as $questionId) {
// for sequential exercises
if (ONE_PER_PAGE == $objExercise->type) {
// if it is not the right question, goes to the next loop iteration
@ -1693,18 +1746,18 @@ if (!empty($error)) {
// quits the loop
break;
}
}
}
if ($objExercise->type == ALL_ON_ONE_PAGE) {
if ($objExercise->type == ALL_ON_ONE_PAGE) {
$exerciseActions = $objExercise->show_button(
$questionId,
$current_question
);
echo Display::div($exerciseActions, ['class' => 'exercise_actions']);
echo '<br>';
}
echo '</form>';
}
echo '</form>';
if (!in_array($origin, ['learnpath', 'embeddable'])) {
// So we are not in learnpath tool
echo '</div>'; //End glossary div

@ -389,8 +389,7 @@ switch ($action) {
$choice = isset($_REQUEST['choice']) ? $_REQUEST['choice'] : [];
// certainty degree choice
$choiceDegreeCertainty = isset($_REQUEST['choiceDegreeCertainty'])
? $_REQUEST['choiceDegreeCertainty'] : [];
$choiceDegreeCertainty = isset($_REQUEST['choiceDegreeCertainty'])? $_REQUEST['choiceDegreeCertainty'] : [];
// Hot spot coordinates from all questions.
$hot_spot_coordinates = isset($_REQUEST['hotspot']) ? $_REQUEST['hotspot'] : [];
@ -450,13 +449,11 @@ switch ($action) {
$exercise_stat_info = $objExercise->get_stat_track_exercise_info_by_exe_id($exeId);
$exercise_id = $exercise_stat_info['exe_exo_id'];
$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.
$exeId = $exercise_stat_info['exe_id'];
$total_score = $exercise_stat_info['exe_result'];
// Getting the list of attempts
$attemptList = Event::getAllExerciseEventByExeId($exeId);
}
@ -495,7 +492,7 @@ switch ($action) {
}
}
// Getting the total weight if the request is simple
// Getting the total weight if the request is simple.
$total_weight = 0;
if ($type === 'simple') {
foreach ($question_list as $my_question_id) {
@ -510,7 +507,7 @@ switch ($action) {
}
// Check we have at least one non-empty answer in the array
// provided by the user's click on the "Finish test" button
// provided by the user's click on the "Finish test" button.
if ('all' === $type) {
$atLeastOneAnswer = false;
foreach ($question_list as $my_question_id) {
@ -534,24 +531,14 @@ switch ($action) {
// Looping the question list from database (not from the user answer)
foreach ($question_list as $my_question_id) {
if ($type === 'simple' && $question_id != $my_question_id) {
if ($debug) {
error_log('Skipping question '.$my_question_id.' in single-question save action');
}
continue;
}
if ($debug) {
error_log("Saving question_id = $my_question_id ");
}
$my_choice = isset($choice[$my_question_id]) ? $choice[$my_question_id] : null;
if ($debug) {
error_log("Saving question_id = $my_question_id ");
error_log("my_choice = ".print_r($my_choice, 1)."");
}
// Creates a temporary Question object
$objQuestionTmp = Question::read($my_question_id, $objExercise->course);
$myChoiceDegreeCertainty = null;
if ($objQuestionTmp->type === MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY) {
if (isset($choiceDegreeCertainty[$my_question_id])) {
@ -560,7 +547,7 @@ switch ($action) {
}
// Getting free choice data.
if (in_array($objQuestionTmp->type, [FREE_ANSWER, 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;
@ -581,7 +568,7 @@ switch ($action) {
$hotspot_delineation_result = $_SESSION['hotspot_delineation_result'][$objExercise->selectId()][$my_question_id];
}
if ($type === 'simple') {
if ('simple' === $type) {
// Getting old attempt in order to decrease the total score.
$old_result = $objExercise->manage_answer(
$exeId,
@ -627,6 +614,8 @@ switch ($action) {
}
}
$questionDuration = Event::getAttemptQuestionDuration($exeId, $objQuestionTmp->iid);
// We're inside *one* question. Go through each possible answer for this question
if ($objQuestionTmp->type === MULTIPLE_ANSWER_TRUE_FALSE_DEGREE_CERTAINTY) {
$myChoiceTmp = [];
@ -642,7 +631,11 @@ switch ($action) {
false,
false,
$objExercise->selectPropagateNeg(),
$hotspot_delineation_result
$hotspot_delineation_result,
true,
false,
false,
$questionDuration
);
} else {
$result = $objExercise->manage_answer(
@ -655,7 +648,11 @@ switch ($action) {
false,
false,
$objExercise->selectPropagateNeg(),
$hotspot_delineation_result
$hotspot_delineation_result,
true,
false,
false,
$questionDuration
);
}
@ -715,6 +712,11 @@ switch ($action) {
$remind_list
);
$questionStart = Session::read('question_start');
unset($questionStart[$my_question_id]);
array_filter($questionStart);
Session::write('question_start', $questionStart);
// Destruction of the Question object
unset($objQuestionTmp);
if ($debug) {
@ -737,7 +739,7 @@ switch ($action) {
exit;
}
if ($type == 'all') {
if ($type === 'all') {
echo 'ok';
exit;
}

@ -403,21 +403,26 @@ class Event
/**
* Update the TRACK_E_EXERCICES exercises.
* Record result of user when an exercise was done.
*
* @param int exeid id of the attempt
* @param int exo_id exercise id
* @param mixed result score
* @param int weighting ( higher score )
* @param int duration ( duration of the attempt in seconds )
* @param int session_id
* @param int learnpath_id (id of the learnpath)
* @param int learnpath_item_id (id of the learnpath_item)
* @param int $exeId
* @param int $exoId
* @param mixed $score
* @param int $weighting
* @param int $sessionId
* @param int $learnpathId
* @param int $learnpathItemId
* @param int $learnpathItemViewId
* @param int $duration
* @param array $questionsList
* @param string $status
* @param array $remindList
* @param null $endDate
*
* @return bool
*
* @author Sebastien Piraux <piraux_seb@hotmail.com>
* @author Julio Montoya Armas <gugli100@gmail.com> Reworked 2010
* @desc Record result of user when an exercise was done
*/
public static function updateEventExercise(
$exeId,
@ -437,15 +442,6 @@ class Event
if (empty($exeId)) {
return false;
}
/*
* Code commented due BT#8423 do not change the score to 0.
*
* Validation in case of fraud with actived control time
if (!ExerciseLib::exercise_time_control_is_valid($exo_id, $learnpath_id, $learnpath_item_id)) {
$score = 0;
}
*/
if (!isset($status) || empty($status)) {
$status = '';
} else {
@ -506,19 +502,20 @@ class Event
/**
* Record an event for this attempt at answering an exercise.
*
* @param float Score achieved
* @param string Answer given
* @param int Question ID
* @param int Exercise attempt ID a.k.a exe_id (from track_e_exercise)
* @param int Position
* @param int Exercise ID (from c_quiz)
* @param bool update results?
* @param $fileName string Filename (for audio answers - using nanogong)
* @param int User ID The user who's going to get this score. Default value of null means "get from context".
* @param int Course ID (from the "id" column of course table). Default value of null means "get from context".
* @param int Session ID (from the session table). Default value of null means "get from context".
* @param int Learnpath ID (from c_lp table). Default value of null means "get from context".
* @param int Learnpath item ID (from the c_lp_item table). Default value of null means "get from context".
* @param float $score Score achieved
* @param string $answer Answer given
* @param int $question_id
* @param int $exe_id Exercise attempt ID a.k.a exe_id (from track_e_exercise)
* @param int $position
* @param int $exercise_id From c_quiz
* @param bool $updateResults
* @param int $duration Time spent in seconds
* @param string $fileName Filename (for audio answers - using nanogong)
* @param int $user_id The user who's going to get this score.
* @param int $course_id Default value of null means "get from context".
* @param int $session_id Default value of null means "get from context".
* @param int $learnpath_id (from c_lp table). Default value of null means "get from context".
* @param int $learnpath_item_id (from the c_lp_item table). Default value of null means "get from context".
*
* @return bool Result of the insert query
*/
@ -530,6 +527,7 @@ class Event
$position,
$exercise_id = 0,
$updateResults = false,
$questionDuration = 0,
$fileName = null,
$user_id = null,
$course_id = null,
@ -538,12 +536,13 @@ class Event
$learnpath_item_id = null
) {
global $debug;
$question_id = Database::escape_string($question_id);
$exe_id = Database::escape_string($exe_id);
$position = Database::escape_string($position);
$now = api_get_utc_datetime();
$questionDuration = (int) $questionDuration;
$question_id = (int) $question_id;
$exe_id = (int) $exe_id;
$position = (int) $position;
$course_id = (int) $course_id;
$recording = api_get_configuration_value('quiz_answer_extra_recording') == true;
$now = api_get_utc_datetime();
$recording = api_get_configuration_value('quiz_answer_extra_recording');
// check user_id or get from context
if (empty($user_id)) {
@ -590,7 +589,10 @@ class Event
$answer = 0;
}
if (!empty($question_id) && !empty($exe_id) && !empty($user_id)) {
if (empty($question_id) || empty($exe_id) || empty($user_id)) {
return false;
}
if (is_null($answer)) {
$answer = '';
}
@ -612,6 +614,10 @@ class Event
'teacher_comment' => '',
];
if (api_get_configuration_value('allow_time_per_question')) {
$attempt['seconds_spent'] = $questionDuration;
}
// Check if attempt exists.
$sql = "SELECT exe_id FROM $TBL_TRACK_ATTEMPT
WHERE
@ -622,12 +628,11 @@ class Event
question_id = $question_id AND
position = $position";
$result = Database::query($sql);
$attemptData = [];
if (Database::num_rows($result)) {
if ($debug) {
error_log("Attempt already exist: exe_id: $exe_id - user_id:$user_id - question_id:$question_id");
}
$attemptData = Database::fetch_array($result, 'ASSOC');
if ($updateResults == false) {
//The attempt already exist do not update use update_event_exercise() instead
// The attempt already exist do not update use update_event_exercise() instead
return false;
}
} else {
@ -641,18 +646,9 @@ class Event
}
$recording_table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT_RECORDING);
if ($updateResults == false) {
$attempt_id = Database::insert($TBL_TRACK_ATTEMPT, $attempt);
if ($debug) {
error_log("Insert attempt with id #$attempt_id");
}
if ($recording) {
if ($debug) {
error_log("Saving e attempt recording ");
}
$attempt_recording = [
'exe_id' => $exe_id,
'question_id' => $question_id,
@ -664,6 +660,9 @@ class Event
Database::insert($recording_table, $attempt_recording);
}
} else {
if (api_get_configuration_value('allow_time_per_question')) {
$attempt['seconds_spent'] = $questionDuration + (int) $attemptData['seconds_spent'];
}
Database::update(
$TBL_TRACK_ATTEMPT,
$attempt,
@ -702,9 +701,7 @@ class Event
}
return $attempt_id;
} else {
return false;
}
}
/**
@ -1197,7 +1194,7 @@ class Event
$lpInteraction = Database::get_course_table(TABLE_LP_IV_INTERACTION);
$lpObjective = Database::get_course_table(TABLE_LP_IV_OBJECTIVE);
if (empty($course)) {
if (empty($course) || empty($user_id)) {
return false;
}
@ -1732,13 +1729,13 @@ class Event
$courseId,
$session_id = 0
) {
$table_track_exercises = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
$table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_EXERCISES);
$courseId = (int) $courseId;
$exercise_id = (int) $exercise_id;
$session_id = (int) $session_id;
$user_id = (int) $user_id;
$sql = "SELECT * FROM $table_track_exercises
$sql = "SELECT * FROM $table
WHERE
status = '' AND
c_id = $courseId AND
@ -1935,6 +1932,26 @@ class Event
return $list;
}
public static function getQuestionAttemptByExeIdAndQuestion($exeId, $questionId)
{
$table = Database::get_main_table(TABLE_STATISTIC_TRACK_E_ATTEMPT);
$exeId = (int) $exeId;
$questionId = (int) $questionId;
$sql = "SELECT * FROM $table
WHERE
exe_id = $exeId AND
question_id = $questionId
ORDER BY position";
$result = Database::query($sql);
$attempt = [];
if (Database::num_rows($result)) {
$attempt = Database::fetch_array($result, 'ASSOC');
}
return $attempt;
}
/**
* Delete one record from the track_e_attempt table (recorded quiz answer)
* and register the deletion event (LOG_QUESTION_RESULT_DELETE) in
@ -2566,4 +2583,30 @@ class Event
return true;
}
public static function getAttemptQuestionDuration($exeId, $questionId)
{
// Check current attempt.
$questionAttempt = self::getQuestionAttemptByExeIdAndQuestion($exeId, $questionId);
$alreadySpent = 0;
if (!empty($questionAttempt) && $questionAttempt['seconds_spent']) {
$alreadySpent = $questionAttempt['seconds_spent'];
}
$now = time();
$questionStart = Session::read('question_start', []);
if (!empty($questionStart) &&
isset($questionStart[$questionId]) && !empty($questionStart[$questionId])
) {
$time = $questionStart[$questionId];
} else {
$diff = 0;
if (!empty($alreadySpent)) {
$diff = $alreadySpent;
}
$time = $questionStart[$questionId] = $now - $diff;
Session::write('question_start', $questionStart);
}
return $now - $time;
}
}

@ -1727,7 +1727,9 @@ $_configuration['auth_password_links'] = [
// Resource sequence: Validate course in the same session.
//$_configuration['course_sequence_valid_only_in_same_session'] = false;
// Allow time per question. Requires a question text extra field called "time", value in seconds.
// Allow time per question. BT#17791
// Requires a question text extra field called "time", value in seconds.
// ALTER TABLE track_e_attempt ADD COLUMN seconds_spent INT;
//$_configuration['allow_time_per_question'] = true;
// Disable change user visibility tool icon.

Loading…
Cancel
Save