| [ Index ] |
PHP Cross Reference of Moodle 1.9.3 [Build 15-Oct-2008] |
[Summary view] [Print] [Text view]
1 <?php // $Id: report.php,v 1.25.2.13 2008/08/12 06:07:13 tjhunt Exp $ 2 /** 3 * Quiz report to help teachers manually grade quiz questions that need it. 4 * 5 * @package quiz 6 * @subpackage reports 7 */ 8 9 // Flow of the file: 10 // Get variables, run essential queries 11 // Check for post data submitted. If exists, then process data (the data is the grades and comments for essay questions) 12 // Check for userid, attemptid, or gradeall and for questionid. If found, print out the appropriate essay question attempts 13 // Switch: 14 // first case: print out all essay questions in quiz and the number of ungraded attempts 15 // second case: print out all users and their attempts for a specific essay question 16 17 require_once($CFG->dirroot . "/mod/quiz/editlib.php"); 18 require_once($CFG->libdir . '/tablelib.php'); 19 20 /** 21 * Quiz report to help teachers manually grade quiz questions that need it. 22 * 23 * @package quiz 24 * @subpackage reports 25 */ 26 class quiz_report extends quiz_default_report { 27 /** 28 * Displays the report. 29 */ 30 function display($quiz, $cm, $course) { 31 global $CFG, $QTYPES; 32 33 $viewoptions = array('mode'=>'grading', 'q'=>$quiz->id); 34 35 if ($questionid = optional_param('questionid', 0, PARAM_INT)){ 36 $viewoptions += array('questionid'=>$questionid); 37 } 38 39 // grade question specific parameters 40 $gradeungraded = optional_param('gradeungraded', 0, PARAM_INT); 41 42 if ($userid = optional_param('userid', 0, PARAM_INT)){ 43 $viewoptions += array('userid'=>$userid); 44 } 45 if ($attemptid = optional_param('attemptid', 0, PARAM_INT)){ 46 $viewoptions += array('attemptid'=>$attemptid); 47 } 48 if ($gradeall = optional_param('gradeall', 0, PARAM_INT)){ 49 $viewoptions += array('gradeall'=> $gradeall); 50 } 51 if ($gradeungraded = optional_param('gradeungraded', 0, PARAM_INT)){ 52 $viewoptions += array('gradeungraded'=> $gradeungraded); 53 } 54 if ($gradenextungraded = optional_param('gradenextungraded', 0, PARAM_INT)){ 55 $viewoptions += array('gradenextungraded'=> $gradenextungraded); 56 } 57 58 59 $this->cm = $cm; 60 61 $this->print_header_and_tabs($cm, $course, $quiz, $reportmode="grading"); 62 63 // Check permissions 64 $this->context = get_context_instance(CONTEXT_MODULE, $cm->id); 65 if (!has_capability('mod/quiz:grade', $this->context)) { 66 notify(get_string('gradingnotallowed', 'quiz_grading')); 67 return true; 68 } 69 70 $gradeableqs = quiz_report_load_questions($quiz); 71 foreach ($gradeableqs as $qid => $questionformenu){ 72 if (!$QTYPES[$questionformenu->qtype]->is_manual_graded()){ 73 unset($gradeableqs[$qid]); 74 } 75 } 76 77 if (empty($gradeableqs)) { 78 print_heading(get_string('noessayquestionsfound', 'quiz')); 79 return true; 80 } else if (count($gradeableqs)==1){ 81 $questionid = array_shift(array_keys($gradeableqs)); 82 } 83 84 $currentgroup = groups_get_activity_group($this->cm, true); 85 $this->users = get_users_by_capability($this->context, 'mod/quiz:attempt','','','','',$currentgroup,'',false); 86 $this->userids = implode(',', array_keys($this->users)); 87 88 89 if (!empty($questionid)) { 90 if (!isset($gradeableqs[$questionid])){ 91 error("Gradeable question with id $questionid not found"); 92 } else { 93 $question =& $gradeableqs[$questionid]; 94 } 95 $question->maxgrade = get_field('quiz_question_instances', 'grade', 'quiz', $quiz->id, 'question', $question->id); 96 97 // Some of the questions code is optimised to work with several questions 98 // at once so it wants the question to be in an array. The array key 99 // must be the question id. 100 $key = $question->id; 101 $questions[$key] = &$question; 102 103 // We need to add additional questiontype specific information to 104 // the question objects. 105 if (!get_question_options($questions)) { 106 error("Unable to load questiontype specific question information"); 107 } 108 // This will have extended the question object so that it now holds 109 // all the information about the questions that may be needed later. 110 } 111 112 add_to_log($course->id, "quiz", "manualgrading", "report.php?mode=grading&q=$quiz->id", "$quiz->id", "$cm->id"); 113 114 echo '<div id="overDiv" style="position:absolute; visibility:hidden; z-index:1000;"></div>'; // for overlib 115 116 if ($data = data_submitted()) { // post data submitted, process it 117 confirm_sesskey(); 118 119 // now go through all of the responses and save them. 120 $allok = true; 121 foreach($data->manualgrades as $uniqueid => $response) { 122 // get our attempt 123 $uniqueid = clean_param($uniqueid, PARAM_INT); 124 if (!$attempt = get_record_sql("SELECT * FROM {$CFG->prefix}quiz_attempts " . 125 "WHERE uniqueid = $uniqueid AND " . 126 "userid IN ($this->userids) AND " . 127 "quiz=".$quiz->id)){ 128 error('No such attempt ID exists'); 129 } 130 131 // Load the state for this attempt (The questions array was created earlier) 132 $states = get_question_states($questions, $quiz, $attempt); 133 // The $states array is indexed by question id but because we are dealing 134 // with only one question there is only one entry in this array 135 $state = &$states[$question->id]; 136 137 // the following will update the state and attempt 138 $error = question_process_comment($question, $state, $attempt, $response['comment'], $response['grade']); 139 if (is_string($error)) { 140 notify($error); 141 $allok = false; 142 } else if ($state->changed) { 143 // If the state has changed save it and update the quiz grade 144 save_question_session($question, $state); 145 quiz_save_best_grade($quiz, $attempt->userid); 146 } 147 } 148 149 if ($allok) { 150 notify(get_string('changessaved', 'quiz'), 'notifysuccess'); 151 } else { 152 notify(get_string('changessavedwitherrors', 'quiz'), 'notifysuccess'); 153 } 154 } 155 $this->viewurl = new moodle_url($CFG->wwwroot.'/mod/quiz/report.php', $viewoptions); 156 /// find out current groups mode 157 158 if ($groupmode = groups_get_activity_groupmode($this->cm)) { // Groups are being used 159 groups_print_activity_menu($this->cm, $this->viewurl->out(false, array('userid'=>0, 'attemptid'=>0))); 160 } 161 162 echo '<div class="quizattemptcounts">' . quiz_num_attempt_summary($quiz, $cm, true, $currentgroup) . '</div>'; 163 164 if(empty($this->users)) { 165 if ($currentgroup){ 166 notify(get_string('nostudentsingroup')); 167 } else { 168 notify(get_string('nostudentsyet')); 169 } 170 return true; 171 } 172 $gradeablequestionids = implode(',',array_keys($gradeableqs)); 173 $qattempts = quiz_get_total_qas_graded_and_ungraded($quiz, $gradeablequestionids, $this->userids); 174 if(empty($qattempts)) { 175 notify(get_string('noattemptstoshow', 'quiz')); 176 return true; 177 } 178 $qmenu = array(); 179 foreach ($gradeableqs as $qid => $questionformenu){ 180 $a= new object(); 181 $a->number = $gradeableqs[$qid]->number; 182 $a->name = $gradeableqs[$qid]->name; 183 $a->gradedattempts =$qattempts[$qid]->gradedattempts; 184 $a->totalattempts =$qattempts[$qid]->totalattempts; 185 $a->openspan =''; 186 $a->closespan =''; 187 $qmenu[$qid]= get_string('questiontitle', 'quiz_grading', $a); 188 } 189 if (count($gradeableqs)!=1){ 190 $qurl = fullclone($this->viewurl); 191 $qurl->remove_params('questionid', 'attemptid', 'gradeall', 'gradeungraded', 'gradenextungraded'); 192 $menu = popup_form(($qurl->out()).'&questionid=',$qmenu, 'questionid', $questionid, 'choose', '', '', true); 193 echo '<div class="mdl-align">'.$menu.'</div>'; 194 } 195 if (!$questionid){ 196 return true; 197 } 198 $a= new object(); 199 $a->number = $question->number; 200 $a->name = $question->name; 201 $a->gradedattempts =$qattempts[$question->id]->gradedattempts; 202 $a->totalattempts =$qattempts[$question->id]->totalattempts; 203 $a->openspan ='<span class="highlightgraded">'; 204 $a->closespan ='</span>'; 205 print_heading(get_string('questiontitle', 'quiz_grading', $a)); 206 207 // our 3 different views 208 // the first one displays all of the manually graded questions in the quiz 209 // with the number of ungraded attempts for each question 210 211 // the second view displays the users who have answered the essay question 212 // and all of their attempts at answering the question 213 214 // the third prints the question with a comment 215 // and grade form underneath it 216 217 $ungraded = $qattempts[$questionid]->totalattempts- $qattempts[$questionid]->gradedattempts; 218 if ($gradenextungraded ||$gradeungraded || $gradeall || $userid || $attemptid){ 219 $this->print_questions_and_form($quiz, $question, $userid, $attemptid, $gradeungraded, $gradenextungraded, $ungraded); 220 } else { 221 $this->view_question($quiz, $question, $qattempts[$questionid]->totalattempts, $ungraded); 222 } 223 return true; 224 } 225 226 /** 227 * Prints a table with users and their attempts 228 * 229 * @return void 230 * @todo Add current grade to the table 231 * Finnish documenting 232 **/ 233 function view_question($quiz, $question, $totalattempts, $ungraded) { 234 global $CFG; 235 236 237 $usercount = count($this->users); 238 239 // set up table 240 $tablecolumns = array('picture', 'fullname', 'timefinish', 'grade'); 241 $tableheaders = array('', get_string('name'), get_string("completedon", "quiz"), ''); 242 243 $table = new flexible_table('mod-quiz-report-grading'); 244 245 $table->define_columns($tablecolumns); 246 $table->define_headers($tableheaders); 247 $table->define_baseurl($this->viewurl->out()); 248 249 $table->sortable(true); 250 $table->initialbars($usercount>20); // will show initialbars if there are more than 20 users 251 $table->pageable(true); 252 $table->collapsible(true); 253 254 $table->column_suppress('fullname'); 255 $table->column_suppress('picture'); 256 $table->column_suppress('grade'); 257 258 $table->column_class('picture', 'picture'); 259 260 // attributes in the table tag 261 $table->set_attribute('cellspacing', '0'); 262 $table->set_attribute('id', 'attempts'); 263 $table->set_attribute('class', 'generaltable generalbox'); 264 $table->set_attribute('align', 'center'); 265 //$table->set_attribute('width', '50%'); 266 267 // get it ready! 268 $table->setup(); 269 270 list($select, $from, $where) = $this->attempts_sql($quiz->id, true, $question->id); 271 272 if($table->get_sql_where()) { // forgot what this does 273 $where .= 'AND '.$table->get_sql_where(); 274 } 275 276 // sorting of the table 277 if($sort = $table->get_sql_sort()) { 278 $sort = 'ORDER BY '.$sort; // seems like I would need to have u. or qa. infront of the ORDER BY attribues... but seems to work.. 279 } else { 280 // my default sort rule 281 $sort = 'ORDER BY u.firstname, u.lastname, qa.timefinish ASC'; 282 } 283 284 // set up the pagesize 285 $table->pagesize(QUIZ_REPORT_DEFAULT_PAGE_SIZE, $totalattempts); 286 287 // get the attempts and process them 288 if ($attempts = get_records_sql($select.$from.$where.$sort,$table->get_page_start(), $table->get_page_size())) { 289 // grade all link 290 $links = "<strong><a href=\"report.php?mode=grading&gradeall=1&q=$quiz->id&questionid=$question->id\">".get_string('gradeall', 'quiz_grading', $totalattempts).'</a></strong>'; 291 if ($ungraded>0){ 292 $links .="<br /><strong><a href=\"report.php?mode=grading&gradeungraded=1&q=$quiz->id&questionid=$question->id\">".get_string('gradeungraded', 'quiz_grading', $ungraded).'</a></strong>'; 293 if ($ungraded>QUIZ_REPORT_DEFAULT_GRADING_PAGE_SIZE){ 294 $links .="<br /><strong><a href=\"report.php?mode=grading&gradenextungraded=1&q=$quiz->id&questionid=$question->id\">".get_string('gradenextungraded', 'quiz_grading', QUIZ_REPORT_DEFAULT_GRADING_PAGE_SIZE).'</a></strong>'; 295 } 296 } 297 $table->add_data_keyed(array('grade'=> $links)); 298 $table->add_separator(); 299 foreach($attempts as $attempt) { 300 301 $picture = print_user_picture($attempt->userid, $quiz->course, $attempt->picture, false, true); 302 303 // link to student profile 304 $userlink = "<a href=\"$CFG->wwwroot/user/view.php?id=$attempt->userid&course=$quiz->course\">". 305 fullname($attempt, true).'</a>'; 306 307 $gradedclass = question_state_is_graded($attempt)?' class="highlightgraded" ':''; 308 $gradedstring = question_state_is_graded($attempt)?(' '.get_string('graded','quiz_grading')):''; 309 310 // link for the attempt 311 $attemptlink = "<a {$gradedclass}href=\"report.php?mode=grading&q=$quiz->id&questionid=$question->id&attemptid=$attempt->attemptid\">". 312 userdate($attempt->timefinish, get_string('strftimedatetime')). 313 $gradedstring.'</a>'; 314 315 // grade all attempts for this user 316 $gradelink = "<a href=\"report.php?mode=grading&q=$quiz->id&questionid=$question->id&userid=$attempt->userid\">". 317 get_string('grade').'</a>'; 318 319 $table->add_data( array($picture, $userlink, $attemptlink, $gradelink) ); 320 } 321 $table->add_separator(); 322 $table->add_data_keyed(array('grade'=> $links)); 323 // print everything here 324 echo '<div id="tablecontainer">'; 325 $table->print_html(); 326 echo '</div>'; 327 } else { 328 notify(get_string('noattemptstoshow', 'quiz')); 329 } 330 } 331 332 333 /** 334 * Prints questions with comment and grade form underneath each question 335 * 336 * @return void 337 * @todo Finish documenting this function 338 **/ 339 function print_questions_and_form($quiz, $question, $userid, $attemptid, $gradeungraded, $gradenextungraded, $ungraded) { 340 global $CFG; 341 342 // TODO get the context, and put in proper roles an permissions checks. 343 $context = NULL; 344 345 $questions[$question->id] = &$question; 346 $usehtmleditor = can_use_richtext_editor(); 347 348 list($select, $from, $where) = $this->attempts_sql($quiz->id, false, $question->id, $userid, $attemptid, $gradeungraded, $gradenextungraded); 349 350 $sort = 'ORDER BY u.firstname, u.lastname, qa.attempt ASC'; 351 352 if ($gradenextungraded){ 353 $attempts = get_records_sql($select.$from.$where.$sort, 0, QUIZ_REPORT_DEFAULT_GRADING_PAGE_SIZE); 354 } else { 355 $attempts = get_records_sql($select.$from.$where.$sort); 356 } 357 if ($attempts){ 358 $firstattempt = current($attempts); 359 $fullname = fullname($firstattempt); 360 if ($gradeungraded) { // getting all ungraded attempts 361 print_heading(get_string('gradingungraded','quiz_grading', $ungraded), '', 3); 362 } else if ($gradenextungraded) { // getting next ungraded attempts 363 print_heading(get_string('gradingnextungraded','quiz_grading', QUIZ_REPORT_DEFAULT_GRADING_PAGE_SIZE), '', 3); 364 } else if ($userid){ 365 print_heading(get_string('gradinguser','quiz_grading', $fullname), '', 3); 366 } else if ($attemptid){ 367 $a = new object(); 368 $a->fullname = $fullname; 369 $a->attempt = $firstattempt->attempt; 370 print_heading(get_string('gradingattempt','quiz_grading', $a), '', 3); 371 } else { 372 print_heading(get_string('gradingall','quiz_grading', count($attempts)), '', 3); 373 } 374 375 // Display the form with one part for each selected attempt 376 377 echo '<form method="post" action="report.php">'. 378 '<input type="hidden" name="mode" value="grading" />'. 379 '<input type="hidden" name="q" value="'.$quiz->id.'" />'. 380 '<input type="hidden" name="sesskey" value="'.sesskey().'" />'. 381 '<input type="hidden" name="questionid" value="'.$question->id.'" />'; 382 383 foreach ($attempts as $attempt) { 384 385 // Load the state for this attempt (The questions array was created earlier) 386 $states = get_question_states($questions, $quiz, $attempt); 387 // The $states array is indexed by question id but because we are dealing 388 // with only one question there is only one entry in this array 389 $state = &$states[$question->id]; 390 391 $options = quiz_get_reviewoptions($quiz, $attempt, $context); 392 unset($options->questioncommentlink); 393 $copy = $state->manualcomment; 394 $state->manualcomment = ''; 395 396 $options->readonly = 1; 397 398 $gradedclass = question_state_is_graded($state)?' class="highlightgraded" ':''; 399 $gradedstring = question_state_is_graded($state)?(' '.get_string('graded','quiz_grading')):''; 400 $a = new object(); 401 $a->fullname = fullname($attempt, true); 402 $a->attempt = $attempt->attempt; 403 404 // print the user name, attempt count, the question, and some more hidden fields 405 echo '<div class="boxaligncenter" width="80%" style="clear:left;padding:15px;">'; 406 echo "<span$gradedclass>".get_string('gradingattempt','quiz_grading', $a); 407 echo $gradedstring."</span>"; 408 409 print_question($question, $state, '', $quiz, $options); 410 411 $prefix = "manualgrades[$attempt->uniqueid]"; 412 $grade = round($state->last_graded->grade, 3); 413 $state->manualcomment = $copy; 414 415 include($CFG->dirroot . '/question/comment.html'); 416 417 echo '</div>'; 418 } 419 echo '<div class="boxaligncenter"><input type="submit" value="'.get_string('savechanges').'" /></div>'. 420 '</form>'; 421 422 if ($usehtmleditor) { 423 use_html_editor(); 424 } 425 } else { 426 notify(get_string('noattemptstoshow', 'quiz')); 427 } 428 } 429 430 function attempts_sql($quizid, $wantstateevent=false, $questionid=0, $userid=0, $attemptid=0, $gradeungraded=0, $gradenextungraded=0){ 431 global $CFG; 432 // this sql joins the attempts table and the user table 433 $select = 'SELECT qa.id AS attemptid, qa.uniqueid, qa.attempt, qa.timefinish, qa.preview, 434 u.id AS userid, u.firstname, u.lastname, u.picture '; 435 if ($wantstateevent && $questionid){ 436 $select .= ', qs.event '; 437 } 438 $from = 'FROM '.$CFG->prefix.'user u, ' . 439 $CFG->prefix.'quiz_attempts qa '; 440 if (($wantstateevent|| $gradenextungraded || $gradeungraded) && $questionid){ 441 $from .= "LEFT JOIN {$CFG->prefix}question_sessions qns " . 442 "ON (qns.attemptid = qa.uniqueid AND qns.questionid = $questionid) "; 443 $from .= "LEFT JOIN {$CFG->prefix}question_states qs " . 444 "ON (qs.id = qns.newgraded AND qs.question = $questionid) "; 445 } 446 if ($gradenextungraded || $gradeungraded) { // get ungraded attempts 447 $where = 'WHERE u.id IN ('.$this->userids.') AND qs.event NOT IN ('.QUESTION_EVENTS_GRADED.') '; 448 } else if ($userid) { // get all the attempts for a specific user 449 $where = 'WHERE u.id='.$userid.' '; 450 } else if ($attemptid) { // get a specific attempt 451 $where = 'WHERE qa.id='.$attemptid.' '; 452 } else { // get all user attempts 453 $where = 'WHERE u.id IN ('.$this->userids.') '; 454 } 455 456 $where .= ' AND u.id = qa.userid AND qa.quiz = '.$quizid; 457 // ignore previews 458 $where .= ' AND preview = 0 '; 459 460 $where .= ' AND qa.timefinish != 0 '; 461 462 return array($select, $from, $where); 463 } 464 465 } 466 467 ?>
title
Description
Body
title
Description
Body
title
Description
Body
title
Body
| Generated: Wed Jan 14 11:33:29 2009 | Cross-referenced by PHPXref 0.7 |