diff --git a/mod/quiz/report/grading/db/access.php b/mod/quiz/report/grading/db/access.php
index 859962fc94d..385e60dea4f 100644
--- a/mod/quiz/report/grading/db/access.php
+++ b/mod/quiz/report/grading/db/access.php
@@ -18,14 +18,15 @@
/**
* Capability definitions for the quiz manual grading report.
*
- * @package quiz_grading
+ * @package quiz
+ * @subpackage grading
* @copyright 2010 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-$quizreport_grading_capabilities = array(
+$capabilities = array(
// Is the user allowed to see the student's real names while grading?
- 'quizreport/grading:viewstudentnames' => array(
+ 'quiz/grading:viewstudentnames' => array(
'captype' => 'read',
'contextlevel' => CONTEXT_MODULE,
'legacy' => array(
@@ -36,7 +37,7 @@ $quizreport_grading_capabilities = array(
),
// Is the user allowed to see the student's idnumber while grading?
- 'quizreport/grading:viewidnumber' => array(
+ 'quiz/grading:viewidnumber' => array(
'captype' => 'read',
'contextlevel' => CONTEXT_MODULE,
'legacy' => array(
diff --git a/mod/quiz/report/grading/report.php b/mod/quiz/report/grading/report.php
index 26ea18c1735..59c5f4d5365 100644
--- a/mod/quiz/report/grading/report.php
+++ b/mod/quiz/report/grading/report.php
@@ -50,7 +50,7 @@ class quiz_grading_report extends quiz_default_report {
protected $context;
function display($quiz, $cm, $course) {
- global $CFG, $DB, $OUTPUT, $PAGE;
+ global $CFG, $DB, $PAGE;
$this->quiz = $quiz;
$this->cm = $cm;
@@ -86,8 +86,8 @@ class quiz_grading_report extends quiz_default_report {
// Check permissions
$this->context = get_context_instance(CONTEXT_MODULE, $cm->id);
require_capability('mod/quiz:grade', $this->context);
- $shownames = has_capability('quizreport/grading:viewstudentnames', $this->context);
- $showidnumbers = has_capability('quizreport/grading:viewidnumber', $this->context);
+ $shownames = has_capability('quiz/grading:viewstudentnames', $this->context);
+ $showidnumbers = has_capability('quiz/grading:viewidnumber', $this->context);
// Validate order.
if (!in_array($order, array('random', 'date', 'student', 'idnumber'))) {
@@ -134,351 +134,415 @@ class quiz_grading_report extends quiz_default_report {
return true;
}
- protected function get_attempts_query() {
+ protected function get_qubaids_condition() {
global $DB;
- $from = "FROM {$CFG->prefix}quiz_attempts quiza";
- $where = "quiza.quiz = {$this->cm->instance} AND quiza.preview = 0 AND quiza.timefinish <> 0";
+ $where = "quiza.quiz = :mangrquizid AND
+ quiza.preview = 0 AND
+ quiza.timefinish <> 0";
+ $params = array('mangrquizid' => $this->cm->instance);
if ($this->currentgroup) {
- $where .= ' AND quiza.userid IN (' . implode(',', array_keys($this->users)) . ')';
+ list($usql, $uparam) = $DB->get_in_or_equal(array_keys($this->users),
+ SQL_PARAMS_NAMED, 'mangru00000');
+ $where .= ' AND quiza.userid ' . $usql;
+ $params += $uparam;
}
- $sql = new stdClass;
- $sql->from = $from;
- $sql->where = $where;
- $sql->usageidcolumn = 'quiza.uniqueid';
-
- return $sql;
+ return new qubaid_join('{quiz_attempts} quiza', 'quiza.uniqueid', $where, $params);
}
- function temp() { // TODO
- {
- {
- if ($allok) {
- echo $OUTPUT->notification(get_string('changessaved', 'quiz'), 'notifysuccess');
- } else {
- echo $OUTPUT->notification(get_string('changessavedwitherrors', 'quiz'), 'notifysuccess');
+ protected function load_attempts_by_usage_ids($qubaids) {
+ global $DB;
+
+ list($asql, $params) = $DB->get_in_or_equal($qubaids);
+ $params[] = $this->quiz->id;
+
+ $attemptsbyid = $DB->get_records_sql("
+ SELECT quiza.*, u.firstname, u.lastname, u.idnumber
+ FROM {quiz_attempts} quiza
+ JOIN {user} u ON u.id = quiza.userid
+ WHERE quiza.uniqueid $asql AND quiza.timefinish <> 0 AND quiza.quiz = ?",
+ $params);
+
+ $attempts = array();
+ foreach ($attemptsbyid as $attempt) {
+ $attempts[$attempt->uniqueid] = $attempt;
+ }
+ return $attempts;
+ }
+
+ /**
+ * Get the URL of the front page of the report that lists all the questions.
+ * @param $includeauto if not given, use the current setting, otherwise,
+ * force a paricular value of includeauto in the URL.
+ * @return string the URL.
+ */
+ protected function base_url() {
+ return new moodle_url('/mod/quiz/report.php',
+ array('id' => $this->cm->id, 'mode' => 'grading'));
+ }
+
+ /**
+ * Get the URL of the front page of the report that lists all the questions.
+ * @param $includeauto if not given, use the current setting, otherwise,
+ * force a paricular value of includeauto in the URL.
+ * @return string the URL.
+ */
+ protected function list_questions_url($includeauto = null) {
+ $url = $this->base_url();
+
+ $url->params($this->viewoptions);
+
+ if (!is_null($includeauto)) {
+ $url->param('includeauto', $includeauto);
+ }
+
+ return $url;
+ }
+
+ /**
+ * @param integer $slot
+ * @param integer $questionid
+ * @param string $grade
+ * @param mixed $page = true, link to current page. false = omit page.
+ * number = link to specific page.
+ */
+ protected function grade_question_url($slot, $questionid, $grade, $page = true) {
+ $url = $this->base_url();
+ $url->params(array('slot' => $slot, 'qid' => $questionid, 'grade' => $grade));
+ $url->params($this->viewoptions);
+
+ $options = $this->viewoptions;
+ if (!$page) {
+ $url->remove_params('page');
+ } else if (is_integer($page)) {
+ $url->param('page', $page);
+ }
+
+ return $url;
+ }
+
+ protected function format_count_for_table($counts, $type, $gradestring) {
+ $result = $counts->$type;
+ if ($counts->$type > 0) {
+ $result .= ' ' . html_writer::link($this->grade_question_url(
+ $counts->slot, $counts->questionid, $type),
+ get_string($gradestring, 'quiz_grading'),
+ array('class' => 'gradetheselink'));
+ }
+ return $result;
+ }
+
+ protected function display_index($includeauto) {
+ global $OUTPUT;
+
+ if ($groupmode = groups_get_activity_groupmode($this->cm)) {
+ // Groups are being used
+ groups_print_activity_menu($this->cm, $this->list_questions_url());
+ }
+
+ echo $OUTPUT->heading(get_string('questionsthatneedgrading', 'quiz_grading'));
+ if ($includeauto) {
+ $linktext = get_string('hideautomaticallygraded', 'quiz_grading');
+ } else {
+ $linktext = get_string('alsoshowautomaticallygraded', 'quiz_grading');
+ }
+ echo html_writer::tag('p', html_writer::link($this->list_questions_url(!$includeauto),
+ $linktext), array('class' => 'toggleincludeauto'));
+
+ $statecounts = $this->get_question_state_summary(array_keys($this->questions));
+
+ $data = array();
+ foreach ($statecounts as $counts) {
+ if ($counts->all == 0) {
+ continue;
+ }
+ if (!$includeauto && $counts->needsgrading == 0 && $counts->manuallygraded == 0) {
+ continue;
+ }
+
+ $row = array();
+
+ $row[] = $this->questions[$counts->slot]->number;
+
+ $row[] = format_string($counts->name);
+
+ $row[] = $this->format_count_for_table($counts, 'needsgrading', 'grade');
+
+ $row[] = $this->format_count_for_table($counts, 'manuallygraded', 'updategrade');
+
+ if ($includeauto) {
+ $row[] = $this->format_count_for_table($counts, 'autograded', 'updategrade');
+ }
+
+ $row[] = $this->format_count_for_table($counts, 'all', 'gradeall');
+
+ $data[] = $row;
+ }
+
+ if (empty($data)) {
+ echo $OUTPUT->heading(get_string('noquestionsfound', 'quiz_grading'));
+ return;
+ }
+
+ $table = new html_table();
+ $table->class = 'generaltable';
+ $table->id = 'questionstograde';
+
+ $table->head[] = get_string('qno', 'quiz_grading');
+ $table->head[] = get_string('questionname', 'quiz_grading');
+ $table->head[] = get_string('tograde', 'quiz_grading');
+ $table->head[] = get_string('alreadygraded', 'quiz_grading');
+ if ($includeauto) {
+ $table->head[] = get_string('automaticallygraded', 'quiz_grading');
+ }
+ $table->head[] = get_string('total', 'quiz_grading');
+
+ $table->data = $data;
+ echo html_writer::table($table);
+ }
+
+ protected function display_grading_interface($slot, $questionid, $grade,
+ $pagesize, $page, $shownames, $showidnumbers, $order) {
+ global $OUTPUT;
+
+ // Make sure there is something to do.
+ $statecounts = $this->get_question_state_summary(array($slot));
+
+ $counts = null;
+ foreach ($statecounts as $record) {
+ if ($record->questionid == $questionid) {
+ $counts = $record;
+ break;
+ }
+ }
+
+ // If not, redirect back to the list.
+ if (!$counts || $counts->$grade == 0) {
+ redirect($this->list_questions_url(), get_string('alldoneredirecting', 'quiz_grading'));
+ }
+
+ if ($pagesize * $page >= $counts->$grade) {
+ $page = 0;
+ }
+
+ list($qubaids, $count) = $this->get_usage_ids_where_question_in_state(
+ $grade, $slot, $questionid, $order, $page, $pagesize);
+ $attempts = $this->load_attempts_by_usage_ids($qubaids);
+
+ // Prepare the form.
+ $hidden = array(
+ 'id' => $this->cm->id,
+ 'mode' => 'grading',
+ 'slot' => $slot,
+ 'qid' => $questionid,
+ 'page' => $page,
+ );
+ if (array_key_exists('includeauto', $this->viewoptions)) {
+ $hidden['includeauto'] = $this->viewoptions['includeauto'];
+ }
+ $mform = new quiz_grading_settings($hidden, $counts, $shownames, $showidnumbers);
+
+ // Tell the form the current settings.
+ $settings = new stdClass;
+ $settings->grade = $grade;
+ $settings->pagesize = $pagesize;
+ $settings->order = $order;
+ $mform->set_data($settings);
+
+ // Print the heading and form.
+ echo question_engine::initialise_js();
+
+ $a = new stdClass;
+ $a->number = $this->questions[$slot]->number;
+ $a->questionname = format_string($counts->name);
+ echo $OUTPUT->heading(get_string('gradingquestionx', 'quiz_grading', $a));
+ echo html_writer::tag('p', html_writer::link($this->list_questions_url(),
+ get_string('backtothelistofquestions', 'quiz_grading')),
+ array('class' => 'mdl-align'));
+
+ $mform->display();
+
+ // Paging info.
+ $a = new stdClass;
+ $a->from = $page * $pagesize + 1;
+ $a->to = min(($page + 1) * $pagesize, $count);
+ $a->of = $count;
+ echo $OUTPUT->heading(get_string('gradingattemptsxtoyofz', 'quiz_grading', $a), 3);
+
+ if ($count > $pagesize && $order != 'random') {
+ echo $OUTPUT->paging_bar($count, $page, $pagesize,
+ $this->grade_question_url($slot, $questionid, $grade, false));
+ }
+
+ // Display the form with one section for each attempt.
+ $usehtmleditor = can_use_html_editor();
+ $sesskey = sesskey();
+ $qubaidlist = implode(',', $qubaids);
+ echo html_writer::start_tag('form', array('method' => 'post',
+ 'action' => $this->grade_question_url($slot, $questionid, $grade, $page),
+ 'class' => 'mform', 'id' => 'manualgradingform')) .
+ html_writer::start_tag('div') .
+ html_writer::input_hidden_params(new moodle_url('', array(
+ 'qubaids' => $qubaidlist, 'slots' => $slot, 'sesskey' => $sesskey)));
+
+ foreach ($qubaids as $qubaid) {
+ $attempt = $attempts[$qubaid];
+ $quba = question_engine::load_questions_usage_by_activity($qubaid);
+ $displayoptions = quiz_get_review_options($this->quiz, $attempt, $this->context);
+ $displayoptions->hide_all_feedback();
+ $displayoptions->history = question_display_options::HIDDEN;
+ $displayoptions->manualcomment = question_display_options::EDITABLE;
+
+ $heading = $this->get_question_heading($attempt, $shownames, $showidnumbers);
+ if ($heading) {
+ echo $OUTPUT->heading($heading, 4);
+ }
+ echo $quba->render_question($slot, $displayoptions, $this->questions[$slot]->number);
+ }
+
+ echo html_writer::tag('div', html_writer::empty_tag('input', array(
+ 'type' => 'submit', 'value' => get_string('saveandnext', 'quiz_grading'))),
+ array('class' => 'mdl-align')) .
+ html_writer::end_tag('div') . html_writer::end_tag('form');
+ }
+
+ protected function get_question_heading($attempt, $shownames, $showidnumbers) {
+ $a = new stdClass;
+ $a->attempt = $attempt->attempt;
+ $a->fullname = fullname($attempt);
+ $a->idnumber = $attempt->idnumber;
+
+ $showidnumbers &= !empty($attempt->idnumber);
+
+ if ($shownames && $showidnumbers) {
+ return get_string('gradingattemptwithidnumber', 'quiz_grading', $a);
+ } else if ($shownames) {
+ return get_string('gradingattempt', 'quiz_grading', $a);
+ } else if ($showidnumbers) {
+ $a->fullname = $attempt->idnumber;
+ return get_string('gradingattempt', 'quiz_grading', $a);
+ } else {
+ return '';
+ }
+ }
+
+ protected function validate_submitted_marks() {
+
+ $qubaids = optional_param('qubaids', null, PARAM_SEQUENCE);
+ if (!$qubaids) {
+ return false;
+ }
+ $qubaids = clean_param(explode(',', $qubaids), PARAM_INT);
+
+ $slots = optional_param('slots', '', PARAM_SEQUENCE);
+ if (!$slots) {
+ $slots = array();
+ } else {
+ $slots = explode(',', $slots);
+ }
+
+ foreach ($qubaids as $qubaid) {
+ foreach ($slots as $slot) {
+ $prefix = 'q' . $qubaid . ':' . $slot . '_';
+ $mark = optional_param($prefix . '-mark', null, PARAM_NUMBER);
+ $maxmark = optional_param($prefix . '-maxmark', null, PARAM_NUMBER);
+ $minfraction = optional_param($prefix . ':minfraction', null, PARAM_NUMBER);
+ if (!is_null($mark) && ($mark < $minfraction * $maxmark || $mark > $maxmark)) {
+ return false;
}
}
}
- $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 '
'.$menu.'
';
- }
- 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 ='';
- $a->closespan ='';
- 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;
}
- /**
- * Prints a table with users and their attempts
- *
- * @return void
- * @todo Add current grade to the table
- * Finnish documenting
- **/
- function view_question($quiz, $question, $totalattempts, $ungraded) {
- global $CFG, $DB, $OUTPUT;
+ protected function process_submitted_data() {
+ global $DB;
- $usercount = count($this->users);
-
- // set up table
- $tablecolumns = array('picture', 'fullname', 'timefinish', 'grade');
- $tableheaders = array('', get_string('name'), get_string("completedon", "quiz"), '');
-
- $table = new flexible_table('mod-quiz-report-grading');
-
- $table->define_columns($tablecolumns);
- $table->define_headers($tableheaders);
- $table->define_baseurl($this->viewurl->out());
-
- $table->sortable(true);
- $table->initialbars($usercount>20); // will show initialbars if there are more than 20 users
- $table->pageable(true);
- $table->collapsible(true);
-
- $table->column_suppress('fullname');
- $table->column_suppress('picture');
- $table->column_suppress('grade');
-
- $table->column_class('picture', 'picture');
-
- // attributes in the table tag
- $table->set_attribute('cellspacing', '0');
- $table->set_attribute('id', 'attempts');
- $table->set_attribute('class', 'generaltable generalbox');
- $table->set_attribute('align', 'center');
- //$table->set_attribute('width', '50%');
-
- // get it ready!
- $table->setup();
-
- list($select, $from, $where, $params) = $this->attempts_sql($quiz->id, true, $question->id);
-
- list($twhere, $tparams) = $table->get_sql_where();
- if ($twhere) {
- $where .= ' AND '.$twhere; //initial bar
- $params = array_merge($params, $tparams);
+ $qubaids = optional_param('qubaids', null, PARAM_SEQUENCE);
+ if (!$qubaids) {
+ return;
}
- // sorting of the table
- if ($sort = $table->get_sql_sort()) {
- $sort = 'ORDER BY '.$sort; // seems like I would need to have u. or qa. infront of the ORDER BY attribues... but seems to work..
- } else {
- // my default sort rule
- $sort = 'ORDER BY u.firstname, u.lastname, qa.timefinish ASC';
+ $qubaids = clean_param(explode(',', $qubaids), PARAM_INT);
+ $attempts = $this->load_attempts_by_usage_ids($qubaids);
+
+ $transaction = $DB->start_delegated_transaction();
+ foreach ($qubaids as $qubaid) {
+ $attempt = $attempts[$qubaid];
+ $quba = question_engine::load_questions_usage_by_activity($qubaid);
+ $attemptobj = new quiz_attempt($attempt, $this->quiz, $this->cm, $this->course);
+ $attemptobj->process_all_actions(time());
}
-
- // set up the pagesize
- $table->pagesize(QUIZ_REPORT_DEFAULT_PAGE_SIZE, $totalattempts);
-
- // get the attempts and process them
- echo '';
- if ($attempts = $DB->get_records_sql($select.$from.$where.$sort, $params, $table->get_page_start(), $table->get_page_size())) {
- // grade all link
- $links = "
id&questionid=$question->id\">".get_string('gradeall', 'quiz_grading', $totalattempts).'';
- if ($ungraded>0){
- $links .="
id&questionid=$question->id\">".get_string('gradeungraded', 'quiz_grading', $ungraded).'';
- if ($ungraded>QUIZ_REPORT_DEFAULT_GRADING_PAGE_SIZE){
- $links .="
id&questionid=$question->id\">".get_string('gradenextungraded', 'quiz_grading', QUIZ_REPORT_DEFAULT_GRADING_PAGE_SIZE).'';
- }
- }
- $table->add_data_keyed(array('grade'=> $links));
- $table->add_separator();
- foreach($attempts as $attempt) {
-
- $user = clone($attempt);
- $user->id = $user->userid;
- $picture = $OUTPUT->user_picture($user, array('courseid'=>$quiz->course));
-
- // link to student profile
- $userlink = "
wwwroot/user/view.php?id=$attempt->userid&course=$quiz->course\">".
- fullname($attempt, true).'';
-
- $gradedclass = question_state_is_graded($attempt)?' class="highlightgraded" ':'';
- $gradedstring = question_state_is_graded($attempt)?(' '.get_string('graded','quiz_grading')):'';
-
- // link for the attempt
- $attemptlink = "
id&questionid=$question->id&attemptid=$attempt->attemptid\">".
- userdate($attempt->timefinish, get_string('strftimedatetime')).
- $gradedstring.'';
-
- // grade all attempts for this user
- $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'=> $links));
- }
- // print everything here
- $table->print_html();
- echo '
';
+ $transaction->allow_commit();
}
-
/**
- * Prints questions with comment and grade form underneath each question
+ * Load information about the number of attempts at various questions in each
+ * summarystate.
*
- * @return void
- * @todo Finish documenting this function
- **/
- function print_questions_and_form($quiz, $question, $userid, $attemptid, $gradeungraded, $gradenextungraded, $ungraded) {
- global $CFG, $DB, $OUTPUT;
-
- $context = get_context_instance(CONTEXT_MODULE, $this->cm->id);
-
- $questions[$question->id] = &$question;
- $usehtmleditor = can_use_html_editor();
-
- list($select, $from, $where, $params) = $this->attempts_sql($quiz->id, false, $question->id, $userid, $attemptid, $gradeungraded, $gradenextungraded);
-
- $sort = 'ORDER BY u.firstname, u.lastname, qa.attempt ASC';
-
- if ($gradenextungraded){
- $attempts = $DB->get_records_sql($select.$from.$where.$sort, $params, 0, QUIZ_REPORT_DEFAULT_GRADING_PAGE_SIZE);
- } else {
- $attempts = $DB->get_records_sql($select.$from.$where.$sort, $params);
- }
- if ($attempts){
- $firstattempt = current($attempts);
- $fullname = fullname($firstattempt);
- if ($gradeungraded) { // getting all ungraded attempts
- echo $OUTPUT->heading(get_string('gradingungraded','quiz_grading', $ungraded), 3);
- } else if ($gradenextungraded) { // getting next ungraded attempts
- echo $OUTPUT->heading(get_string('gradingnextungraded','quiz_grading', QUIZ_REPORT_DEFAULT_GRADING_PAGE_SIZE), 3);
- } else if ($userid){
- echo $OUTPUT->heading(get_string('gradinguser','quiz_grading', $fullname), 3);
- } else if ($attemptid){
- $a = new stdClass();
- $a->fullname = $fullname;
- $a->attempt = $firstattempt->attempt;
- echo $OUTPUT->heading(get_string('gradingattempt','quiz_grading', $a), 3);
- } else {
- echo $OUTPUT->heading(get_string('gradingall','quiz_grading', count($attempts)), 3);
- }
-
- // Display the form with one part for each selected attempt
-
- echo '';
- } else {
- echo $OUTPUT->notification(get_string('noattemptstoshow', 'quiz'));
- }
+ * The results are returned as an two dimensional array $qubaid => $slot => $dataobject
+ *
+ * @param array $slots A list of slots for the questions you want to konw about.
+ * @return array The array keys are slot,qestionid. The values are objects with
+ * fields $slot, $questionid, $inprogress, $name, $needsgrading, $autograded,
+ * $manuallygraded and $all.
+ */
+ protected function get_question_state_summary($slots) {
+ $dm = new question_engine_data_mapper();
+ return $dm->load_questions_usages_question_state_summary(
+ $this->get_qubaids_condition(), $slots);
}
- function attempts_sql($quizid, $wantstateevent=false, $questionid=0, $userid=0, $attemptid=0, $gradeungraded=0, $gradenextungraded=0){
- global $CFG, $DB;
- // this sql joins the attempts table and the user table
- $select = 'SELECT qa.id AS attemptid, qa.uniqueid, qa.attempt, qa.timefinish, qa.preview,
- u.id AS userid, u.firstname, u.lastname, u.picture, u.imagealt, u.email ';
- if ($wantstateevent && $questionid){
- $select .= ', qs.event ';
+ /**
+ * Get a list of usage ids where the question with slot $slot, and optionally
+ * also with question id $questionid, is in summary state $summarystate. Also
+ * return the total count of such states.
+ *
+ * Only a subset of the ids can be returned by using $orderby, $limitfrom and
+ * $limitnum. A special value 'random' can be passed as $orderby, in which case
+ * $limitfrom is ignored.
+ *
+ * @param integer $slot The slot for the questions you want to konw about.
+ * @param integer $questionid (optional) Only return attempts that were of this specific question.
+ * @param string $summarystate 'all', 'needsgrading', 'autograded' or 'manuallygraded'.
+ * @param string $orderby 'random', 'date', 'student' or 'idnumber'.
+ * @param integer $page implements paging of the results.
+ * Ignored if $orderby = random or $pagesize is null.
+ * @param integer $pagesize implements paging of the results. null = all.
+ */
+ function get_usage_ids_where_question_in_state($summarystate, $slot,
+ $questionid = null, $orderby = 'random', $page = 0, $pagesize = null) {
+ global $CFG;
+ $dm = new question_engine_data_mapper();
+
+ if ($pagesize && $orderby != 'random') {
+ $limitfrom = $page * $pagesize;
+ } else {
+ $limitfrom = 0;
}
- $from = 'FROM {user} u, ' .
- '{quiz_attempts} qa ';
+
+ $qubaids = $this->get_qubaids_condition();
+
$params = array();
-
- $from .= "LEFT JOIN {question_sessions} qns " .
- "ON (qns.attemptid = qa.uniqueid AND qns.questionid = ?) ";
- $params[] = $questionid;
- $from .= "LEFT JOIN {question_states} qs " .
- "ON (qs.id = qns.newest AND qs.question = ?) ";
- $params[] = $questionid;
-
- list($usql, $u_params) = $DB->get_in_or_equal(array_keys($this->users));
- if ($gradenextungraded || $gradeungraded) { // get ungraded attempts
- $where = "WHERE u.id $usql AND qs.event NOT IN (".QUESTION_EVENTS_GRADED.")";
- $params = array_merge($params, $u_params);
- } else if ($userid) { // get all the attempts for a specific user
- $where = 'WHERE u.id=?';
- $params[] = $userid;
- } else if ($attemptid) { // get a specific attempt
- $where = 'WHERE qa.id=? ';
- $params[] = $attemptid;
- } else { // get all user attempts
- $where = "WHERE u.id $usql ";
- $params = array_merge($params, $u_params);
+ if ($orderby == 'date') {
+ list($statetest, $params) = $dm->in_summary_state_test('manuallygraded', false, 'mangrstate');
+ $orderby = "(
+ SELECT MAX(sortqas.timecreated)
+ FROM {question_attempt_steps} sortqas
+ WHERE sortqas.questionattemptid = qa.id
+ AND sortqas.state $statetest
+ )";
+ } else if ($orderby == 'student' || $orderby == 'idnumber') {
+ $qubaids->from .= " JOIN {user} u ON quiza.userid = u.id ";
+ if ($orderby == 'student') {
+ $orderby = sql_fullname('u.firstname', 'u.lastname');
+ }
}
- $where .= ' AND qs.event IN ('.QUESTION_EVENTS_CLOSED_OR_GRADED.')';
-
- $where .= ' AND u.id = qa.userid AND qa.quiz = ?';
- $params[] = $quizid;
- // ignore previews
- $where .= ' AND preview = 0 ';
-
- $where .= ' AND qa.timefinish != 0 ';
-
- return array($select, $from, $where, $params);
+ return $dm->load_questions_usages_where_question_in_state($qubaids, $summarystate,
+ $slot, $questionid, $orderby, $params, $limitfrom, $pagesize);
}
-
}
-
-
diff --git a/mod/quiz/report/grading/version.php b/mod/quiz/report/grading/version.php
index b9a0b85f791..9ff23c41924 100644
--- a/mod/quiz/report/grading/version.php
+++ b/mod/quiz/report/grading/version.php
@@ -23,4 +23,4 @@
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-$plugin->version = 2010070100;
+$plugin->version = 2011021400;
diff --git a/mod/quiz/report/reportlib.php b/mod/quiz/report/reportlib.php
index 746b9dc1b6f..26c1658f51a 100644
--- a/mod/quiz/report/reportlib.php
+++ b/mod/quiz/report/reportlib.php
@@ -87,11 +87,11 @@ function quiz_report_unindex($datum) {
* @param object $quiz the quiz.
* @return array of slot => $question object with fields ->slot, ->id, ->maxmark, ->number, ->length.
*/
-function quiz_report_load_questions($quiz){
+function quiz_report_get_significant_questions($quiz) {
global $DB;
- $questionlist = quiz_questions_in_quiz($quiz->questions);
- list($usql, $params) = $DB->get_in_or_equal(explode(',', $questionlist));
+ $questionids = quiz_questions_in_quiz($quiz->questions);
+ list($usql, $params) = $DB->get_in_or_equal(explode(',', $questionids));
$params[] = $quiz->id;
$questions = $DB->get_records_sql("
SELECT
@@ -103,8 +103,8 @@ FROM {question} q
JOIN {quiz_question_instances} qqi ON qqi.question = q.id
WHERE
- qqi.quiz = ? AND
q.id $usql AND
+ qqi.quiz = ? AND
length > 0", $params);
$qsbyslot = array();
diff --git a/question/engine/datalib.php b/question/engine/datalib.php
index dd670676820..4c8846f81ba 100644
--- a/question/engine/datalib.php
+++ b/question/engine/datalib.php
@@ -321,7 +321,7 @@ WHERE
* $manuallygraded and $all.
*/
public function load_questions_usages_question_state_summary(qubaid_condition $qubaids, $slots) {
- list($slottest, $params) = get_in_or_equal($slots, SQL_PARAMS_NAMED, 'slot0000');
+ list($slottest, $params) = $this->db->get_in_or_equal($slots, SQL_PARAMS_NAMED, 'slot0000');
$rs = $this->db->get_recordset_sql("
SELECT
@@ -356,12 +356,8 @@ ORDER BY
q.id
", $params + $qubaids->from_where_params());
- if (!$rs) {
- throw new moodle_exception('errorloadingdata');
- }
-
$results = array();
- while ($row = rs_fetch_next_record($rs)) {
+ foreach ($rs as $row) {
$index = $row->slot . ',' . $row->questionid;
if (!array_key_exists($index, $results)) {
@@ -380,7 +376,7 @@ ORDER BY
$results[$index]->{$row->summarystate} = $row->numattempts;
$results[$index]->all += $row->numattempts;
}
- rs_close($rs);
+ $rs->close();
return $results;
}
@@ -400,6 +396,7 @@ ORDER BY
* @param integer $questionid (optional) Only return attempts that were of this specific question.
* @param string $summarystate the summary state of interest, or 'all'.
* @param string $orderby the column to order by.
+ * @param array $params any params required by any of the SQL fragments.
* @param integer $limitfrom implements paging of the results.
* Ignored if $orderby = random or $limitnum is null.
* @param integer $limitnum implements paging of the results. null = all.
@@ -407,16 +404,18 @@ ORDER BY
*/
public function load_questions_usages_where_question_in_state(
qubaid_condition $qubaids, $summarystate, $slot, $questionid = null,
- $orderby = 'random', $limitfrom = 0, $limitnum = null) {
+ $orderby = 'random', $params, $limitfrom = 0, $limitnum = null) {
global $CFG;
$extrawhere = '';
if ($questionid) {
- $extrawhere .= ' AND qa.questionid = ' . $questionid;
+ $extrawhere .= ' AND qa.questionid = :questionid';
+ $params['questionid'] = $questionid;
}
if ($summarystate != 'all') {
- $test = $this->in_summary_state_test($summarystate);
+ list($test, $sparams) = $this->in_summary_state_test($summarystate);
$extrawhere .= ' AND qas.state ' . $test;
+ $params += $sparams;
}
if ($orderby == 'random') {
@@ -433,23 +432,25 @@ ORDER BY
// 10,000 students in the coures, each doing 5 quiz attempts. That
// is a 50,000 element int => int array, which PHP seems to use 5MB
// memeory to store on a 64 bit server.
- $qubaids = get_records_sql_menu("
+ $params += $qubaids->from_where_params();
+ $params['slot'] = $slot;
+ $qubaids = $this->db->get_records_sql_menu("
SELECT
qa.questionusageid,
1
FROM {$qubaids->from_question_attempts('qa')}
-JOIN {$CFG->prefix}question_attempt_steps qas ON
+JOIN {question_attempt_steps} qas ON
qas.id = {$this->latest_step_for_qa_subquery()}
-JOIN {$CFG->prefix}question q ON q.id = qa.questionid
+JOIN {question} q ON q.id = qa.questionid
WHERE
{$qubaids->where()} AND
- qa.slot = $slot
+ qa.slot = :slot
$extrawhere
$sqlorderby
- ");
+ ", $params);
$qubaids = array_keys($qubaids);
$count = count($qubaids);
@@ -606,8 +607,6 @@ ORDER BY
* @param question_attempt $qa the question attempt that has changed.
*/
public function update_question_attempt(question_attempt $qa) {
- global $DB;
-
$record = new stdClass;
$record->id = $qa->get_database_id();
$record->maxmark = $qa->get_max_mark();
@@ -725,11 +724,9 @@ ORDER BY
* @param boolean $equal if false, do a NOT IN test. Default true.
* @return string SQL fragment.
*/
- public function in_summary_state_test($summarystate, $equal = true) {
+ public function in_summary_state_test($summarystate, $equal = true, $prefix = 'summarystates') {
$states = question_state::get_all_for_summary_state($summarystate);
- list($sql, $params) = $this->db->get_in_or_equal($states, SQL_PARAMS_QM, 'param0000', $equal);
- // TODO return $params;
- return $sql;
+ return $this->db->get_in_or_equal($states, SQL_PARAMS_NAMED, $prefix . '00', $equal);
}
/**
diff --git a/question/todo/diffstat.txt b/question/todo/diffstat.txt
index 03a5041aafa..df28181e002 100644
--- a/question/todo/diffstat.txt
+++ b/question/todo/diffstat.txt
@@ -124,7 +124,7 @@ DONE lang/en_utf8/quiz_regrade.php | 7 -
DONE mod/quiz/report/grading/db/access.php | 49 +
DONE mod/quiz/report/grading/gradingsettings_form.php | 91 +
DONE mod/quiz/report/grading/lang/en_utf8/quiz_grading.php | 60 +
- mod/quiz/report/grading/report.php | 842 ++++---
+DONE mod/quiz/report/grading/report.php | 842 ++++---
DONE mod/quiz/report/grading/version.php | 26 +
DONE lang/en_utf8/quiz_grading.php | 18 -