Skip to content

Commit

Permalink
MDL-3030 quiz overdue handling: back-end code for the new transitions
Browse files Browse the repository at this point in the history
  • Loading branch information
timhunt committed Apr 27, 2012
1 parent 34b7d83 commit a403bce
Show file tree
Hide file tree
Showing 4 changed files with 102 additions and 32 deletions.
104 changes: 75 additions & 29 deletions mod/quiz/attemptlib.php
Expand Up @@ -1255,19 +1255,22 @@ public function links_to_other_attempts(moodle_url $url) {
* Check this attempt, to see if there are any state transitions that should
* happen automatically.
* @param int $timestamp the timestamp that should be stored as the modifed
* @param bool $studentisonline is the student currently interacting with Moodle?
*/
public function handle_if_time_expired($timestamp) {
public function handle_if_time_expired($timestamp, $studentisonline) {
global $DB;

if ($this->get_time_left($timestamp) > 0) {
$timeleft = $this->get_access_manager($timestamp)->get_time_left($this->attempt, $timestamp);

if ($timeleft === false || $timeleft > 0) {
return; // Time has not yet expired.
}

// If the attempt is already overdue, look to see if it should be abandoned ...
if ($this->attempt->state == quiz_attempt::OVERDUE) {
$timeover = -$this->get_access_manager($timestamp)->get_time_left($this->attempt, $timestamp);
if ($timeover > $this->quizobj->get_quiz()->graceperiod) {
$this->process_abandon($timestamp);
$timeoverdue = -$timeleft;
if ($timeoverdue > $this->quizobj->get_quiz()->graceperiod) {
$this->process_abandon($timestamp, $studentisonline);
}

return; // ... and we are done.
Expand All @@ -1285,11 +1288,11 @@ public function handle_if_time_expired($timestamp) {
return;

case 'graceperiod':
$this->process_going_overdue($timestamp);
$this->process_going_overdue($timestamp, $studentisonline);
return;

case 'autoabandon':
$this->process_abandon($timestamp);
$this->process_abandon($timestamp, $studentisonline);
return;
}
}
Expand All @@ -1313,12 +1316,10 @@ public function process_submitted_actions($timestamp, $becomingoverdue = false)
$this->attempt->sumgrades = $this->quba->get_total_mark();
}
if ($becomingoverdue) {
// We do not trigger a full becoming overdue transition, because we
// don't want an email if the student is actively doing the quiz when
// time expires - I think.
$this->attempt->state == self::OVERDUE;
$this->process_going_overdue($timestamp, true);
} else {
$DB->update_record('quiz_attempts', $this->attempt);
}
$DB->update_record('quiz_attempts', $this->attempt);

if (!$this->is_preview() && $this->attempt->timefinish) {
quiz_save_best_grade($this->get_quiz(), $this->get_userid());
Expand All @@ -1339,7 +1340,7 @@ public function save_question_flags() {
}

public function process_finish($timestamp, $processsubmitted) {
global $DB, $USER;
global $DB;

$transaction = $DB->start_delegated_transaction();

Expand All @@ -1353,22 +1354,14 @@ public function process_finish($timestamp, $processsubmitted) {
$this->attempt->timemodified = $timestamp;
$this->attempt->timefinish = $timestamp;
$this->attempt->sumgrades = $this->quba->get_total_mark();
$this->attempt->state = self::FINISHED;
$DB->update_record('quiz_attempts', $this->attempt);

if (!$this->is_preview()) {
quiz_save_best_grade($this->get_quiz(), $this->attempt->userid);

// Trigger event
$eventdata = new stdClass();
$eventdata->component = 'mod_quiz';
$eventdata->attemptid = $this->attempt->id;
$eventdata->timefinish = $this->attempt->timefinish;
$eventdata->userid = $this->attempt->userid;
$eventdata->submitterid = $USER->id;
$eventdata->quizid = $this->get_quizid();
$eventdata->cmid = $this->get_cmid();
$eventdata->courseid = $this->get_courseid();
events_trigger('quiz_attempt_submitted', $eventdata);
$this->fire_state_transition_event('quiz_attempt_submitted', $timestamp);

// Tell any access rules that care that the attempt is over.
$this->get_access_manager($timestamp)->current_attempt_finished();
Expand All @@ -1380,19 +1373,72 @@ public function process_finish($timestamp, $processsubmitted) {
/**
* Mark this attempt as now overdue.
* @param int $timestamp the time to deem as now.
* @param bool $studentisonline is the student currently interacting with Moodle?
*/
public function process_going_overdue($timestamp) {
// TODO update DB.
// TODO raise event.
public function process_going_overdue($timestamp, $studentisonline) {
global $DB;

$transaction = $DB->start_delegated_transaction();
$this->attempt->timemodified = $timestamp;
$this->attempt->state = self::OVERDUE;
$DB->update_record('quiz_attempts', $this->attempt);

$this->fire_state_transition_event('quiz_attempt_overdue', $timestamp);

$transaction->allow_commit();
}

/**
* Mark this attempt as abandoned.
* @param int $timestamp the time to deem as now.
* @param bool $studentisonline is the student currently interacting with Moodle?
*/
public function process_abandon($timestamp) {
// TODO update DB.
// TODO raise event.
public function process_abandon($timestamp, $studentisonline) {
global $DB;

$transaction = $DB->start_delegated_transaction();
$this->attempt->timemodified = $timestamp;
$this->attempt->state = self::ABANDONED;
$DB->update_record('quiz_attempts', $this->attempt);

$this->fire_state_transition_event('quiz_attempt_abandoned', $timestamp);

$transaction->allow_commit();
}

/**
* Fire a state transition event.
* @param string $event the type of event. Should be listed in db/events.php.
* @param int $timestamp the timestamp to include in the event.
*/
protected function fire_state_transition_event($event, $timestamp) {
global $USER;

// Trigger event
$eventdata = new stdClass();
$eventdata->component = 'mod_quiz';
$eventdata->attemptid = $this->attempt->id;
$eventdata->timestamp = $timestamp;
$eventdata->userid = $this->attempt->userid;
$eventdata->quizid = $this->get_quizid();
$eventdata->cmid = $this->get_cmid();
$eventdata->courseid = $this->get_courseid();

// I don't think if (CLI_SCRIPT) is really the right logic here. The
// question is really 'is $USER currently set to a real user', but I cannot
// see standard Moodle function to answer that question. For example,
// cron fakes $USER.
if (CLI_SCRIPT) {
$eventdata->submitterid = null;
} else {
$eventdata->submitterid = $USER->id;
}

if ($event == 'quiz_attempt_submitted') {
$eventdata->timefinish = $timestamp; // Backwards compatibility.
}

events_trigger($event, $eventdata);
}

/**
Expand Down
22 changes: 22 additions & 0 deletions mod/quiz/db/events.php
Expand Up @@ -43,6 +43,7 @@
->component = 'mod_quiz';
->attemptid = // The id of the new quiz attempt.
->timestart = // The timestamp of when the attempt was started.
->timestamp = // The timestamp of when the attempt was started.
->userid = // The user id that the attempt belongs to.
->quizid = // The quiz id of the quiz the attempt belongs to.
->cmid = // The course_module id of the quiz the attempt belongs to.
Expand All @@ -52,10 +53,31 @@
->component = 'mod_quiz';
->attemptid = // The id of the quiz attempt that was submitted.
->timefinish = // The timestamp of when the attempt was submitted.
->timestamp = // The timestamp of when the attempt was submitted.
->userid = // The user id that the attempt belongs to.
->submitterid = // The user id of the user who sumitted the attempt.
->quizid = // The quiz id of the quiz the attempt belongs to.
->cmid = // The course_module id of the quiz the attempt belongs to.
->courseid = // The course id of the course the quiz belongs to.
quiz_attempt_overdue
->component = 'mod_quiz';
->attemptid = // The id of the quiz attempt that has become overdue.
->timestamp = // The timestamp of when the attempt become overdue.
->userid = // The user id that the attempt belongs to.
->submitterid = // The user id of the user who triggered this transition (may be null, e.g. on cron.).
->quizid = // The quiz id of the quiz the attempt belongs to.
->cmid = // The course_module id of the quiz the attempt belongs to.
->courseid = // The course id of the course the quiz belongs to.
quiz_attempt_abandoned
->component = 'mod_quiz';
->attemptid = // The id of the quiz attempt that was submitted.
->timestamp = // The timestamp of when the attempt was submitted.
->userid = // The user id that the attempt belongs to.
->submitterid = // The user id of the user who triggered this transition (may be null, e.g. on cron.).
->quizid = // The quiz id of the quiz the attempt belongs to.
->cmid = // The course_module id of the quiz the attempt belongs to.
->courseid = // The course id of the course the quiz belongs to.
*/
3 changes: 2 additions & 1 deletion mod/quiz/startattempt.php
Expand Up @@ -89,7 +89,7 @@
$messages = $accessmanager->prevent_access();

// If the attempt is now overdue, deal with that.
$quizobj->create_attempt_object($lastattempt)->handle_if_time_expired($timenow);
$quizobj->create_attempt_object($lastattempt)->handle_if_time_expired($timenow, true);

// And, if the attempt is now no longer in progress, redirect to the appropriate place.
if ($lastattempt->state == quiz_attempt::OVERDUE) {
Expand Down Expand Up @@ -264,6 +264,7 @@
$eventdata->component = 'mod_quiz';
$eventdata->attemptid = $attempt->id;
$eventdata->timestart = $attempt->timestart;
$eventdata->timestamp = $attempt->timestart;
$eventdata->userid = $attempt->userid;
$eventdata->quizid = $quizobj->get_quizid();
$eventdata->cmid = $quizobj->get_cmid();
Expand Down
5 changes: 3 additions & 2 deletions mod/quiz/view.php
Expand Up @@ -90,8 +90,9 @@
if ($unfinishedattempt = quiz_get_user_attempt_unfinished($quiz->id, $USER->id)) {
$attempts[] = $unfinishedattempt;

// If the attempt is now overdue, deal with that.
$quizobj->create_attempt_object($unfinishedattempt)->handle_if_time_expired(time());
// If the attempt is now overdue, deal with that - and pass isonline = false.
// We want the student notified in this case.
$quizobj->create_attempt_object($unfinishedattempt)->handle_if_time_expired(time(), false);

$unfinished = $unfinishedattempt->state == quiz_attempt::IN_PROGRESS ||
$unfinishedattempt->state == quiz_attempt::OVERDUE;
Expand Down

0 comments on commit a403bce

Please sign in to comment.