From 720be6f2c3bdf5b075336e42643dbd6f4da5290e Mon Sep 17 00:00:00 2001 From: jamiesensei Date: Mon, 26 May 2008 11:39:51 +0000 Subject: [PATCH] MDL-5241 "When manually grading, identity questions by their number within the quiz as well as by question name" Also added a drop down box to select question to mark instead of the viewquestions table. The drop down box is available at the top of every page, but only if there is more than one manually gradeable question in the quiz. If there is only one question it is automatically selected for marking. Also added a new constant QUESTION_EVENTS_GRADED during a general refactoring of UI code in grading report and some improvements to efficiency of SQL in report. Merged from Moodle 1.9 branch --- lang/en_utf8/quiz_grading.php | 3 +- lib/questionlib.php | 9 +- mod/quiz/report/grading/report.php | 179 ++++++++++++----------------- mod/quiz/report/reportlib.php | 24 +++- question/type/questiontype.php | 2 +- 5 files changed, 104 insertions(+), 113 deletions(-) diff --git a/lang/en_utf8/quiz_grading.php b/lang/en_utf8/quiz_grading.php index 672487e23a..368570e52e 100644 --- a/lang/en_utf8/quiz_grading.php +++ b/lang/en_utf8/quiz_grading.php @@ -4,6 +4,7 @@ $string['essayonly'] = 'The following questions need to be graded manually'; $string['grading'] = 'Manual grading'; +$string['gradeall'] = 'Grade all $a attempts'; $string['gradingnotallowed'] = 'You do not have permission to manually grade responses in this quiz'; - +$string['questiontitle'] = 'Question $a->number : \"$a->name\" ($a->gradedattempts / $a->totalattempts attempts graded).'; ?> diff --git a/lib/questionlib.php b/lib/questionlib.php index b14bd2aa82..008d28e9f3 100644 --- a/lib/questionlib.php +++ b/lib/questionlib.php @@ -33,6 +33,10 @@ define('QUESTION_EVENTCLOSEANDGRADE', '6'); // Moodle has graded the responses. define('QUESTION_EVENTSUBMIT', '7'); // The student response has been submitted but it has not yet been marked define('QUESTION_EVENTCLOSE', '8'); // The response has been submitted and the session has been closed, either because the student requested it or because Moodle did it (e.g. because of a timelimit). The responses have not been graded. define('QUESTION_EVENTMANUALGRADE', '9'); // Grade was entered by teacher + +define('QUESTION_EVENTS_GRADED', QUESTION_EVENTGRADE.','. + QUESTION_EVENTCLOSEANDGRADE.','. + QUESTION_EVENTMANUALGRADE); /**#@-*/ /**#@+ @@ -1059,9 +1063,8 @@ function save_question_session(&$question, &$state) { * @param object $state */ function question_state_is_graded($state) { - return ($state->event == QUESTION_EVENTGRADE - or $state->event == QUESTION_EVENTCLOSEANDGRADE - or $state->event == QUESTION_EVENTMANUALGRADE); + $gradedevents = explode(',', QUESTION_EVENTS_GRADED); + return (in_array($state->event, $gradedevents)); } /** diff --git a/mod/quiz/report/grading/report.php b/mod/quiz/report/grading/report.php index 5be4860f32..0dd6924f73 100644 --- a/mod/quiz/report/grading/report.php +++ b/mod/quiz/report/grading/report.php @@ -9,7 +9,7 @@ // Flow of the file: // Get variables, run essential queries // Check for post data submitted. If exists, then process data (the data is the grades and comments for essay questions) -// Check for userid, attemptid and for questionid. If found, print out the appropriate essay question attempts +// Check for userid, attemptid, or gradeall and for questionid. If found, print out the appropriate essay question attempts // Switch: // first case: print out all essay questions in quiz and the number of ungraded attempts // second case: print out all users and their attempts for a specific essay question @@ -28,28 +28,27 @@ class quiz_report extends quiz_default_report { * Displays the report. */ function display($quiz, $cm, $course) { - global $CFG; + global $CFG, $QTYPES; $viewoptions = array('mode'=>'grading', 'q'=>$quiz->id); - $action = optional_param('action', 'viewquestions', PARAM_ALPHA); - if ($action !== 'viewquestions'){ - $viewoptions += array('action'=> $action); - } - $questionid = optional_param('questionid', 0, PARAM_INT); - if ($questionid){ + + if ($questionid = optional_param('questionid', 0, PARAM_INT)){ $viewoptions += array('questionid'=>$questionid); } // grade question specific parameters - + $gradeall = optional_param('gradeall', 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 ($action !== 'viewquestions'){ - $viewoptions += array('action'=> $action); + if ($attemptid || $userid){ + $gradeall = 0; + } + if ($gradeall){ + $viewoptions += array('gradeall'=> $gradeall); } @@ -64,9 +63,25 @@ class quiz_report extends quiz_default_report { return true; } + $gradeableqs = quiz_report_load_questions($quiz); + foreach ($gradeableqs as $qid => $questionformenu){ + if (!$QTYPES[$questionformenu->qtype]->is_manual_graded()){ + unset($gradeableqs[$qid]); + } + } + + if (empty($gradeableqs)) { + print_heading(get_string('noessayquestionsfound', 'quiz')); + return false; + } else if (count($gradeableqs)==1){ + $questionid = array_shift(array_keys($gradeableqs)); + } + if (!empty($questionid)) { - if (! $question = get_record('question', 'id', $questionid)) { - print_error("Question with id $questionid not found"); + 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); @@ -79,7 +94,7 @@ class quiz_report extends quiz_default_report { // We need to add additional questiontype specific information to // the question objects. if (!get_question_options($questions)) { - print_error("Unable to load questiontype specific question information"); + 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. @@ -97,7 +112,7 @@ class quiz_report extends quiz_default_report { foreach($data->manualgrades as $uniqueid => $response) { // get our attempt if (! $attempt = get_record('quiz_attempts', 'uniqueid', $uniqueid)) { - print_error('No such attempt ID exists'); + error('No such attempt ID exists'); } // Load the state for this attempt (The questions array was created earlier) @@ -134,7 +149,35 @@ class quiz_report extends quiz_default_report { $this->users = get_users_by_capability($this->context, 'mod/quiz:attempt','','','','',$currentgroup,'',false); $this->userids = implode(',', array_keys($this->users)); - + if(empty($this->users)) { + print_heading(get_string("noattempts", "quiz")); + return true; + } + $gradeablequestionids = implode(',',array_keys($gradeableqs)); + $qattempts = quiz_get_total_qas_graded_and_ungraded($quiz, $gradeablequestionids, $this->userids); + if(empty($qattempts)) { + print_heading(get_string("noattempts", "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; + + $qmenu[$qid]= get_string('questiontitle', 'quiz_grading', $a); + } + if (count($gradeableqs)!=1){ + $qurl = clone($this->viewurl); + $qurl->remove_params('questionid', 'attemptid'); + $menu = popup_form(($qurl->out()).'&questionid=',$qmenu, 'questionid', $questionid, 'choose', '', '', true); + echo '
'.$menu.'
'; + } + if ($questionid){ + print_heading($qmenu[$questionid]); + } // 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 @@ -145,84 +188,14 @@ class quiz_report extends quiz_default_report { // 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, $userid, $attemptid); - break; + if ($gradeall || $userid || $attemptid){ + $this->print_questions_and_form($quiz, $question, $userid, $attemptid); + } else if ($questionid){ + $this->view_question($quiz, $question, $qattempts[$questionid]->totalattempts); } return true; } - /** - * Prints a table containing all manually graded questions - * - * @param object $quiz Quiz object of the currrent quiz - * @return boolean - * @todo Look for the TODO in this code to see what still needs to be done - **/ - function view_questions($quiz) { - global $CFG, $QTYPE_MANUAL; - - if(empty($this->users)) { - print_heading(get_string("noattempts", "quiz")); - return true; - } - - // setup the table - $table = new stdClass; - $table->head = array(get_string("essayquestions", "quiz"), get_string("ungraded", "quiz")); - $table->align = array("left", "left"); - $table->wrap = array("wrap", "wrap"); - $table->width = "20%"; - $table->size = array("*", "*"); - $table->data = array(); - - // get the essay questions - $questionlist = quiz_questions_in_quiz($quiz->questions); - $sql = "SELECT q.*, i.grade AS maxgrade, i.id AS instance". - " FROM {$CFG->prefix}question q,". - " {$CFG->prefix}quiz_question_instances i". - " WHERE i.quiz = '$quiz->id' AND q.id = i.question". - " AND q.id IN ($questionlist)". - " AND q.qtype IN ($QTYPE_MANUAL)". - " ORDER BY q.name"; - if (empty($questionlist) or !$questions = get_records_sql($sql)) { - print_heading(get_string('noessayquestionsfound', 'quiz')); - return false; - } - - notify(get_string('essayonly', 'quiz_grading')); - - // get all the finished attempts by the users - if ($attempts = get_records_select('quiz_attempts', "quiz = $quiz->id and timefinish > 0 AND userid IN ({$this->userids}) AND preview = 0", 'userid, attempt')) { - foreach($questions as $question) { - - $link = "id&action=viewquestion&questionid=$question->id\">". - $question->name.""; - // determine the number of ungraded attempts - $ungraded = 0; - foreach ($attempts as $attempt) { - if (!$this->is_graded($question, $attempt)) { - $ungraded++; - } - } - - $table->data[] = array($link, $ungraded); - } - print_table($table); - } else { - print_heading(get_string('noattempts', 'quiz')); - } - - return true; - } - /** * Prints a table with users and their attempts * @@ -230,8 +203,8 @@ class quiz_report extends quiz_default_report { * @todo Add current grade to the table * Finnish documenting **/ - function view_question($quiz, $question) { - global $CFG, $db; + function view_question($quiz, $question, $totalattempts) { + global $CFG; $usercount = count($this->users); @@ -288,11 +261,14 @@ class quiz_report extends quiz_default_report { } // set up the pagesize - $total = count_records_sql('SELECT COUNT(DISTINCT('.sql_concat('u.id', '\'#\'', 'COALESCE(qa.attempt, 0)').')) '.$from.$where); - $table->pagesize(QUIZ_REPORT_DEFAULT_PAGE_SIZE, $total); + $table->pagesize(QUIZ_REPORT_DEFAULT_PAGE_SIZE, $totalattempts); // get the attempts and process them if ($attempts = get_records_sql($select.$from.$where.$sort,$table->get_page_start(), $table->get_page_size())) { + // grade all and "back" links + $link = "id&questionid=$question->id\">".get_string('gradeall', 'quiz_grading', $totalattempts).''; + $table->add_data_keyed(array('grade'=> $link)); + $table->add_separator(); foreach($attempts as $attempt) { $picture = print_user_picture($attempt->userid, $quiz->course, $attempt->picture, false, true); @@ -308,28 +284,24 @@ class quiz_report extends quiz_default_report { } // link for the attempt - $attemptlink = "id&questionid=$question->id&attemptid=$attempt->attemptid\">". + $attemptlink = "id&questionid=$question->id&attemptid=$attempt->attemptid\">". userdate($attempt->timefinish, get_string('strftimedatetime')).''; // grade all attempts for this user - $gradelink = "id&questionid=$question->id&userid=$attempt->userid\">". + $gradelink = "id&questionid=$question->id&userid=$attempt->userid\">". get_string('grade').''; $table->add_data( array($picture, $userlink, $attemptlink, $gradelink) ); } + $table->add_separator(); + $table->add_data_keyed(array('grade'=> $link)); } - // grade all and "back" links - $links = "
id&questionid=$question->id\">".get_string('gradeall', 'quiz').' | '. - "id&action=viewquestions\">".get_string('backtoquestionlist', 'quiz').'
'. // print everything here - print_heading($question->name); - echo $links; echo '
'; $table->print_html(); echo '
'; - echo $links; } /** @@ -346,7 +318,7 @@ class quiz_report extends quiz_default_report { WHERE sess.newest = state.id AND sess.attemptid = $attempt->uniqueid AND sess.questionid = $question->id")) { - print_error('Could not find question state'); + error('Could not find question state'); } return question_state_is_graded($state); @@ -395,7 +367,6 @@ class quiz_report extends quiz_default_report { ''. ''. ''. - ''. ''; foreach ($attempts as $attempt) { diff --git a/mod/quiz/report/reportlib.php b/mod/quiz/report/reportlib.php index 7bf9991fc2..c63164bc7d 100644 --- a/mod/quiz/report/reportlib.php +++ b/mod/quiz/report/reportlib.php @@ -39,9 +39,6 @@ function quiz_get_newgraded_states($attemptids, $idxattemptq = true){ function quiz_get_average_grade_for_questions($quiz, $userids){ global $CFG; - $gradedevents = QUESTION_EVENTGRADE.','. - QUESTION_EVENTCLOSEANDGRADE.','. - QUESTION_EVENTMANUALGRADE; $qmfilter = quiz_report_qm_filter_subselect($quiz, 'qa.userid'); $questionavgssql = "SELECT qs.question, AVG(qs.grade) FROM " . "{$CFG->prefix}question_sessions qns, " . @@ -51,10 +48,29 @@ function quiz_get_average_grade_for_questions($quiz, $userids){ "qa.quiz = {$quiz->id} AND " . ($qmfilter?$qmfilter.' AND ':''). "qa.userid IN ({$userids}) AND " . - "qs.event IN ($gradedevents) AND ". + "qs.event IN (".QUESTION_EVENTS_GRADED.") AND ". "qns.newgraded = qs.id GROUP BY qs.question"; return get_records_sql_menu($questionavgssql); } + +function quiz_get_total_qas_graded_and_ungraded($quiz, $questionids, $userids){ + global $CFG; + $sql = "SELECT qs.question, COUNT(1) AS totalattempts, " . + "SUM(qs.event IN (".QUESTION_EVENTS_GRADED.")) AS gradedattempts " . + "FROM " . + "{$CFG->prefix}quiz_attempts qa, " . + "{$CFG->prefix}question_sessions qns, " . + "{$CFG->prefix}question_states qs " . + "WHERE " . + "qa.quiz = {$quiz->id} AND " . + "qa.userid IN ({$userids}) AND " . + "qns.attemptid = qa.uniqueid AND " . + "qns.newgraded = qs.id AND " . + "qs.question IN ({$questionids}) " . + "GROUP BY qs.question"; + return get_records_sql($sql); +} + function quiz_format_average_grade_for_questions($avggradebyq, $questions, $quiz, $download){ $row = array(); if (!$avggradebyq){ diff --git a/question/type/questiontype.php b/question/type/questiontype.php index 8753e90a97..f44fea03b8 100644 --- a/question/type/questiontype.php +++ b/question/type/questiontype.php @@ -878,7 +878,7 @@ class default_questiontype { $states = get_records_select('question_states', "attempt = '$state->attempt' AND question = '$question->id' AND event > '0'", 'seq_number ASC'); } else { // show only graded states - $states = get_records_select('question_states', "attempt = '$state->attempt' AND question = '$question->id' AND event IN (".QUESTION_EVENTGRADE.','.QUESTION_EVENTCLOSEANDGRADE.")", 'seq_number ASC'); + $states = get_records_select('question_states', "attempt = '$state->attempt' AND question = '$question->id' AND event IN (".QUESTION_EVENTS_GRADED.")", 'seq_number ASC'); } if (count($states) > 1) { $strreviewquestion = get_string('reviewresponse', 'quiz'); -- 2.39.5