659 lines
28 KiB
PHP

<?php
/**
* This script lists student attempts
*
* @version $Id$
* @author Martin Dougiamas, Tim Hunt and others.
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package quiz
*/
require_once($CFG->libdir.'/tablelib.php');
require_once($CFG->dirroot.'/mod/quiz/report/overview/overviewsettings_form.php');
require_once($CFG->dirroot.'/mod/quiz/report/overview/overview_table.php');
class quiz_overview_report extends quiz_default_report {
/**
* Display the report.
*/
function display($quiz, $cm, $course) {
global $CFG, $COURSE, $DB;
$this->context = get_context_instance(CONTEXT_MODULE, $cm->id);
// Work out some display options - whether there is feedback, and whether scores should be shown.
$hasfeedback = quiz_has_feedback($quiz->id) && $quiz->grade > 1.e-7 && $quiz->sumgrades > 1.e-7;
$fakeattempt = new stdClass();
$fakeattempt->preview = false;
$fakeattempt->timefinish = $quiz->timeopen;
$reviewoptions = quiz_get_reviewoptions($quiz, $fakeattempt, $this->context);
$showgrades = $quiz->grade && $quiz->sumgrades && $reviewoptions->scores;
$download = optional_param('download', '', PARAM_ALPHA);
/// find out current groups mode
$currentgroup = groups_get_activity_group($cm, true);
if (!$students = get_users_by_capability($this->context, 'mod/quiz:attempt','','','','','','',false)){
$students = array();
} else {
$students = array_keys($students);
}
if (empty($currentgroup)) {
// all users who can attempt quizzes
$allowed = $students;
$groupstudents = array();
} else {
// all users who can attempt quizzes and who are in the currently selected group
if (!$groupstudents = get_users_by_capability($this->context, 'mod/quiz:attempt','','','','',$currentgroup,'',false)){
$groupstudents = array();
} else {
$groupstudents = array_keys($groupstudents);
}
$allowed = $groupstudents;
}
if (empty($currentgroup)||$groupstudents) {
if (optional_param('delete', 0, PARAM_BOOL)){
if($attemptids = optional_param('attemptid', array(), PARAM_INT)) {
//attempts need to be deleted
$this->delete_selected_attempts($quiz, $cm, $attemptids, $groupstudents);
//No need for a redirect, any attemptids that do not exist are ignored.
//So no problem if the user refreshes and tries to delete the same attempts
//twice.
}
} else if (optional_param('regrade', 0, PARAM_BOOL)){
if($attemptids = optional_param('attemptid', array(), PARAM_INT)) {
$this->regrade_selected_attempts($quiz, $attemptids, $groupstudents);
//No need for a redirect, any attemptids that do not exist are ignored.
//So no problem if the user refreshes and tries to delete the same attempts
//twice.
}
}
}
$pageoptions = array();
$pageoptions['id'] = $cm->id;
$pageoptions['q'] = $quiz->id;
$pageoptions['mode'] = 'overview';
$reporturl = new moodle_url($CFG->wwwroot.'/mod/quiz/report.php', $pageoptions);
$qmsubselect = quiz_report_qm_filter_select($quiz);
$mform = new mod_quiz_report_overview_settings($reporturl, array('qmsubselect'=> $qmsubselect, 'quiz'=>$quiz, 'currentgroup'=>$currentgroup));
if ($fromform = $mform->get_data()){
$regradeall = false;
$regradealldry = false;
$regradealldrydo = false;
$attemptsmode = $fromform->attemptsmode;
if ($qmsubselect){
//control is not on the form if
//the grading method is not set
//to grade one attempt per user eg. for average attempt grade.
$qmfilter = $fromform->qmfilter;
} else {
$qmfilter = 0;
}
$regradefilter = $fromform->regradefilter;
set_user_preference('quiz_report_overview_detailedmarks', $fromform->detailedmarks);
set_user_preference('quiz_report_pagesize', $fromform->pagesize);
$detailedmarks = $fromform->detailedmarks;
$pagesize = $fromform->pagesize;
} else {
$regradeall = optional_param('regradeall', 0, PARAM_BOOL);
$regradealldry = optional_param('regradealldry', 0, PARAM_BOOL);
$regradealldrydo = optional_param('regradealldrydo', 0, PARAM_BOOL);
$attemptsmode = optional_param('attemptsmode', null, PARAM_INT);
if ($qmsubselect){
$qmfilter = optional_param('qmfilter', 0, PARAM_INT);
} else {
$qmfilter = 0;
}
$regradefilter = optional_param('regradefilter', 0, PARAM_INT);
if ($attemptsmode === null){
//default
$attemptsmode = QUIZ_REPORT_ATTEMPTS_ALL;
} else if ($currentgroup){
//default for when a group is selected
if ($attemptsmode === null || $attemptsmode == QUIZ_REPORT_ATTEMPTS_ALL){
$attemptsmode = QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH;
}
} else if (!$currentgroup && $course->id == SITEID) {
//force report on front page to show all, unless a group is selected.
$attemptsmode = QUIZ_REPORT_ATTEMPTS_ALL;
}
$detailedmarks = get_user_preferences('quiz_report_overview_detailedmarks', 1);
$pagesize = get_user_preferences('quiz_report_pagesize', 0);
}
if (!$reviewoptions->scores) {
$detailedmarks = 0;
}
if ($pagesize < 1) {
$pagesize = QUIZ_REPORT_DEFAULT_PAGE_SIZE;
}
// We only want to show the checkbox to delete attempts
// if the user has permissions and if the report mode is showing attempts.
$candelete = has_capability('mod/quiz:deleteattempts', $this->context)
&& ($attemptsmode!= QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH_NO);
$displayoptions = array();
$displayoptions['attemptsmode'] = $attemptsmode;
$displayoptions['qmfilter'] = $qmfilter;
$displayoptions['regradefilter'] = $regradefilter;
//work out the sql for this table.
if ($detailedmarks) {
$questions = quiz_report_load_questions($quiz);
} else {
$questions = array();
}
$table = new quiz_report_overview_table($quiz , $qmsubselect, $groupstudents,
$students, $detailedmarks, $questions, $candelete, $reporturl,
$displayoptions, $this->context);
$table->is_downloading($download, get_string('reportoverview','quiz'),
"$COURSE->shortname ".format_string($quiz->name,true));
if (!$table->is_downloading()) {
// Only print headers if not asked to download data
$this->print_header_and_tabs($cm, $course, $quiz, "overview");
}
if ($regradeall){
$this->regrade_all(false, $quiz, $groupstudents);
} else if ($regradealldry){
$this->regrade_all(true, $quiz, $groupstudents);
} else if ($regradealldrydo){
$this->regrade_all_needed($quiz, $groupstudents);
}
if ($regradeall || $regradealldry || $regradealldrydo){
redirect($reporturl->out(false, $displayoptions), '', 5);
}
if ($groupmode = groups_get_activity_groupmode($cm)) { // Groups are being used
if (!$table->is_downloading()) {
groups_print_activity_menu($cm, $reporturl->out(false, $displayoptions));
}
}
// Print information on the number of existing attempts
if (!$table->is_downloading()) { //do not print notices when downloading
if ($strattemptnum = quiz_num_attempt_summary($quiz, $cm, true, $currentgroup)) {
echo '<div class="quizattemptcounts">' . $strattemptnum . '</div>';
}
}
$nostudents = false;
if (!$students){
notify(get_string('nostudentsyet'));
$nostudents = true;
}else if ($currentgroup && !$groupstudents){
notify(get_string('nostudentsingroup'));
$nostudents = true;
}
if (!$table->is_downloading()) {
// Print display options
$mform->set_data($displayoptions +compact('detailedmarks', 'pagesize'));
$mform->display();
}
$countregradeneeded = $this->count_regrade_all_needed($quiz, $groupstudents);
//regrade buttons
if ($currentgroup){
$a= new object();
$a->groupname = groups_get_group_name($currentgroup);
$a->coursestudents = $COURSE->students;
$a->countregradeneeded = $countregradeneeded;
$regradealldrydolabel = get_string('regradealldrydogroup', 'quiz_overview', $a);
$regradealldrylabel = get_string('regradealldrygroup', 'quiz_overview', $a);
$regradealllabel = get_string('regradeallgroup', 'quiz_overview', $a);
} else {
$regradealldrydolabel = get_string('regradealldrydo', 'quiz_overview', $countregradeneeded);
$regradealldrylabel = get_string('regradealldry', 'quiz_overview');
$regradealllabel = get_string('regradeall', 'quiz_overview');
}
if (has_capability('mod/quiz:grade', $this->context)){
echo '<div class="mdl-align">';
echo '<form action="'.$reporturl->out(true).'">';
echo '<div>';
echo $reporturl->hidden_params_out(array(), 0, $displayoptions);
echo '<input type="submit" name="regradeall" value="'.$regradealllabel.'"/>';
echo '<input type="submit" name="regradealldry" value="'.$regradealldrylabel.'"/>';
if ($countregradeneeded){
echo '<input type="submit" name="regradealldrydo" value="'.$regradealldrydolabel.'"/>';
}
echo '</div>';
echo '</form>';
echo '</div>';
}
if (!$nostudents || ($attemptsmode == QUIZ_REPORT_ATTEMPTS_ALL)){
// Print information on the grading method and whether we are displaying
//
if (!$table->is_downloading()) { //do not print notices when downloading
if ($strattempthighlight = quiz_report_highlighting_grading_method($quiz, $qmsubselect, $qmfilter)) {
echo '<div class="quizattemptcounts">' . $strattempthighlight . '</div>';
}
}
$showgrades = $quiz->grade && $quiz->sumgrades && $reviewoptions->scores;
$hasfeedback = quiz_has_feedback($quiz->id) && $quiz->grade > 1.e-7 && $quiz->sumgrades > 1.e-7;
// Construct the SQL
$fields = $DB->sql_concat('u.id', '\'#\'', 'COALESCE(qa.attempt, \'0\')').' AS uniqueid, '.
($qmsubselect?"($qmsubselect) AS gradedattempt, ":'').
'qa.uniqueid AS attemptuniqueid, qa.id AS attempt, u.id AS userid, u.idnumber, u.firstname, u.lastname, u.picture, '.
'qa.sumgrades, qa.timefinish, qa.timestart, qa.timefinish - qa.timestart AS duration ';
// This part is the same for all cases - join users and quiz_attempts tables
$from = '{user} u ';
$from .= 'LEFT JOIN {quiz_attempts} qa ON qa.userid = u.id AND qa.quiz = :quizid';
$params = array('quizid' => $quiz->id);
if ($qmsubselect && $qmfilter){
$from .= ' AND '.$qmsubselect;
}
switch ($attemptsmode){
case QUIZ_REPORT_ATTEMPTS_ALL:
// Show all attempts, including students who are no longer in the course
$where = 'qa.id IS NOT NULL AND qa.preview = 0';
break;
case QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH:
// Show only students with attempts
list($allowed_usql, $allowed_params) = $DB->get_in_or_equal($allowed, SQL_PARAMS_NAMED, 'u0000');
$params += $allowed_params;
$where = "u.id $allowed_usql AND qa.preview = 0 AND qa.id IS NOT NULL";
break;
case QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH_NO:
// Show only students without attempts
list($allowed_usql, $allowed_params) = $DB->get_in_or_equal($allowed, SQL_PARAMS_NAMED, 'u0000');
$params += $allowed_params;
$where = "u.id $allowed_usql AND qa.id IS NULL";
break;
case QUIZ_REPORT_ATTEMPTS_ALL_STUDENTS:
// Show all students with or without attempts
list($allowed_usql, $allowed_params) = $DB->get_in_or_equal($allowed, SQL_PARAMS_NAMED, 'u0000');
$params += $allowed_params;
$where = "u.id $allowed_usql AND (qa.preview = 0 OR qa.preview IS NULL)";
break;
}
$table->set_count_sql("SELECT COUNT(1) FROM $from WHERE $where", $params);
$sqlobject = new object;
$sqlobject->from = $from;
$sqlobject->where = $where;
$sqlobject->params = $params;
//test to see if there are any regraded attempts to be listed.
if (quiz_get_regraded_qs($sqlobject, 0, 1)){
$regradedattempts = true;
} else {
$regradedattempts = false;
}
$fields .= ', COALESCE((SELECT MAX(qqr.regraded) FROM {quiz_question_regrade} qqr WHERE qqr.attemptid = qa.uniqueid),-1) AS regraded';
if ($regradefilter){
$where .= ' AND COALESCE((SELECT MAX(qqr.regraded) FROM {quiz_question_regrade} qqr WHERE qqr.attemptid = qa.uniqueid),-1) !=\'-1\'';
}
$table->set_sql($fields, $from, $where, $params);
// Define table columns
$columns = array();
$headers = array();
if (!$table->is_downloading() && $candelete) {
$columns[]= 'checkbox';
$headers[]= NULL;
}
if (!$table->is_downloading() && $CFG->grade_report_showuserimage) {
$columns[]= 'picture';
$headers[]= '';
}
if (!$table->is_downloading()){
$columns[]= 'fullname';
$headers[]= get_string('name');
} else {
$columns[]= 'lastname';
$headers[]= get_string('lastname');
$columns[]= 'firstname';
$headers[]= get_string('firstname');
}
if ($CFG->grade_report_showuseridnumber) {
$columns[]= 'idnumber';
$headers[]= get_string('idnumber');
}
$columns[]= 'timestart';
$headers[]= get_string('startedon', 'quiz');
$columns[]= 'timefinish';
$headers[]= get_string('timecompleted','quiz');
$columns[]= 'duration';
$headers[]= get_string('attemptduration', 'quiz');
if ($detailedmarks) {
foreach ($questions as $id => $question) {
// Ignore questions of zero length
$columns[] = 'qsgrade'.$id;
$header = '#'.$question->number;
if (!$table->is_downloading()) {
$header .='<br />';
} else {
$header .=' ';
}
$header .='--/'.quiz_rescale_grade($question->grade, $quiz);
$headers[] = $header;
}
}
if ($regradedattempts){
$columns[] = 'regraded';
$headers[] = get_string('regrade', 'quiz_overview');
}
if ($showgrades) {
$columns[] = 'sumgrades';
$headers[] = get_string('grade', 'quiz').'/'.$quiz->grade;
}
if ($hasfeedback) {
$columns[] = 'feedbacktext';
$headers[] = get_string('feedback', 'quiz');
}
$table->define_columns($columns);
$table->define_headers($headers);
$table->sortable(true, 'uniqueid');
// Set up the table
$table->define_baseurl($reporturl->out(false, $displayoptions));
$table->collapsible(false);
$table->column_suppress('picture');
$table->column_suppress('fullname');
$table->column_suppress('idnumber');
$table->no_sorting('feedbacktext');
$table->column_class('picture', 'picture');
$table->column_class('lastname', 'bold');
$table->column_class('firstname', 'bold');
$table->column_class('fullname', 'bold');
$table->column_class('sumgrades', 'bold');
$table->set_attribute('id', 'attempts');
$table->out($pagesize, true);
}
if (!$table->is_downloading()) {
//should be quicker than a COUNT to test if there is at least one record :
if ($DB->get_records('quiz_grades', array('quiz'=> $quiz->id), '', '*', 0, 1)){
$imageurl = $CFG->wwwroot.'/mod/quiz/report/overview/overviewgraph.php?id='.$quiz->id;
print_heading(get_string('overviewreportgraph', 'quiz_overview'));
echo '<div class="mdl-align"><img src="'.$imageurl.'" alt="'.get_string('overviewreportgraph', 'quiz_overview').'" /></div>';
}
}
return true;
}
/**
* @param bool changedb whether to change contents of state and grades
* tables.
*/
function regrade_all($dry, $quiz, $groupstudents){
global $DB;
if (!has_capability('mod/quiz:grade', $this->context)) {
notify(get_string('regradenotallowed', 'quiz'));
return true;
}
// Fetch all attempts
if ($groupstudents){
list($usql, $params) = $DB->get_in_or_equal($groupstudents);
$select = "userid $usql AND ";
} else {
$select = '';
$params = array();
}
$select .= "quiz = ? AND preview = 0";
$params[] = $quiz->id;
if (!$attempts = $DB->get_records_select('quiz_attempts', $select, $params)) {
print_heading(get_string('noattempts', 'quiz'));
return true;
}
$this->clear_regrade_table($quiz, $groupstudents);
// Fetch all questions
$questions = question_load_questions(quiz_questions_in_quiz($quiz->questions), 'qqi.grade AS maxgrade, qqi.id AS instance',
'{quiz_question_instances} qqi ON qqi.quiz = ' . $quiz->id . ' AND q.id = qqi.question');
// Print heading
print_heading(get_string('regradingquiz', 'quiz', format_string($quiz->name)));
$qstodo = count($questions);
$qsdone = 0;
if ($qstodo > 1){
$qpb = new progress_bar('qregradingbar', 500, true);
$qpb->update($qsdone, $qstodo, "Question $qsdone of $qstodo");
}
$apb = new progress_bar('aregradingbar', 500, true);
// Loop through all questions and all attempts and regrade while printing progress info
$attemptstodo = count($attempts);
foreach ($questions as $question) {
$attemptsdone = 0;
$apb->restart();
echo '<p class="mdl-align"><strong>'.get_string('regradingquestion', 'quiz', $question->name).'</strong></p>';
@flush();@ob_flush();
foreach ($attempts as $attempt) {
set_time_limit(30);
$changed = regrade_question_in_attempt($question, $attempt, $quiz, true, $dry);
$attemptsdone++;
$a = new object();
$a->done = $attemptsdone;
$a->todo = $attemptstodo;
$apb->update($attemptsdone, $attemptstodo, get_string('attemptprogress', 'quiz_overview', $a));
}
$qsdone++;
if (isset($qpb)){
$a = new object();
$a->done = $qsdone;
$a->todo = $qstodo;
$qpb->update($qsdone, $qstodo, get_string('qprogress', 'quiz_overview', $a));
}
// the following makes sure that the output is sent immediately.
@flush();@ob_flush();
}
if (!$dry){
$this->check_overall_grades($quiz, $groupstudents);
}
}
function count_regrade_all_needed($quiz, $groupstudents){
global $DB;
// Fetch all attempts that need regrading
if ($groupstudents){
list($usql, $params) = $DB->get_in_or_equal($groupstudents);
$where = "qa.userid $usql AND ";
} else {
$where = '';
$params = array();
}
$where .= "qa.quiz = ? AND qa.preview = 0 AND qa.uniqueid = qqr.attemptid AND qqr.regraded = 0";
$params[] = $quiz->id;
return $DB->get_field_sql('SELECT COUNT(1) FROM {quiz_attempts} qa, {quiz_question_regrade} qqr WHERE '. $where, $params);
}
function regrade_all_needed($quiz, $groupstudents){
global $DB;
if (!has_capability('mod/quiz:grade', $this->context)) {
notify(get_string('regradenotallowed', 'quiz'));
return;
}
// Fetch all attempts that need regrading
if ($groupstudents){
list($usql, $params) = $DB->get_in_or_equal($groupstudents);
$where = "qa.userid $usql AND ";
} else {
$where = '';
$params = array();
}
$where .= "qa.quiz = ? AND qa.preview = 0 AND qa.uniqueid = qqr.attemptid AND qqr.regraded = 0";
$params[] = $quiz->id;
if (!$attempts = $DB->get_records_sql('SELECT qa.*, qqr.questionid FROM {quiz_attempts} qa, {quiz_question_regrade} qqr WHERE '. $where, $params)) {
print_heading(get_string('noattemptstoregrade', 'quiz_overview'));
return true;
}
$this->clear_regrade_table($quiz, $groupstudents);
// Fetch all questions
$questions = question_load_questions(quiz_questions_in_quiz($quiz->questions), 'qqi.grade AS maxgrade, qqi.id AS instance',
'{quiz_question_instances} qqi ON qqi.quiz = ' . $quiz->id . ' AND q.id = qqi.question');
// Print heading
print_heading(get_string('regradingquiz', 'quiz', format_string($quiz->name)));
$apb = new progress_bar('aregradingbar', 500, true);
// Loop through all questions and all attempts and regrade while printing progress info
$attemptstodo = count($attempts);
$attemptsdone = 0;
@flush();@ob_flush();
$attemptschanged = array();
foreach ($attempts as $attempt) {
$question = $questions[$attempt->questionid];
$changed = regrade_question_in_attempt($question, $attempt, $quiz, true);
if ($changed){
$attemptschanged[] = $attempt->uniqueid;
$usersschanged[] = $attempt->userid;
}
if (!empty($apb)){
$attemptsdone++;
$a = new object();
$a->done = $attemptsdone;
$a->todo = $attemptstodo;
$apb->update($attemptsdone, $attemptstodo, get_string('attemptprogress', 'quiz_overview', $a));
}
}
$this->check_overall_grades($quiz, array(), $attemptschanged);
}
function clear_regrade_table($quiz, $groupstudents){
global $DB;
// Fetch all attempts that need regrading
if ($groupstudents){
list($usql, $params) = $DB->get_in_or_equal($groupstudents);
$where = "qa.userid $usql AND ";
} else {
$usql = '';
$where = '';
$params = array();
}
$params[] = $quiz->id;
$delsql = 'DELETE FROM qqr USING {quiz_question_regrade} qqr, {quiz_attempts} qa WHERE qqr.attemptid = qa.uniqueid AND ';
if ($usql){
$delsql .= "qa.userid $usql AND ";
}
$delsql .='qa.quiz=?';
if (!$DB->execute($delsql, $params)){
print_error('err_failedtodeleteregrades', 'quiz_overview');
}
}
function check_overall_grades($quiz, $userids=array(), $attemptids=array()){
global $DB;
//recalculate $attempt->sumgrade
//already updated in regrade_question_in_attempt
$sql = "UPDATE {quiz_attempts} qa SET qa.sumgrades= " .
"(SELECT SUM(qs.grade) FROM {question_sessions} qns, {question_states} qs " .
"WHERE qns.newgraded = qs.id AND qns.attemptid = qa.uniqueid ) WHERE ";
$attemptsql='';
if (!$attemptids){
if ($userids){
list($usql, $params) = $DB->get_in_or_equal($userids);
$attemptsql .= "qa.userid $usql AND ";
} else {
$params = array();
}
$attemptsql .= "qa.quiz =? AND preview = 0";
$params[] = $quiz->id;
} else {
list($asql, $params) = $DB->get_in_or_equal($attemptids);
$attemptsql .= "qa.uniqueid $asql";
}
$sql .= $attemptsql;
if (!$DB->execute($sql, $params)){
print_error('err_failedtorecalculateattemptgrades', 'quiz_overview');
}
// Update the overall quiz grades
if ($attemptids){
//make sure we fetch all attempts for users to calculate grade.
//not just those that have changed.
$sql = "SELECT qa2.* FROM {quiz_attempts} qa2 WHERE qa2.userid IN (SELECT DISTINCT qa.userid FROM {quiz_attempts} qa WHERE $attemptsql)";
} else {
$sql = "SELECT qa.* FROM {quiz_attempts} qa WHERE $attemptsql";
}
if ($attempts = $DB->get_records_sql($sql, $params)) {
$attemptsbyuser = quiz_report_index_by_keys($attempts, array('userid', 'id'));
foreach($attemptsbyuser as $userid => $attemptsforuser) {
quiz_save_best_grade($quiz, $userid, $attemptsforuser);
}
}
}
function delete_selected_attempts($quiz, $cm, $attemptids, $groupstudents){
global $DB, $COURSE;
require_capability('mod/quiz:deleteattempts', $this->context);
$attemptids = optional_param('attemptid', array(), PARAM_INT);
if ($groupstudents){
list($usql, $params) = $DB->get_in_or_equal($groupstudents);
$where = "qa.userid $usql AND ";
}
foreach($attemptids as $attemptid) {
add_to_log($COURSE->id, 'quiz', 'delete attempt', 'report.php?id=' . $cm->id,
$attemptid, $cm->id);
quiz_delete_attempt($attemptid, $quiz);
}
}
function regrade_selected_attempts($quiz, $attemptids, $groupstudents){
global $DB;
require_capability('mod/quiz:grade', $this->context);
if ($groupstudents){
list($usql, $params) = $DB->get_in_or_equal($groupstudents);
$where = "qa.userid $usql AND ";
} else {
$params = array();
$where = '';
}
list($asql, $aparams) = $DB->get_in_or_equal($attemptids);
$where = "qa.id $asql AND ";
$params = array_merge($params, $aparams);
$where .= "qa.quiz = ? AND qa.preview = 0";
$params[] = $quiz->id;
if (!$attempts = $DB->get_records_sql('SELECT qa.* FROM {quiz_attempts} qa WHERE '. $where, $params)) {
print_error('noattemptstoregrade', 'quiz_overview');
}
// Fetch all questions
$questions = question_load_questions(quiz_questions_in_quiz($quiz->questions), 'qqi.grade AS maxgrade, qqi.id AS instance',
'{quiz_question_instances} qqi ON qqi.quiz = ' . $quiz->id . ' AND q.id = qqi.question');
$updateoverallgrades = array();
foreach($attempts as $attempt) {
foreach ($questions as $question){
$changed = regrade_question_in_attempt($question, $attempt, $quiz, true);
}
$updateoverallgrades[] = $attempt->uniqueid;
}
$this->check_overall_grades($quiz, array(), $updateoverallgrades);
}
}
?>