array_intersect(array_keys($teachersgroups), array_keys($studentsgroups));
}
+ /**
+ * Has the student, in this attempt, engaged with the quiz in a non-trivial way?
+ * That is, is there any question worth a non-zero number of marks, where
+ * the student has made some response that we have saved?
+ * @return bool true if we have saved a response for at least one graded question.
+ */
+ public function has_response_to_at_least_one_graded_question() {
+ foreach ($this->quba->get_attempt_iterator() as $qa) {
+ if ($qa->get_max_mark() == 0) {
+ continue;
+ }
+ if ($qa->get_num_steps() > 1) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Get extra summary information about this attempt.
*
* The values are arrays with two items, title and content. Each of these
* will be either a string, or a renderable.
*
+ * @param question_display_options $options the display options for this quiz attempt at this time.
* @return array as described above.
*/
public function get_additional_summary_data(question_display_options $options) {
$this->fire_state_transition_event('\mod_quiz\event\attempt_becameoverdue', $timestamp);
$transaction->allow_commit();
+
+ quiz_send_overdue_message($this);
}
/**
'callback' => '\mod_quiz\group_observers::group_member_removed',
),
- // Handle our own \mod_quiz\event\attempt_becameoverdue event, to email
- // the student to let them know they forgot to submit, and that they have another chance.
- array(
- 'eventname' => '\mod_quiz\event\attempt_becameoverdue',
- 'includefile' => '/mod/quiz/locallib.php',
- 'callback' => 'quiz_attempt_overdue_handler',
- 'internal' => false,
- ),
-
// Handle our own \mod_quiz\event\attempt_submitted event, as a way to
// send confirmation messages asynchronously.
array(
/**
* Send the notification message when a quiz attempt becomes overdue.
*
- * @param object $course the course
- * @param object $quiz the quiz
- * @param object $attempt this attempt just finished
- * @param object $context the quiz context
- * @param object $cm the coursemodule for this quiz
+ * @param quiz_attempt $attemptobj all the data about the quiz attempt.
*/
-function quiz_send_overdue_message($course, $quiz, $attempt, $context, $cm) {
+function quiz_send_overdue_message($attemptobj) {
global $CFG, $DB;
- // Do nothing if required objects not present.
- if (empty($course) or empty($quiz) or empty($attempt) or empty($context)) {
- throw new coding_exception('$course, $quiz, $attempt, $context and $cm must all be set.');
- }
+ $submitter = $DB->get_record('user', array('id' => $attemptobj->get_userid()), '*', MUST_EXIST);
- $submitter = $DB->get_record('user', array('id' => $attempt->userid), '*', MUST_EXIST);
+ if (!$attemptobj->has_capability('mod/quiz:emailwarnoverdue', $submitter->id, false)) {
+ return; // Message not required.
+ }
- if (!has_capability('mod/quiz:emailwarnoverdue', $context, $submitter, false)) {
+ if (!$attemptobj->has_response_to_at_least_one_graded_question()) {
return; // Message not required.
}
// Prepare lots of useful information that admins might want to include in
// the email message.
- $quizname = format_string($quiz->name);
+ $quizname = format_string($attemptobj->get_quiz_name());
$deadlines = array();
- if ($quiz->timelimit) {
- $deadlines[] = $attempt->timestart + $quiz->timelimit;
+ if ($attemptobj->get_quiz()->timelimit) {
+ $deadlines[] = $attemptobj->get_attempt()->timestart + $attemptobj->get_quiz()->timelimit;
}
- if ($quiz->timeclose) {
- $deadlines[] = $quiz->timeclose;
+ if ($attemptobj->get_quiz()->timeclose) {
+ $deadlines[] = $attemptobj->get_quiz()->timeclose;
}
$duedate = min($deadlines);
- $graceend = $duedate + $quiz->graceperiod;
+ $graceend = $duedate + $attemptobj->get_quiz()->graceperiod;
$a = new stdClass();
// Course info.
- $a->coursename = $course->fullname;
- $a->courseshortname = $course->shortname;
+ $a->coursename = format_string($attemptobj->get_course()->fullname);
+ $a->courseshortname = format_string($attemptobj->get_course()->shortname);
// Quiz info.
$a->quizname = $quizname;
- $a->quizurl = $CFG->wwwroot . '/mod/quiz/view.php?id=' . $cm->id;
+ $a->quizurl = $attemptobj->view_url();
$a->quizlink = '<a href="' . $a->quizurl . '">' . $quizname . '</a>';
// Attempt info.
- $a->attemptduedate = userdate($duedate);
+ $a->attemptduedate = userdate($duedate);
$a->attemptgraceend = userdate($graceend);
- $a->attemptsummaryurl = $CFG->wwwroot . '/mod/quiz/summary.php?attempt=' . $attempt->id;
+ $a->attemptsummaryurl = $attemptobj->summary_url()->out(false);
$a->attemptsummarylink = '<a href="' . $a->attemptsummaryurl . '">' . $quizname . ' review</a>';
// Student's info.
$a->studentidnumber = $submitter->idnumber;
context_module::instance($cm->id), $cm);
}
-/**
- * Handle the quiz_attempt_overdue event.
- *
- * For quizzes with applicable settings, this sends a message to the user, reminding
- * them that they forgot to submit, and that they have another chance to do so.
- *
- * @param object $event the event object.
- */
-function quiz_attempt_overdue_handler($event) {
- global $DB;
-
- $course = $DB->get_record('course', array('id' => $event->courseid));
- $attempt = $event->get_record_snapshot('quiz_attempts', $event->objectid);
- $quiz = $event->get_record_snapshot('quiz', $attempt->quiz);
- $cm = get_coursemodule_from_id('quiz', $event->get_context()->instanceid, $event->courseid);
-
- if (!($course && $quiz && $cm && $attempt)) {
- // Something has been deleted since the event was raised. Therefore, the
- // event is no longer relevant.
- return true;
- }
-
- return quiz_send_overdue_message($course, $quiz, $attempt,
- context_module::instance($cm->id), $cm);
-}
-
/**
* Handle groups_member_added event
*
// Process some responses from the student.
$attemptobj = quiz_attempt::create($attempt->id);
+ $this->assertFalse($attemptobj->has_response_to_at_least_one_graded_question());
+
$prefix1 = $quba->get_field_prefix(1);
$prefix2 = $quba->get_field_prefix(2);
// Finish the attempt.
$attemptobj = quiz_attempt::create($attempt->id);
+ $this->assertTrue($attemptobj->has_response_to_at_least_one_graded_question());
$attemptobj->process_finish($timenow, false);
// Re-load quiz attempt data.
$this->assertEquals(true, $attemptobj->is_finished());
$this->assertEquals($timenow, $attemptobj->get_submitted_date());
$this->assertEquals($user1->id, $attemptobj->get_userid());
+ $this->assertTrue($attemptobj->has_response_to_at_least_one_graded_question());
// Check quiz grades.
$grades = quiz_get_user_grades($quiz, $user1->id);
// Process some responses from the student.
$attemptobj = quiz_attempt::create($attempt->id);
+ $this->assertFalse($attemptobj->has_response_to_at_least_one_graded_question());
$tosubmit = array();
$selectedquestionid = $quba->get_question_attempt(1)->get_question()->id;
// Finish the attempt.
$attemptobj = quiz_attempt::create($attempt->id);
+ $this->assertTrue($attemptobj->has_response_to_at_least_one_graded_question());
$attemptobj->process_finish($timenow, false);
// Re-load quiz attempt data.
$this->assertEquals(true, $attemptobj->is_finished());
$this->assertEquals($timenow, $attemptobj->get_submitted_date());
$this->assertEquals($user1->id, $attemptobj->get_userid());
+ $this->assertTrue($attemptobj->has_response_to_at_least_one_graded_question());
// Check quiz grades.
$grades = quiz_get_user_grades($quiz, $user1->id);
// Process some responses from the student.
$attemptobj = quiz_attempt::create($attempt->id);
+ $this->assertFalse($attemptobj->has_response_to_at_least_one_graded_question());
+
$tosubmit = array(1 => array('answer' => $correctresponse));
$attemptobj->process_submitted_actions($timenow, false, $tosubmit);
// Finish the attempt.
$attemptobj = quiz_attempt::create($attempt->id);
+ $this->assertTrue($attemptobj->has_response_to_at_least_one_graded_question());
+
$attemptobj->process_finish($timenow, false);
// Re-load quiz attempt data.
$this->assertEquals(true, $attemptobj->is_finished());
$this->assertEquals($timenow, $attemptobj->get_submitted_date());
$this->assertEquals($user1->id, $attemptobj->get_userid());
+ $this->assertTrue($attemptobj->has_response_to_at_least_one_graded_question());
// Check quiz grades.
$grades = quiz_get_user_grades($this->quizwithvariants, $user1->id);
$gradebookgrade = array_shift($gradebookitem->grades);
$this->assertEquals(100, $gradebookgrade->grade);
}
-
}
folder to take advantage of auto-loading. This has involved renaming them.
see the list in mod/quiz/db/renamedclasses.php.
+* The quiz no longer handles its own \mod_quiz\event\attempt_becameoverdue event,
+ and so the event handler function quiz_attempt_overdue_handler has been deleted.
+ Also, the internal function quiz_send_overdue_message has add the arguments
+ changed. It now takes the $attemptobj object, not separate stdClass objects.
* Major changes to the Edit quiz page.
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
- * Quiz statistics report version information.
+ * Quiz activity version information.
*
* @package mod_quiz
* @copyright 1999 onwards Martin Dougiamas {@link http://moodle.com}
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2014100200; // The current module version (Date: YYYYMMDDXX).
+$plugin->version = 2014101500; // The current module version (Date: YYYYMMDDXX).
$plugin->requires = 2014050800; // Requires this Moodle version.
$plugin->component = 'mod_quiz'; // Full name of the plugin (used for diagnostics).
$plugin->cron = 60;