Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/mod/quiz/report/overview/report.php
Go to the documentation of this file.
00001 <?php
00002 // This file is part of Moodle - http://moodle.org/
00003 //
00004 // Moodle is free software: you can redistribute it and/or modify
00005 // it under the terms of the GNU General Public License as published by
00006 // the Free Software Foundation, either version 3 of the License, or
00007 // (at your option) any later version.
00008 //
00009 // Moodle is distributed in the hope that it will be useful,
00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of
00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012 // GNU General Public License for more details.
00013 //
00014 // You should have received a copy of the GNU General Public License
00015 // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
00016 
00027 defined('MOODLE_INTERNAL') || die();
00028 
00029 require_once($CFG->dirroot.'/mod/quiz/report/attemptsreport.php');
00030 require_once($CFG->dirroot.'/mod/quiz/report/overview/overviewsettings_form.php');
00031 require_once($CFG->dirroot.'/mod/quiz/report/overview/overview_table.php');
00032 
00033 
00040 class quiz_overview_report extends quiz_attempt_report {
00041 
00042     public function display($quiz, $cm, $course) {
00043         global $CFG, $COURSE, $DB, $OUTPUT;
00044 
00045         $this->context = get_context_instance(CONTEXT_MODULE, $cm->id);
00046 
00047         $download = optional_param('download', '', PARAM_ALPHA);
00048 
00049         list($currentgroup, $students, $groupstudents, $allowed) =
00050                 $this->load_relevant_students($cm, $course);
00051 
00052         $pageoptions = array();
00053         $pageoptions['id'] = $cm->id;
00054         $pageoptions['mode'] = 'overview';
00055 
00056         $reporturl = new moodle_url('/mod/quiz/report.php', $pageoptions);
00057         $qmsubselect = quiz_report_qm_filter_select($quiz);
00058 
00059         $mform = new mod_quiz_report_overview_settings($reporturl,
00060                 array('qmsubselect' => $qmsubselect, 'quiz' => $quiz,
00061                 'currentgroup' => $currentgroup, 'context' => $this->context));
00062 
00063         if ($fromform = $mform->get_data()) {
00064             $regradeall = false;
00065             $regradealldry = false;
00066             $regradealldrydo = false;
00067             $attemptsmode = $fromform->attemptsmode;
00068             if ($qmsubselect) {
00069                 $qmfilter = $fromform->qmfilter;
00070             } else {
00071                 $qmfilter = 0;
00072             }
00073             $regradefilter = !empty($fromform->regradefilter);
00074             set_user_preference('quiz_report_overview_detailedmarks', $fromform->detailedmarks);
00075             set_user_preference('quiz_report_pagesize', $fromform->pagesize);
00076             $detailedmarks = $fromform->detailedmarks;
00077             $pagesize = $fromform->pagesize;
00078 
00079         } else {
00080             $regradeall  = optional_param('regradeall', 0, PARAM_BOOL);
00081             $regradealldry  = optional_param('regradealldry', 0, PARAM_BOOL);
00082             $regradealldrydo  = optional_param('regradealldrydo', 0, PARAM_BOOL);
00083             $attemptsmode = optional_param('attemptsmode', null, PARAM_INT);
00084             if ($qmsubselect) {
00085                 $qmfilter = optional_param('qmfilter', 0, PARAM_INT);
00086             } else {
00087                 $qmfilter = 0;
00088             }
00089             $regradefilter = optional_param('regradefilter', 0, PARAM_INT);
00090             $detailedmarks = get_user_preferences('quiz_report_overview_detailedmarks', 1);
00091             $pagesize = get_user_preferences('quiz_report_pagesize', 0);
00092         }
00093 
00094         $this->validate_common_options($attemptsmode, $pagesize, $course, $currentgroup);
00095         $displayoptions = array();
00096         $displayoptions['attemptsmode'] = $attemptsmode;
00097         $displayoptions['qmfilter'] = $qmfilter;
00098         $displayoptions['regradefilter'] = $regradefilter;
00099 
00100         $mform->set_data($displayoptions +
00101                 array('detailedmarks' => $detailedmarks, 'pagesize' => $pagesize));
00102 
00103         if (!$this->should_show_grades($quiz)) {
00104             $detailedmarks = 0;
00105         }
00106 
00107         // We only want to show the checkbox to delete attempts
00108         // if the user has permissions and if the report mode is showing attempts.
00109         $includecheckboxes = has_any_capability(
00110                 array('mod/quiz:regrade', 'mod/quiz:deleteattempts'), $this->context)
00111                 && ($attemptsmode != QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH_NO);
00112 
00113         if ($attemptsmode == QUIZ_REPORT_ATTEMPTS_ALL) {
00114             // This option is only available to users who can access all groups in
00115             // groups mode, so setting allowed to empty (which means all quiz attempts
00116             // are accessible, is not a security porblem.
00117             $allowed = array();
00118         }
00119 
00120         $coursecontext = get_context_instance(CONTEXT_COURSE, $course->id);
00121         $courseshortname = format_string($course->shortname, true,
00122                 array('context' => $coursecontext));
00123 
00124         $displaycoursecontext = get_context_instance(CONTEXT_COURSE, $COURSE->id);
00125         $displaycourseshortname = format_string($COURSE->shortname, true,
00126                 array('context' => $displaycoursecontext));
00127 
00128         // Load the required questions.
00129         $questions = quiz_report_get_significant_questions($quiz);
00130 
00131         $table = new quiz_report_overview_table($quiz, $this->context, $qmsubselect,
00132                 $qmfilter, $attemptsmode, $groupstudents, $students, $detailedmarks,
00133                 $questions, $includecheckboxes, $reporturl, $displayoptions);
00134         $filename = quiz_report_download_filename(get_string('overviewfilename', 'quiz_overview'),
00135                 $courseshortname, $quiz->name);
00136         $table->is_downloading($download, $filename,
00137                 $displaycourseshortname . ' ' . format_string($quiz->name, true));
00138         if ($table->is_downloading()) {
00139             raise_memory_limit(MEMORY_EXTRA);
00140         }
00141 
00142         // Process actions.
00143         if (empty($currentgroup) || $groupstudents) {
00144             if (optional_param('delete', 0, PARAM_BOOL) && confirm_sesskey()) {
00145                 if ($attemptids = optional_param_array('attemptid', array(), PARAM_INT)) {
00146                     require_capability('mod/quiz:deleteattempts', $this->context);
00147                     $this->delete_selected_attempts($quiz, $cm, $attemptids, $allowed);
00148                     redirect($reporturl->out(false, $displayoptions));
00149                 }
00150 
00151             } else if (optional_param('regrade', 0, PARAM_BOOL) && confirm_sesskey()) {
00152                 if ($attemptids = optional_param_array('attemptid', array(), PARAM_INT)) {
00153                     require_capability('mod/quiz:regrade', $this->context);
00154                     $this->regrade_attempts($quiz, false, $groupstudents, $attemptids);
00155                     redirect($reporturl->out(false, $displayoptions));
00156                 }
00157             }
00158         }
00159 
00160         if ($regradeall && confirm_sesskey()) {
00161             require_capability('mod/quiz:regrade', $this->context);
00162             $this->regrade_attempts($quiz, false, $groupstudents);
00163             redirect($reporturl->out(false, $displayoptions), '', 5);
00164 
00165         } else if ($regradealldry && confirm_sesskey()) {
00166             require_capability('mod/quiz:regrade', $this->context);
00167             $this->regrade_attempts($quiz, true, $groupstudents);
00168             redirect($reporturl->out(false, $displayoptions), '', 5);
00169 
00170         } else if ($regradealldrydo && confirm_sesskey()) {
00171             require_capability('mod/quiz:regrade', $this->context);
00172             $this->regrade_attempts_needing_it($quiz, $groupstudents);
00173             redirect($reporturl->out(false, $displayoptions), '', 5);
00174         }
00175 
00176         // Start output.
00177         if (!$table->is_downloading()) {
00178             // Only print headers if not asked to download data
00179             $this->print_header_and_tabs($cm, $course, $quiz, 'overview');
00180         }
00181 
00182         if ($groupmode = groups_get_activity_groupmode($cm)) {   // Groups are being used
00183             if (!$table->is_downloading()) {
00184                 groups_print_activity_menu($cm, $reporturl->out(true, $displayoptions));
00185             }
00186         }
00187 
00188         // Print information on the number of existing attempts
00189         if (!$table->is_downloading()) { //do not print notices when downloading
00190             if ($strattemptnum = quiz_num_attempt_summary($quiz, $cm, true, $currentgroup)) {
00191                 echo '<div class="quizattemptcounts">' . $strattemptnum . '</div>';
00192             }
00193         }
00194 
00195         $hasquestions = quiz_questions_in_quiz($quiz->questions);
00196         if (!$table->is_downloading()) {
00197             if (!$hasquestions) {
00198                 echo quiz_no_questions_message($quiz, $cm, $this->context);
00199             } else if (!$students) {
00200                 echo $OUTPUT->notification(get_string('nostudentsyet'));
00201             } else if ($currentgroup && !$groupstudents) {
00202                 echo $OUTPUT->notification(get_string('nostudentsingroup'));
00203             }
00204 
00205             // Print display options
00206             $mform->display();
00207         }
00208 
00209         $hasstudents = $students && (!$currentgroup || $groupstudents);
00210         if ($hasquestions && ($hasstudents || ($attemptsmode == QUIZ_REPORT_ATTEMPTS_ALL))) {
00211             // Construct the SQL
00212             $fields = $DB->sql_concat('u.id', "'#'", 'COALESCE(quiza.attempt, 0)') .
00213                     ' AS uniqueid, ';
00214             if ($qmsubselect) {
00215                 $fields .=
00216                     "(CASE " .
00217                     "   WHEN $qmsubselect THEN 1" .
00218                     "   ELSE 0 " .
00219                     "END) AS gradedattempt, ";
00220             }
00221 
00222             list($fields, $from, $where, $params) = $table->base_sql($allowed);
00223 
00224             $table->set_count_sql("SELECT COUNT(1) FROM $from WHERE $where", $params);
00225 
00226             // Test to see if there are any regraded attempts to be listed.
00227             $fields .= ", COALESCE((
00228                                 SELECT MAX(qqr.regraded)
00229                                   FROM {quiz_overview_regrades} qqr
00230                                  WHERE qqr.questionusageid = quiza.uniqueid
00231                           ), -1) AS regraded";
00232             if ($regradefilter) {
00233                 $where .= " AND COALESCE((
00234                                     SELECT MAX(qqr.regraded)
00235                                       FROM {quiz_overview_regrades} qqr
00236                                      WHERE qqr.questionusageid = quiza.uniqueid
00237                                 ), -1) <> -1";
00238             }
00239             $table->set_sql($fields, $from, $where, $params);
00240 
00241             if (!$table->is_downloading()) {
00242                 // Regrade buttons
00243                 if (has_capability('mod/quiz:regrade', $this->context)) {
00244                     $regradesneeded = $this->count_question_attempts_needing_regrade(
00245                             $quiz, $groupstudents);
00246                     if ($currentgroup) {
00247                         $a= new stdClass();
00248                         $a->groupname = groups_get_group_name($currentgroup);
00249                         $a->coursestudents = get_string('participants');
00250                         $a->countregradeneeded = $regradesneeded;
00251                         $regradealldrydolabel =
00252                                 get_string('regradealldrydogroup', 'quiz_overview', $a);
00253                         $regradealldrylabel =
00254                                 get_string('regradealldrygroup', 'quiz_overview', $a);
00255                         $regradealllabel =
00256                                 get_string('regradeallgroup', 'quiz_overview', $a);
00257                     } else {
00258                         $regradealldrydolabel =
00259                                 get_string('regradealldrydo', 'quiz_overview', $regradesneeded);
00260                         $regradealldrylabel =
00261                                 get_string('regradealldry', 'quiz_overview');
00262                         $regradealllabel =
00263                                 get_string('regradeall', 'quiz_overview');
00264                     }
00265                     $displayurl = new moodle_url($reporturl,
00266                             $displayoptions + array('sesskey' => sesskey()));
00267                     echo '<div class="mdl-align">';
00268                     echo '<form action="'.$displayurl->out_omit_querystring().'">';
00269                     echo '<div>';
00270                     echo html_writer::input_hidden_params($displayurl);
00271                     echo '<input type="submit" name="regradeall" value="'.$regradealllabel.'"/>';
00272                     echo '<input type="submit" name="regradealldry" value="' .
00273                             $regradealldrylabel . '"/>';
00274                     if ($regradesneeded) {
00275                         echo '<input type="submit" name="regradealldrydo" value="' .
00276                                 $regradealldrydolabel . '"/>';
00277                     }
00278                     echo '</div>';
00279                     echo '</form>';
00280                     echo '</div>';
00281                 }
00282                 // Print information on the grading method
00283                 if ($strattempthighlight = quiz_report_highlighting_grading_method(
00284                         $quiz, $qmsubselect, $qmfilter)) {
00285                     echo '<div class="quizattemptcounts">' . $strattempthighlight . '</div>';
00286                 }
00287             }
00288 
00289             // Define table columns
00290             $columns = array();
00291             $headers = array();
00292 
00293             if (!$table->is_downloading() && $includecheckboxes) {
00294                 $columns[] = 'checkbox';
00295                 $headers[] = null;
00296             }
00297 
00298             $this->add_user_columns($table, $columns, $headers);
00299 
00300             $this->add_time_columns($columns, $headers);
00301 
00302             if ($detailedmarks) {
00303                 foreach ($questions as $slot => $question) {
00304                     // Ignore questions of zero length
00305                     $columns[] = 'qsgrade' . $slot;
00306                     $header = get_string('qbrief', 'quiz', $question->number);
00307                     if (!$table->is_downloading()) {
00308                         $header .= '<br />';
00309                     } else {
00310                         $header .= ' ';
00311                     }
00312                     $header .= '/' . quiz_rescale_grade($question->maxmark, $quiz, 'question');
00313                     $headers[] = $header;
00314                 }
00315             }
00316 
00317             if (!$table->is_downloading() && has_capability('mod/quiz:regrade', $this->context) &&
00318                     $this->has_regraded_questions($from, $where, $params)) {
00319                 $columns[] = 'regraded';
00320                 $headers[] = get_string('regrade', 'quiz_overview');
00321             }
00322 
00323             $this->add_grade_columns($quiz, $columns, $headers, false);
00324 
00325             $this->set_up_table_columns(
00326                     $table, $columns, $headers, $reporturl, $displayoptions, false);
00327             $table->set_attribute('class', 'generaltable generalbox grades');
00328 
00329             $table->out($pagesize, true);
00330         }
00331 
00332         if (!$table->is_downloading() && $this->should_show_grades($quiz)) {
00333             if ($currentgroup && $groupstudents) {
00334                 list($usql, $params) = $DB->get_in_or_equal($groupstudents);
00335                 $params[] = $quiz->id;
00336                 if ($DB->record_exists_select('quiz_grades', "userid $usql AND quiz = ?",
00337                         $params)) {
00338                      $imageurl = new moodle_url('/mod/quiz/report/overview/overviewgraph.php',
00339                             array('id' => $quiz->id, 'groupid' => $currentgroup));
00340                      $graphname = get_string('overviewreportgraphgroup', 'quiz_overview',
00341                             groups_get_group_name($currentgroup));
00342                      echo $OUTPUT->heading($graphname);
00343                      echo html_writer::tag('div', html_writer::empty_tag('img',
00344                             array('src' => $imageurl, 'alt' => $graphname)),
00345                             array('class' => 'graph'));
00346                 }
00347             }
00348 
00349             if ($DB->record_exists('quiz_grades', array('quiz'=> $quiz->id))) {
00350                  $graphname = get_string('overviewreportgraph', 'quiz_overview');
00351                  $imageurl = new moodle_url('/mod/quiz/report/overview/overviewgraph.php',
00352                         array('id' => $quiz->id));
00353                  echo $OUTPUT->heading($graphname);
00354                  echo html_writer::tag('div', html_writer::empty_tag('img',
00355                         array('src' => $imageurl, 'alt' => $graphname)),
00356                         array('class' => 'graph'));
00357             }
00358         }
00359         return true;
00360     }
00361 
00375     protected function regrade_attempt($attempt, $dryrun = false, $slots = null) {
00376         global $DB;
00377 
00378         $transaction = $DB->start_delegated_transaction();
00379 
00380         $quba = question_engine::load_questions_usage_by_activity($attempt->uniqueid);
00381 
00382         if (is_null($slots)) {
00383             $slots = $quba->get_slots();
00384         }
00385 
00386         $finished = $attempt->timefinish > 0;
00387         foreach ($slots as $slot) {
00388             $qqr = new stdClass();
00389             $qqr->oldfraction = $quba->get_question_fraction($slot);
00390 
00391             $quba->regrade_question($slot, $finished);
00392 
00393             $qqr->newfraction = $quba->get_question_fraction($slot);
00394 
00395             if (abs($qqr->oldfraction - $qqr->newfraction) > 1e-7) {
00396                 $qqr->questionusageid = $quba->get_id();
00397                 $qqr->slot = $slot;
00398                 $qqr->regraded = empty($dryrun);
00399                 $qqr->timemodified = time();
00400                 $DB->insert_record('quiz_overview_regrades', $qqr, false);
00401             }
00402         }
00403 
00404         if (!$dryrun) {
00405             question_engine::save_questions_usage_by_activity($quba);
00406         }
00407 
00408         $transaction->allow_commit();
00409     }
00410 
00421     protected function regrade_attempts($quiz, $dryrun = false,
00422             $groupstudents = array(), $attemptids = array()) {
00423         global $DB;
00424 
00425         $where = "quiz = ? AND preview = 0";
00426         $params = array($quiz->id);
00427 
00428         if ($groupstudents) {
00429             list($usql, $uparams) = $DB->get_in_or_equal($groupstudents);
00430             $where .= " AND userid $usql";
00431             $params = array_merge($params, $uparams);
00432         }
00433 
00434         if ($attemptids) {
00435             list($asql, $aparams) = $DB->get_in_or_equal($attemptids);
00436             $where .= " AND id $asql";
00437             $params = array_merge($params, $aparams);
00438         }
00439 
00440         $attempts = $DB->get_records_select('quiz_attempts', $where, $params);
00441         if (!$attempts) {
00442             return;
00443         }
00444 
00445         $this->clear_regrade_table($quiz, $groupstudents);
00446 
00447         foreach ($attempts as $attempt) {
00448             set_time_limit(30);
00449             $this->regrade_attempt($attempt, $dryrun);
00450         }
00451 
00452         if (!$dryrun) {
00453             $this->update_overall_grades($quiz);
00454         }
00455     }
00456 
00464     protected function regrade_attempts_needing_it($quiz, $groupstudents) {
00465         global $DB;
00466 
00467         $where = "quiza.quiz = ? AND quiza.preview = 0 AND qqr.regraded = 0";
00468         $params = array($quiz->id);
00469 
00470         // Fetch all attempts that need regrading
00471         if ($groupstudents) {
00472             list($usql, $uparams) = $DB->get_in_or_equal($groupstudents);
00473             $where .= " AND quiza.userid $usql";
00474             $params += $uparams;
00475         }
00476 
00477         $toregrade = $DB->get_records_sql("
00478                 SELECT quiza.uniqueid, qqr.slot
00479                 FROM {quiz_attempts} quiza
00480                 JOIN {quiz_overview_regrades} qqr ON qqr.questionusageid = quiza.uniqueid
00481                 WHERE $where", $params);
00482 
00483         if (!$toregrade) {
00484             return;
00485         }
00486 
00487         $attemptquestions = array();
00488         foreach ($toregrade as $row) {
00489             $attemptquestions[$row->uniqueid][] = $row->slot;
00490         }
00491         $attempts = $DB->get_records_list('quiz_attempts', 'uniqueid',
00492                 array_keys($attemptquestions));
00493 
00494         $this->clear_regrade_table($quiz, $groupstudents);
00495 
00496         foreach ($attempts as $attempt) {
00497             set_time_limit(30);
00498             $this->regrade_attempt($attempt, false, $attemptquestions[$attempt->uniqueid]);
00499         }
00500 
00501         $this->update_overall_grades($quiz);
00502     }
00503 
00510     protected function count_question_attempts_needing_regrade($quiz, $groupstudents) {
00511         global $DB;
00512 
00513         $usertest = '';
00514         $params = array();
00515         if ($groupstudents) {
00516             list($usql, $params) = $DB->get_in_or_equal($groupstudents);
00517             $usertest = "quiza.userid $usql AND ";
00518         }
00519 
00520         $params[] = $quiz->id;
00521         $sql = "SELECT COUNT(DISTINCT quiza.id)
00522                 FROM {quiz_attempts} quiza
00523                 JOIN {quiz_overview_regrades} qqr ON quiza.uniqueid = qqr.questionusageid
00524                 WHERE
00525                     $usertest
00526                     quiza.quiz = ? AND
00527                     quiza.preview = 0 AND
00528                     qqr.regraded = 0";
00529         return $DB->count_records_sql($sql, $params);
00530     }
00531 
00539     protected function has_regraded_questions($from, $where, $params) {
00540         global $DB;
00541         $qubaids = new qubaid_join($from, 'uniqueid', $where, $params);
00542         return $DB->record_exists_select('quiz_overview_regrades',
00543                 'questionusageid ' . $qubaids->usage_id_in(),
00544                 $qubaids->usage_id_in_params());
00545     }
00546 
00553     protected function clear_regrade_table($quiz, $groupstudents) {
00554         global $DB;
00555 
00556         // Fetch all attempts that need regrading
00557         $where = '';
00558         $params = array();
00559         if ($groupstudents) {
00560             list($usql, $params) = $DB->get_in_or_equal($groupstudents);
00561             $where = "userid $usql AND ";
00562         }
00563 
00564         $params[] = $quiz->id;
00565         $DB->delete_records_select('quiz_overview_regrades',
00566                 "questionusageid IN (
00567                     SELECT uniqueid
00568                     FROM {quiz_attempts}
00569                     WHERE $where quiz = ?
00570                 )", $params);
00571     }
00572 
00580     protected function update_overall_grades($quiz) {
00581         quiz_update_all_attempt_sumgrades($quiz);
00582         quiz_update_all_final_grades($quiz);
00583         quiz_update_grades($quiz);
00584     }
00585 }
 All Data Structures Namespaces Files Functions Variables Enumerations