/** * For a given question in an attempt we walk the complete history of states * and recalculate the grades as we go along. * * This is used when a question is changed and old student * responses need to be marked with the new version of a question. * * TODO: Make sure this is not quiz-specific * * @return boolean Indicates whether the grade has changed * @param object $question A question object * @param object $attempt The attempt, in which the question needs to be regraded. * @param object $cmoptions * @param boolean $verbose Optional. Whether to print progress information or not. */ function regrade_question_in_attempt($question, $attempt, $cmoptions, $verbose = false) { // load all states for this question in this attempt, ordered in sequence if ($states = get_records_select('question_states', "attempt = '{$attempt->uniqueid}' AND question = '{$question->id}'", 'seq_number ASC')) { $states = array_values($states); // Subtract the grade for the latest state from $attempt->sumgrades to get the // sumgrades for the attempt without this question. $attempt->sumgrades -= $states[count($states) - 1]->grade; // Initialise the replaystate $state = clone $states[0]; $state->manualcomment = get_field('question_sessions', 'manualcomment', 'attemptid', $attempt->uniqueid, 'questionid', $question->id); restore_question_state($question, $state); $state->sumpenalty = 0.0; $replaystate = clone $state; $replaystate->last_graded = $state; $changed = false; for ($j = 1; $j < count($states); $j++) { restore_question_state($question, $states[$j]); $action = new stdClass(); $action->responses = $states[$j]->responses; $action->timestamp = $states[$j]->timestamp; // Change event to submit so that it will be reprocessed if (QUESTION_EVENTCLOSE == $states[$j]->event or QUESTION_EVENTGRADE == $states[$j]->event or QUESTION_EVENTCLOSEANDGRADE == $states[$j]->event) { $action->event = QUESTION_EVENTSUBMIT; // By default take the event that was saved in the database } else { $action->event = $states[$j]->event; } if ($action->event == QUESTION_EVENTMANUALGRADE) { // Ensure that the grade is in range - in the past this was not checked, // but now it is (MDL-14835) - so we need to ensure the data is valid before // proceeding. if ($states[$j]->grade < 0) { $states[$j]->grade = 0; } else { if ($states[$j]->grade > $question->maxgrade) { $states[$j]->grade = $question->maxgrade; } } $error = question_process_comment($question, $replaystate, $attempt, $replaystate->manualcomment, $states[$j]->grade); if (is_string($error)) { notify($error); } } else { // Reprocess (regrade) responses if (!question_process_responses($question, $replaystate, $action, $cmoptions, $attempt)) { $verbose && notify("Couldn't regrade state #{$state->id}!"); } } // We need rounding here because grades in the DB get truncated // e.g. 0.33333 != 0.3333333, but we want them to be equal here if (round((double) $replaystate->raw_grade, 5) != round((double) $states[$j]->raw_grade, 5) or round((double) $replaystate->penalty, 5) != round((double) $states[$j]->penalty, 5) or round((double) $replaystate->grade, 5) != round((double) $states[$j]->grade, 5)) { $changed = true; } $replaystate->id = $states[$j]->id; $replaystate->changed = true; $replaystate->update = true; // This will ensure that the existing database entry is updated rather than a new one created save_question_session($question, $replaystate); } if ($changed) { // TODO, call a method in quiz to do this, where 'quiz' comes from // the question_attempts table. update_record('quiz_attempts', $attempt); } return $changed; } return false; }
/** * Displays the report. */ function display($quiz, $cm, $course) { global $CFG, $QTYPES; $viewoptions = array('mode' => 'grading', 'q' => $quiz->id); if ($questionid = optional_param('questionid', 0, PARAM_INT)) { $viewoptions += array('questionid' => $questionid); } // grade question specific parameters $gradeungraded = optional_param('gradeungraded', 0, PARAM_INT); if ($userid = optional_param('userid', 0, PARAM_INT)) { $viewoptions += array('userid' => $userid); } if ($attemptid = optional_param('attemptid', 0, PARAM_INT)) { $viewoptions += array('attemptid' => $attemptid); } if ($gradeall = optional_param('gradeall', 0, PARAM_INT)) { $viewoptions += array('gradeall' => $gradeall); } if ($gradeungraded = optional_param('gradeungraded', 0, PARAM_INT)) { $viewoptions += array('gradeungraded' => $gradeungraded); } if ($gradenextungraded = optional_param('gradenextungraded', 0, PARAM_INT)) { $viewoptions += array('gradenextungraded' => $gradenextungraded); } $this->cm = $cm; $this->print_header_and_tabs($cm, $course, $quiz, $reportmode = "grading"); // Check permissions $this->context = get_context_instance(CONTEXT_MODULE, $cm->id); if (!has_capability('mod/quiz:grade', $this->context)) { notify(get_string('gradingnotallowed', 'quiz_grading')); return true; } $gradeableqs = quiz_report_load_questions($quiz); $questionsinuse = implode(',', array_keys($gradeableqs)); foreach ($gradeableqs as $qid => $question) { if (!$QTYPES[$question->qtype]->is_question_manual_graded($question, $questionsinuse)) { unset($gradeableqs[$qid]); } } if (empty($gradeableqs)) { print_heading(get_string('noessayquestionsfound', 'quiz')); return true; } else { if (count($gradeableqs) == 1) { $questionid = array_shift(array_keys($gradeableqs)); } } $currentgroup = groups_get_activity_group($this->cm, true); $this->users = get_users_by_capability($this->context, array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'), '', '', '', '', $currentgroup, '', false); $this->userids = implode(',', array_keys($this->users)); if (!empty($questionid)) { if (!isset($gradeableqs[$questionid])) { error("Gradeable question with id {$questionid} not found"); } else { $question =& $gradeableqs[$questionid]; } $question->maxgrade = get_field('quiz_question_instances', 'grade', 'quiz', $quiz->id, 'question', $question->id); // Some of the questions code is optimised to work with several questions // at once so it wants the question to be in an array. The array key // must be the question id. $key = $question->id; $questions[$key] =& $question; // We need to add additional questiontype specific information to // the question objects. if (!get_question_options($questions)) { error("Unable to load questiontype specific question information"); } // This will have extended the question object so that it now holds // all the information about the questions that may be needed later. } add_to_log($course->id, "quiz", "manualgrading", "report.php?mode=grading&q={$quiz->id}", "{$quiz->id}", "{$cm->id}"); echo '<div id="overDiv" style="position:absolute; visibility:hidden; z-index:1000;"></div>'; // for overlib if ($data = data_submitted()) { // post data submitted, process it require_sesskey(); // now go through all of the responses and save them. $allok = true; foreach ($data->manualgrades as $uniqueid => $response) { // get our attempt $uniqueid = clean_param($uniqueid, PARAM_INT); if (!($attempt = get_record_sql("SELECT * FROM {$CFG->prefix}quiz_attempts " . "WHERE uniqueid = {$uniqueid} AND " . "userid IN ({$this->userids}) AND " . "quiz=" . $quiz->id))) { error('No such attempt ID exists'); } // Load the state for this attempt (The questions array was created earlier) $states = get_question_states($questions, $quiz, $attempt); // The $states array is indexed by question id but because we are dealing // with only one question there is only one entry in this array $state =& $states[$question->id]; // the following will update the state and attempt $error = question_process_comment($question, $state, $attempt, $response['comment'], $response['grade']); if (is_string($error)) { notify($error); $allok = false; } else { if ($state->changed) { // If the state has changed save it and update the quiz grade save_question_session($question, $state); quiz_save_best_grade($quiz, $attempt->userid); } } } if ($allok) { notify(get_string('changessaved', 'quiz'), 'notifysuccess'); } else { notify(get_string('changessavedwitherrors', 'quiz'), 'notifysuccess'); } } $this->viewurl = new moodle_url($CFG->wwwroot . '/mod/quiz/report.php', $viewoptions); /// find out current groups mode if ($groupmode = groups_get_activity_groupmode($this->cm)) { // Groups are being used groups_print_activity_menu($this->cm, $this->viewurl->out(false, array('userid' => 0, 'attemptid' => 0))); } echo '<div class="quizattemptcounts">' . quiz_num_attempt_summary($quiz, $cm, true, $currentgroup) . '</div>'; if (empty($this->users)) { if ($currentgroup) { notify(get_string('nostudentsingroup')); } else { notify(get_string('nostudentsyet')); } return true; } $gradeablequestionids = implode(',', array_keys($gradeableqs)); $qattempts = quiz_get_total_qas_graded_and_ungraded($quiz, $gradeablequestionids, $this->userids); if (empty($qattempts)) { notify(get_string('noattemptstoshow', 'quiz')); return true; } $qmenu = array(); foreach ($gradeableqs as $qid => $questionformenu) { $a = new object(); $a->number = $gradeableqs[$qid]->number; $a->name = $gradeableqs[$qid]->name; $a->gradedattempts = $qattempts[$qid]->gradedattempts; $a->totalattempts = $qattempts[$qid]->totalattempts; $a->openspan = ''; $a->closespan = ''; $qmenu[$qid] = get_string('questiontitle', 'quiz_grading', $a); } if (count($gradeableqs) != 1) { $qurl = fullclone($this->viewurl); $qurl->remove_params('questionid', 'attemptid', 'gradeall', 'gradeungraded', 'gradenextungraded'); $menu = popup_form($qurl->out() . '&questionid=', $qmenu, 'questionid', $questionid, 'choose', '', '', true); echo '<div class="mdl-align">' . $menu . '</div>'; } if (!$questionid) { return true; } $a = new object(); $a->number = $question->number; $a->name = $question->name; $a->gradedattempts = $qattempts[$question->id]->gradedattempts; $a->totalattempts = $qattempts[$question->id]->totalattempts; $a->openspan = '<span class="highlightgraded">'; $a->closespan = '</span>'; print_heading(get_string('questiontitle', 'quiz_grading', $a)); // our 3 different views // the first one displays all of the manually graded questions in the quiz // with the number of ungraded attempts for each question // the second view displays the users who have answered the essay question // and all of their attempts at answering the question // the third prints the question with a comment // and grade form underneath it $ungraded = $qattempts[$questionid]->totalattempts - $qattempts[$questionid]->gradedattempts; if ($gradenextungraded || $gradeungraded || $gradeall || $userid || $attemptid) { $this->print_questions_and_form($quiz, $question, $userid, $attemptid, $gradeungraded, $gradenextungraded, $ungraded); } else { $this->view_question($quiz, $question, $qattempts[$questionid]->totalattempts, $ungraded); } return true; }
/** * For a given question in an attempt we walk the complete history of states * and recalculate the grades as we go along. * * This is used when a question is changed and old student * responses need to be marked with the new version of a question. * * TODO: Make sure this is not quiz-specific * * @return boolean Indicates whether the grade has changed * @param object $question A question object * @param object $attempt The attempt, in which the question needs to be regraded. * @param object $cmoptions * @param boolean $verbose Optional. Whether to print progress information or not. * @param boolean $dryrun Optional. Whether to make changes to grades records * or record that changes need to be made for a later regrade. */ function regrade_question_in_attempt($question, $attempt, $cmoptions, $verbose = false, $dryrun = false) { global $DB; // load all states for this question in this attempt, ordered in sequence if ($states = $DB->get_records('question_states', array('attempt' => $attempt->uniqueid, 'question' => $question->id), 'seq_number ASC')) { $states = array_values($states); // Subtract the grade for the latest state from $attempt->sumgrades to get the // sumgrades for the attempt without this question. $attempt->sumgrades -= $states[count($states) - 1]->grade; // Initialise the replaystate $replaystate = question_load_specific_state($question, $cmoptions, $attempt, $states[0]->id); $replaystate->sumpenalty = 0; $replaystate->last_graded->sumpenalty = 0; $changed = false; for ($j = 1; $j < count($states); $j++) { restore_question_state($question, $states[$j]); $action = new stdClass(); $action->responses = $states[$j]->responses; $action->timestamp = $states[$j]->timestamp; // Change event to submit so that it will be reprocessed if (in_array($states[$j]->event, array(QUESTION_EVENTCLOSE, QUESTION_EVENTGRADE, QUESTION_EVENTCLOSEANDGRADE))) { $action->event = QUESTION_EVENTSUBMIT; // By default take the event that was saved in the database } else { $action->event = $states[$j]->event; } if ($action->event == QUESTION_EVENTMANUALGRADE) { // Ensure that the grade is in range - in the past this was not checked, // but now it is (MDL-14835) - so we need to ensure the data is valid before // proceeding. if ($states[$j]->grade < 0) { $states[$j]->grade = 0; $changed = true; } else { if ($states[$j]->grade > $question->maxgrade) { $states[$j]->grade = $question->maxgrade; $changed = true; } } if (!$dryrun) { $error = question_process_comment($question, $replaystate, $attempt, $replaystate->manualcomment, $states[$j]->grade); if (is_string($error)) { notify($error); } } else { $replaystate->grade = $states[$j]->grade; } } else { // Reprocess (regrade) responses if (!question_process_responses($question, $replaystate, $action, $cmoptions, $attempt) && $verbose) { $a = new stdClass(); $a->qid = $question->id; $a->stateid = $states[$j]->id; notify(get_string('errorduringregrade', 'question', $a)); } // We need rounding here because grades in the DB get truncated // e.g. 0.33333 != 0.3333333, but we want them to be equal here if (round((double) $replaystate->raw_grade, 5) != round((double) $states[$j]->raw_grade, 5) or round((double) $replaystate->penalty, 5) != round((double) $states[$j]->penalty, 5) or round((double) $replaystate->grade, 5) != round((double) $states[$j]->grade, 5)) { $changed = true; } // If this was previously a closed state, and it has been knoced back to // graded, then fix up the state again. if ($replaystate->event == QUESTION_EVENTGRADE && ($states[$j]->event == QUESTION_EVENTCLOSE || $states[$j]->event == QUESTION_EVENTCLOSEANDGRADE)) { $replaystate->event = $states[$j]->event; } } $replaystate->id = $states[$j]->id; $replaystate->changed = true; $replaystate->update = true; // This will ensure that the existing database entry is updated rather than a new one created if (!$dryrun) { save_question_session($question, $replaystate); } } if ($changed) { if (!$dryrun) { // TODO, call a method in quiz to do this, where 'quiz' comes from // the question_attempts table. $DB->update_record('quiz_attempts', $attempt); } } if ($changed) { $toinsert = new object(); $toinsert->oldgrade = round((double) $states[count($states) - 1]->grade, 5); $toinsert->newgrade = round((double) $replaystate->grade, 5); $toinsert->attemptid = $attempt->uniqueid; $toinsert->questionid = $question->id; //the grade saved is the old grade if the new grade is saved //it is the new grade if this is a dry run. $toinsert->regraded = $dryrun ? 0 : 1; $toinsert->timemodified = time(); $DB->insert_record('quiz_question_regrade', $toinsert); return true; } else { return false; } } return false; }
// Some of the questions code is optimised to work with several questions // at once so it wants the question to be in an array. $key = $question->id; $questions[$key] =& $question; // Add additional questiontype specific information to the question objects. if (!get_question_options($questions)) { error("Unable to load questiontype specific question information"); } // Load state $states = get_question_states($questions, $quiz, $attempt); // The $states array is indexed by question id but because we are dealing // with only one question there is only one entry in this array $state =& $states[$question->id]; print_header(); print_heading(format_string($question->name)); //add_to_log($course->id, 'quiz', 'review', "review.php?id=$cm->id&attempt=$attempt->id", "$quiz->id", "$cm->id"); if ($data = data_submitted() and confirm_sesskey()) { // the following will update the state and attempt question_process_comment($question, $state, $attempt, $data->response['comment'], $data->response['grade']); // If the state has changed save it and update the quiz grade if ($state->changed) { save_question_session($question, $state); quiz_save_best_grade($quiz, $attempt->userid); } notify(get_string('changessaved')); echo '<div class="boxaligncenter"><input type="button" onclick="window.opener.location.reload(1); self.close();return false;" value="' . get_string('closewindow') . "\" /></div>"; print_footer(); exit; } question_print_comment_box($question, $state, $attempt, $CFG->wwwroot . '/mod/quiz/comment.php'); print_footer();
/** * Displays the report. */ function display($quiz, $cm, $course) { global $CFG, $SESSION, $USER, $db, $QTYPES; $action = optional_param('action', 'viewquestions', PARAM_ALPHA); $questionid = optional_param('questionid', 0, PARAM_INT); $this->print_header_and_tabs($cm, $course, $quiz, $reportmode = "grading"); // Check permissions $context = get_context_instance(CONTEXT_MODULE, $cm->id); if (!has_capability('mod/quiz:grade', $context)) { notify(get_string('gradingnotallowed', 'quiz_grading')); return true; } if (!empty($questionid)) { if (!($question = get_record('question', 'id', $questionid))) { error("Question with id {$questionid} not found"); } $question->maxgrade = get_field('quiz_question_instances', 'grade', 'quiz', $quiz->id, 'question', $question->id); // Some of the questions code is optimised to work with several questions // at once so it wants the question to be in an array. The array key // must be the question id. $key = $question->id; $questions[$key] =& $question; // We need to add additional questiontype specific information to // the question objects. if (!get_question_options($questions)) { error("Unable to load questiontype specific question information"); } // This will have extended the question object so that it now holds // all the information about the questions that may be needed later. } add_to_log($course->id, "quiz", "manualgrading", "report.php?mode=grading&q={$quiz->id}", "{$quiz->id}", "{$cm->id}"); echo '<div id="overDiv" style="position:absolute; visibility:hidden; z-index:1000;"></div>'; // for overlib if ($data = data_submitted()) { // post data submitted, process it confirm_sesskey(); // now go through all of the responses and save them. foreach ($data->manualgrades as $uniqueid => $response) { // get our attempt if (!($attempt = get_record('quiz_attempts', 'uniqueid', $uniqueid))) { error('No such attempt ID exists'); } // Load the state for this attempt (The questions array was created earlier) $states = get_question_states($questions, $quiz, $attempt); // The $states array is indexed by question id but because we are dealing // with only one question there is only one entry in this array $state =& $states[$question->id]; // the following will update the state and attempt question_process_comment($question, $state, $attempt, $response['comment'], $response['grade']); // If the state has changed save it and update the quiz grade if ($state->changed) { save_question_session($question, $state); quiz_save_best_grade($quiz, $attempt->userid); } } notify(get_string('changessaved', 'quiz'), 'notifysuccess'); } // our 3 different views // the first one displays all of the manually graded questions in the quiz // with the number of ungraded attempts for each question // the second view displays the users who have answered the essay question // and all of their attempts at answering the question // the third prints the question with a comment // and grade form underneath it switch ($action) { case 'viewquestions': $this->view_questions($quiz); break; case 'viewquestion': $this->view_question($quiz, $question); break; case 'grade': $this->print_questions_and_form($quiz, $question); break; } return true; }
/** * Process a manual comment for a question in this attempt. * @param $questionid * @param integer $questionid the question id * @param string $comment the new comment from the teacher. * @param mixed $grade the grade the teacher assigned, or '' to not change the grade. * @return mixed true on success, a string error message if a problem is detected * (for example score out of range). */ public function process_comment($questionid, $comment, $commentformat, $grade) { // I am not sure it is a good idea to have update methods here - this // class is only about getting data out of the question engine, and // helping to display it, apart from this. $this->ensure_question_loaded($questionid); $this->ensure_state_loaded($questionid); $state = $this->states[$questionid]; $error = question_process_comment($this->questions[$questionid], $state, $this->attempt, $comment, $commentformat, $grade); // If the state was update (successfully), save the changes. if (!is_string($error) && $state->changed) { if (!save_question_session($this->questions[$questionid], $state)) { $error = get_string('errorudpatingquestionsession', 'quiz'); } if (!quiz_save_best_grade($this->quiz, $this->attempt->userid)) { $error = get_string('errorudpatingbestgrade', 'quiz'); } } return $error; }
public function process_comment($questionid, $comment, $grade) { $this->ensure_question_loaded($questionid); $this->ensure_state_loaded($questionid); $state = $this->states[$questionid]; $error = question_process_comment($this->questions[$questionid], $state, $this->attempt, $comment, $grade); // If the state was update (successfully), save the changes. if (!is_string($error) && $state->changed) { if (!save_question_session($this->questions[$questionid], $state)) { $error = get_string('errorudpatingquestionsession', 'quiz'); } if (!quiz_save_best_grade($this->quiz, $this->attempt->userid)) { $error = get_string('errorudpatingbestgrade', 'quiz'); } } return $error; }
/** * Displays the report. */ function display($quiz, $cm, $course) { global $CFG, $QTYPES, $DB, $OUTPUT, $PAGE; $viewoptions = array('mode' => 'grading', 'q' => $quiz->id); if ($questionid = optional_param('questionid', 0, PARAM_INT)) { $viewoptions += array('questionid' => $questionid); } // grade question specific parameters if ($userid = optional_param('userid', 0, PARAM_INT)) { $viewoptions += array('userid' => $userid); } if ($attemptid = optional_param('attemptid', 0, PARAM_INT)) { $viewoptions += array('attemptid' => $attemptid); } if ($gradeall = optional_param('gradeall', 0, PARAM_INT)) { $viewoptions += array('gradeall' => $gradeall); } if ($gradeungraded = optional_param('gradeungraded', 0, PARAM_INT)) { $viewoptions += array('gradeungraded' => $gradeungraded); } if ($gradenextungraded = optional_param('gradenextungraded', 0, PARAM_INT)) { $viewoptions += array('gradenextungraded' => $gradenextungraded); } $this->cm = $cm; $this->print_header_and_tabs($cm, $course, $quiz, $reportmode = "grading"); // Check permissions $this->context = get_context_instance(CONTEXT_MODULE, $cm->id); if (!has_capability('mod/quiz:grade', $this->context)) { echo $OUTPUT->notification(get_string('gradingnotallowed', 'quiz_grading')); return true; } $gradeableqs = quiz_report_load_questions($quiz); $questionsinuse = implode(',', array_keys($gradeableqs)); foreach ($gradeableqs as $qid => $question) { if (!$QTYPES[$question->qtype]->is_question_manual_graded($question, $questionsinuse)) { unset($gradeableqs[$qid]); } } if (empty($gradeableqs)) { echo $OUTPUT->heading(get_string('noessayquestionsfound', 'quiz')); return true; } else { if (count($gradeableqs) == 1) { $questionid = array_shift(array_keys($gradeableqs)); } } $currentgroup = groups_get_activity_group($this->cm, true); $this->users = get_users_by_capability($this->context, array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'), '', '', '', '', $currentgroup, '', false); if (!empty($questionid)) { if (!isset($gradeableqs[$questionid])) { print_error('invalidquestionid', 'quiz_grading', '', $questionid); } else { $question =& $gradeableqs[$questionid]; } // Some of the questions code is optimised to work with several questions // at once so it wants the question to be in an array. The array key // must be the question id. $key = $question->id; $questions[$key] =& $question; // We need to add additional questiontype specific information to // the question objects. if (!get_question_options($questions)) { print_error('cannotloadquestioninfo', 'quiz_grading'); } // This will have extended the question object so that it now holds // all the information about the questions that may be needed later. } add_to_log($course->id, "quiz", "manualgrading", "report.php?mode=grading&q={$quiz->id}", "{$quiz->id}", "{$cm->id}"); if ($data = data_submitted()) { // post data submitted, process it if (confirm_sesskey() && $this->users) { // now go through all of the responses and save them. $allok = true; foreach ($data->manualgrades as $uniqueid => $response) { // get our attempt $uniqueid = clean_param($uniqueid, PARAM_INT); list($usql, $params) = $DB->get_in_or_equal(array_keys($this->users)); if (!($attempt = $DB->get_record_sql("SELECT * FROM {quiz_attempts} " . "WHERE uniqueid = ? AND " . "userid {$usql} AND " . "quiz=?", array_merge(array($uniqueid), $params, array($quiz->id))))) { print_error('invalidattemptid', 'quiz_grading'); } // Load the state for this attempt (The questions array was created earlier) $states = get_question_states($questions, $quiz, $attempt); // The $states array is indexed by question id but because we are dealing // with only one question there is only one entry in this array $state =& $states[$question->id]; // the following will update the state and attempt $error = question_process_comment($question, $state, $attempt, $response['comment'], FORMAT_HTML, $response['grade']); if (is_string($error)) { echo $OUTPUT->notification($error); $allok = false; } else { if ($state->changed) { // If the state has changed save it and update the quiz grade save_question_session($question, $state); quiz_save_best_grade($quiz, $attempt->userid); } } } if ($allok) { echo $OUTPUT->notification(get_string('changessaved', 'quiz'), 'notifysuccess'); } else { echo $OUTPUT->notification(get_string('changessavedwitherrors', 'quiz'), 'notifysuccess'); } } } $this->viewurl = new moodle_url('/mod/quiz/report.php', $viewoptions); /// find out current groups mode if ($groupmode = groups_get_activity_groupmode($this->cm)) { // Groups are being used groups_print_activity_menu($this->cm, $this->viewurl->out(true, array('userid' => 0, 'attemptid' => 0))); } if (empty($this->users)) { if ($currentgroup) { echo $OUTPUT->notification(get_string('nostudentsingroup')); } else { echo $OUTPUT->notification(get_string('nostudentsyet')); } return true; } $qattempts = quiz_get_total_qas_graded_and_ungraded($quiz, array_keys($gradeableqs), array_keys($this->users)); if (empty($qattempts)) { echo $OUTPUT->notification(get_string('noattemptstoshow', 'quiz')); return true; } $qmenu = array(); foreach ($gradeableqs as $qid => $questionformenu) { $a = new stdClass(); $a->number = $gradeableqs[$qid]->number; $a->name = $gradeableqs[$qid]->name; $a->gradedattempts = $qattempts[$qid]->gradedattempts; $a->totalattempts = $qattempts[$qid]->totalattempts; $a->openspan = ''; $a->closespan = ''; $qmenu[$qid] = get_string('questiontitle', 'quiz_grading', $a); } if (count($gradeableqs) != 1) { $qurl = fullclone($this->viewurl); $qurl->remove_params('questionid', 'attemptid', 'gradeall', 'gradeungraded', 'gradenextungraded'); $menu = $OUTPUT->single_select($qurl, 'questionid', $qmenu, $questionid, array('' => 'choosedots'), 'questionid'); echo '<div class="mdl-align">' . $menu . '</div>'; } if (!$questionid) { return true; } $a = new stdClass(); $a->number = $question->number; $a->name = $question->name; $a->gradedattempts = $qattempts[$question->id]->gradedattempts; $a->totalattempts = $qattempts[$question->id]->totalattempts; $a->openspan = '<span class="highlightgraded">'; $a->closespan = '</span>'; echo $OUTPUT->heading(get_string('questiontitle', 'quiz_grading', $a)); // our 2 different views // the first view allows a user to select a question and // displays the users who have answered the essay question // and all of their attempts at answering the question // the second prints selected attempt answer(s) with a comment // and grade form underneath them $ungraded = $qattempts[$questionid]->totalattempts - $qattempts[$questionid]->gradedattempts; if ($gradenextungraded || $gradeungraded || $gradeall || $userid || $attemptid) { $this->print_questions_and_form($quiz, $question, $userid, $attemptid, $gradeungraded, $gradenextungraded, $ungraded); } else { $this->view_question($quiz, $question, $qattempts[$questionid]->totalattempts, $ungraded); } return true; }