Skip to content

Commit

Permalink
MDL-54907 quiz: better timefinish for attempts finished asynchronously
Browse files Browse the repository at this point in the history
There are serveral ways a quiz attempt can be submitted:

1. The student click the Submit and finish button. In this case,
   no problem. We record the current time as the finish time
   for the quiz attempt.

2. The student is activly working away at the quiz, and the
   count-down timer reachers zero. In this case, we also record
   the current time. Note that, if the server is under high load,
   then this could well end up being a few seconds after the
   theoretical end time, so you could have a quiz with a 30 minute
   time limit, with an attempt that lasted 30:07. However, this
   is just an accurate reflection of what happened, so should
   be recorded like this.

3. If the student is offline when the time expires, then
   (depending on the quiz settings) the attempt may be
   automatically submitted by cron, but this will happen with
   at least some delay (to prevent race conditions between cron
   and a student working online) and if cron is running slow
   on the server, it could be a lot later. Previously, this led
   to, say, a 30 minute quiz where an attempt seemed to have
   lasted 67 minutes, which confused people.

   Now, in this situation, the finsh time for the quiz attempt is
   recorded as the time when the time limit ran out. This is not
   just less confusing for teachers looking at the quiz report,
   it is also more accurate. That is the latest time at which
   students could have made any changes to their responses.
  • Loading branch information
timhunt committed Nov 27, 2020
1 parent ec6978b commit fc9d2e0
Show file tree
Hide file tree
Showing 3 changed files with 188 additions and 116 deletions.
21 changes: 17 additions & 4 deletions mod/quiz/attemptlib.php
Expand Up @@ -2002,7 +2002,7 @@ public function handle_if_time_expired($timestamp, $studentisonline) {
// Transition to the appropriate state.
switch ($this->quizobj->get_quiz()->overduehandling) {
case 'autosubmit':
$this->process_finish($timestamp, false);
$this->process_finish($timestamp, false, $studentisonline ? $timestamp : $timeclose);
return;

case 'graceperiod':
Expand Down Expand Up @@ -2160,7 +2160,20 @@ public function save_question_flags() {
$transaction->allow_commit();
}

public function process_finish($timestamp, $processsubmitted) {
/**
* Submit the attempt.
*
* The separate $timefinish argument should be used when the quiz attempt
* is being processed asynchronously (for example when cron is submitting
* attempts where the time has expired).
*
* @param int $timestamp the time to record as last modified time.
* @param bool $processsubmitted if true, and question responses in the current
* POST request are stored to be graded, before the attempt is finished.
* @param ?int $timefinish if set, use this as the finish time for the attempt.
* (otherwise use $timestamp as the finish time as well).
*/
public function process_finish($timestamp, $processsubmitted, $timefinish = null) {
global $DB;

$transaction = $DB->start_delegated_transaction();
Expand All @@ -2173,7 +2186,7 @@ public function process_finish($timestamp, $processsubmitted) {
question_engine::save_questions_usage_by_activity($this->quba);

$this->attempt->timemodified = $timestamp;
$this->attempt->timefinish = $timestamp;
$this->attempt->timefinish = $timefinish ?? $timestamp;
$this->attempt->sumgrades = $this->quba->get_total_mark();
$this->attempt->state = self::FINISHED;
$this->attempt->timecheckstate = null;
Expand Down Expand Up @@ -2440,7 +2453,7 @@ public function process_attempt($timenow, $finishattempt, $timeup, $thispage) {
if ($becomingabandoned) {
$this->process_abandon($timenow, true);
} else {
$this->process_finish($timenow, !$toolate);
$this->process_finish($timenow, !$toolate, $toolate ? $timeclose : $timenow);
}

} catch (question_out_of_sequence_exception $e) {
Expand Down
3 changes: 0 additions & 3 deletions mod/quiz/tests/attempt_test.php
Expand Up @@ -81,9 +81,6 @@ protected function create_quiz_and_attempt_with_layout($layout) {
return quiz_attempt::create($attempt->id);
}

/**
* Test the functions quiz_update_open_attempts() and get_list_of_overdue_attempts()
*/
public function test_attempt_url() {
$attempt = $this->create_quiz_and_attempt_with_layout('1,2,0,3,4,0,5,6,0');

Expand Down

0 comments on commit fc9d2e0

Please sign in to comment.