MDL-20636 Convert the overview report.

This commit is contained in:
Tim Hunt 2011-02-16 17:33:51 +00:00
parent 2a3bdbf98e
commit e24ee794b2
14 changed files with 959 additions and 845 deletions

View file

@ -412,33 +412,30 @@ abstract class quiz_attempt_report_table extends table_sql {
return $OUTPUT->user_picture($user); return $OUTPUT->user_picture($user);
} }
public function col_fullname($attempt) {
public function col_timestart($attempt) { $html = parent::col_fullname($attempt);
if (!$attempt->attempt) { if ($this->is_downloading()) {
return '-'; return $html;
} }
$startdate = userdate($attempt->timestart, $this->strtimeformat); return $html . html_writer::empty_tag('br') . html_writer::link(
if (!$this->is_downloading()) { new moodle_url('/mod/quiz/review.php', array('attempt' => $attempt->attempt)),
return html_writer::link(new moodle_url('/mod/quiz/review.php', array('attempt' => $attempt->attempt)), $startdate); get_string('reviewattempt', 'quiz'), array('class' => 'reviewlink'));
}
public function col_timestart($attempt) {
if ($attempt->attempt) {
return userdate($attempt->timestart, $this->strtimeformat);
} else { } else {
return $startdate; return '-';
} }
} }
public function col_timefinish($attempt) { public function col_timefinish($attempt) {
if (!$attempt->attempt) { if ($attempt->attempt && $attempt->timefinish) {
return '-'; return userdate($attempt->timefinish, $this->strtimeformat);
}
if (!$attempt->timefinish) {
return '-';
}
$timefinish = userdate($attempt->timefinish, $this->strtimeformat);
if (!$this->is_downloading()) {
return html_writer::link(new moodle_url('/mod/quiz/review.php', array('attempt' => $attempt->attempt)), $timefinish);
} else { } else {
return $timefinish; return '-';
} }
} }

View file

@ -4,14 +4,14 @@
xsi:noNamespaceSchemaLocation="../../../../../lib/xmldb/xmldb.xsd" xsi:noNamespaceSchemaLocation="../../../../../lib/xmldb/xmldb.xsd"
> >
<TABLES> <TABLES>
<TABLE NAME="quiz_question_regrade" COMMENT="This table records which question attempts need regrading and the grade they will be regraded to."> <TABLE NAME="quiz_overview_regrades" COMMENT="This table records which question attempts need regrading and the grade they will be regraded to.">
<FIELDS> <FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="questionid"/> <FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="true" NEXT="questionusageid"/>
<FIELD NAME="questionid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" COMMENT="Foreign key references question.id." PREVIOUS="id" NEXT="attemptid"/> <FIELD NAME="questionusageid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" COMMENT="Foreign key references question_usages.id, or equivalently quiz_attempt.uniqueid." PREVIOUS="id" NEXT="slot"/>
<FIELD NAME="attemptid" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" COMMENT="Foreign key references question_attempt.id, or equivalently quiz_attempt.uniqueid." PREVIOUS="questionid" NEXT="newgrade"/> <FIELD NAME="slot" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" COMMENT="Foreign key, references question_attempts.slot" PREVIOUS="questionusageid" NEXT="newfraction"/>
<FIELD NAME="newgrade" TYPE="number" LENGTH="12" NOTNULL="true" UNSIGNED="false" SEQUENCE="false" DECIMALS="7" COMMENT="The new grade for this question after regrading." PREVIOUS="attemptid" NEXT="oldgrade"/> <FIELD NAME="newfraction" TYPE="number" LENGTH="12" NOTNULL="false" UNSIGNED="false" SEQUENCE="false" DECIMALS="7" COMMENT="The new fraction for this question_attempt after regrading." PREVIOUS="slot" NEXT="oldfraction"/>
<FIELD NAME="oldgrade" TYPE="number" LENGTH="12" NOTNULL="true" UNSIGNED="false" SEQUENCE="false" DECIMALS="7" COMMENT="The previous grade for this question in this attempt." PREVIOUS="newgrade" NEXT="regraded"/> <FIELD NAME="oldfraction" TYPE="number" LENGTH="12" NOTNULL="false" UNSIGNED="false" SEQUENCE="false" DECIMALS="7" COMMENT="The previous fraction for this question_attempt." PREVIOUS="newfraction" NEXT="regraded"/>
<FIELD NAME="regraded" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" COMMENT="set to 0 if element has just been regraded. Set to 1 if element has been marked as needing regrading." PREVIOUS="oldgrade" NEXT="timemodified"/> <FIELD NAME="regraded" TYPE="int" LENGTH="4" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" COMMENT="set to 0 if element has just been regraded. Set to 1 if element has been marked as needing regrading." PREVIOUS="oldfraction" NEXT="timemodified"/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" COMMENT="Timestamp of when this row was last modified." PREVIOUS="regraded"/> <FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" UNSIGNED="true" SEQUENCE="false" COMMENT="Timestamp of when this row was last modified." PREVIOUS="regraded"/>
</FIELDS> </FIELDS>
<KEYS> <KEYS>

View file

@ -1,6 +1,34 @@
<?php <?php
function xmldb_quiz_overview_upgrade($oldversion) { // This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Quiz overview report upgrade script.
*
* @package quiz
* @subpackage overview
* @copyright 2008 Jamie Pratt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Quiz overview report upgrade function.
* @param number $oldversion
*/function xmldb_quiz_overview_upgrade($oldversion) {
global $CFG, $DB; global $CFG, $DB;
$dbman = $DB->get_manager(); $dbman = $DB->get_manager();
@ -9,10 +37,10 @@ function xmldb_quiz_overview_upgrade($oldversion) {
if ($oldversion < 2009091400) { if ($oldversion < 2009091400) {
/// Define table quiz_question_regrade to be created // Define table quiz_question_regrade to be created
$table = new xmldb_table('quiz_question_regrade'); $table = new xmldb_table('quiz_question_regrade');
/// Adding fields to table quiz_question_regrade // Adding fields to table quiz_question_regrade
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null); $table->add_field('id', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_field('questionid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null); $table->add_field('questionid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
$table->add_field('attemptid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null); $table->add_field('attemptid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
@ -21,19 +49,161 @@ function xmldb_quiz_overview_upgrade($oldversion) {
$table->add_field('regraded', XMLDB_TYPE_INTEGER, '4', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null); $table->add_field('regraded', XMLDB_TYPE_INTEGER, '4', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
$table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null); $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null);
/// Adding keys to table quiz_question_regrade // Adding keys to table quiz_question_regrade
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id')); $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
/// Conditionally launch create table for quiz_question_regrade // Conditionally launch create table for quiz_question_regrade
if (!$dbman->table_exists($table)) { if (!$dbman->table_exists($table)) {
$dbman->create_table($table); $dbman->create_table($table);
} }
/// overview savepoint reached // overview savepoint reached
upgrade_plugin_savepoint(true, 2009091400, 'quizreport', 'overview'); upgrade_plugin_savepoint(true, 2009091400, 'quiz', 'overview');
}
if ($oldversion < 2010040600) {
// Wipe the quiz_question_regrade before we changes its structure. The data
// It contains is not important long-term, and it is almost impossible to upgrade.
$DB->delete_records('quiz_question_regrade');
// overview savepoint reached
upgrade_plugin_savepoint(true, 2010040600, 'quiz', 'overview');
}
if ($oldversion < 2010040601) {
// Rename field attemptid on table quiz_question_regrade to questionusageid
$table = new xmldb_table('quiz_question_regrade');
$field = new xmldb_field('attemptid', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, 'id');
// Launch rename field questionusageid
$dbman->rename_field($table, $field, 'questionusageid');
// overview savepoint reached
upgrade_plugin_savepoint(true, 2010040601, 'quiz', 'overview');
}
if ($oldversion < 2010040602) {
// Define field slot to be added to quiz_question_regrade
$table = new xmldb_table('quiz_question_regrade');
$field = new xmldb_field('slot', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, XMLDB_NOTNULL, null, null, 'questionusageid');
// Conditionally launch add field slot
if (!$dbman->field_exists($table, $field)) {
$dbman->add_field($table, $field);
}
// overview savepoint reached
upgrade_plugin_savepoint(true, 2010040602, 'quiz', 'overview');
}
if ($oldversion < 2010040603) {
// Define field questionid to be dropped from quiz_question_regrade
$table = new xmldb_table('quiz_question_regrade');
$field = new xmldb_field('questionid');
// Conditionally launch drop field questionusageid
if ($dbman->field_exists($table, $field)) {
$dbman->drop_field($table, $field);
}
// overview savepoint reached
upgrade_plugin_savepoint(true, 2010040603, 'quiz', 'overview');
}
if ($oldversion < 2010040604) {
// Rename field newgrade on table quiz_question_regrade to newfraction
$table = new xmldb_table('quiz_question_regrade');
$field = new xmldb_field('newgrade', XMLDB_TYPE_NUMBER, '12, 7', null, null, null, null, 'slot');
// Launch rename field newfraction
$dbman->rename_field($table, $field, 'newfraction');
// overview savepoint reached
upgrade_plugin_savepoint(true, 2010040604, 'quiz', 'overview');
}
if ($oldversion < 2010040605) {
// Rename field oldgrade on table quiz_question_regrade to oldfraction
$table = new xmldb_table('quiz_question_regrade');
$field = new xmldb_field('oldgrade', XMLDB_TYPE_NUMBER, '12, 7', null, null, null, null, 'slot');
// Launch rename field newfraction
$dbman->rename_field($table, $field, 'oldfraction');
// overview savepoint reached
upgrade_plugin_savepoint(true, 2010040605, 'quiz', 'overview');
}
if ($oldversion < 2010040606) {
// Changing precision of field newfraction on table quiz_question_regrade to (12, 7)
$table = new xmldb_table('quiz_question_regrade');
$field = new xmldb_field('newfraction', XMLDB_TYPE_NUMBER, '12, 7', null, null, null, null, 'slot');
// Launch change of precision for field newfraction
$dbman->change_field_precision($table, $field);
// overview savepoint reached
upgrade_plugin_savepoint(true, 2010040606, 'quiz', 'overview');
}
if ($oldversion < 2010040607) {
// Changing precision of field oldfraction on table quiz_question_regrade to (12, 7)
$table = new xmldb_table('quiz_question_regrade');
$field = new xmldb_field('oldfraction', XMLDB_TYPE_NUMBER, '12, 7', null, null, null, null, 'slot');
// Launch change of precision for field newfraction
$dbman->change_field_precision($table, $field);
// overview savepoint reached
upgrade_plugin_savepoint(true, 2010040607, 'quiz', 'overview');
}
if ($oldversion < 2010082700) {
// Changing nullability of field newfraction on table quiz_question_regrade to null
$table = new xmldb_table('quiz_question_regrade');
$field = new xmldb_field('newfraction', XMLDB_TYPE_NUMBER, '12, 7', null, null, null, null, 'slot');
// Launch change of nullability for field newfraction
$dbman->change_field_notnull($table, $field);
// overview savepoint reached
upgrade_plugin_savepoint(true, 2010082700, 'quiz', 'overview');
}
if ($oldversion < 2010082701) {
// Changing nullability of field oldfraction on table quiz_question_regrade to null
$table = new xmldb_table('quiz_question_regrade');
$field = new xmldb_field('oldfraction', XMLDB_TYPE_NUMBER, '12, 7', null, null, null, null, 'slot');
// Launch change of nullability for field newfraction
$dbman->change_field_notnull($table, $field);
// overview savepoint reached
upgrade_plugin_savepoint(true, 2010082701, 'quiz', 'overview');
}
if ($oldversion < 2011021600) {
// Define table quiz_question_regrade to be renamed to quiz_overview_regrades
// so that it follows the Moodle coding guidelines.
$table = new xmldb_table('quiz_question_regrade');
// Launch rename table for quiz_question_regrade
$dbman->rename_table($table, 'quiz_overview_regrades');
// overview savepoint reached
upgrade_plugin_savepoint(true, 2011021600, 'quiz', 'overview');
} }
return true; return true;
} }

View file

@ -47,6 +47,7 @@ $string['optonlygradedattempts'] = 'that are graded for each user ({$a})';
$string['optonlyregradedattempts'] = 'that have been regraded / are marked as needing regrading'; $string['optonlyregradedattempts'] = 'that have been regraded / are marked as needing regrading';
$string['overview'] = 'Grades'; $string['overview'] = 'Grades';
$string['overviewdownload'] = 'Overview download'; $string['overviewdownload'] = 'Overview download';
$string['overviewfilename'] = 'grades';
$string['overviewreport'] = 'Grades report'; $string['overviewreport'] = 'Grades report';
$string['overviewreportgraph'] = 'Overall number of students achieving grade ranges'; $string['overviewreportgraph'] = 'Overall number of students achieving grade ranges';
$string['overviewreportgraphgroup'] = 'Number of students in group \'{$a}\' achieving grade ranges'; $string['overviewreportgraphgroup'] = 'Number of students in group \'{$a}\' achieving grade ranges';
@ -64,10 +65,9 @@ $string['regradealldrygroup'] = 'Dry run a full regrade for group \'{$a->groupna
$string['regradeallgroup'] = 'Full regrade for group \'{$a->groupname}\''; $string['regradeallgroup'] = 'Full regrade for group \'{$a->groupname}\'';
$string['regradeheader'] = 'Regrading'; $string['regradeheader'] = 'Regrading';
$string['regradeselected'] = 'Regrade selected attempts'; $string['regradeselected'] = 'Regrade selected attempts';
$string['requiresgrading'] = 'Requires grading'; $string['show'] = 'Show / download';
$string['show'] = 'Include'; $string['showattempts'] = 'Only show / download attempts';
$string['showattempts'] = 'Include attempts'; $string['showdetailedmarks'] = 'Show / download marks for each question';
$string['showdetailedmarks'] = 'Marks for each question';
$string['showinggraded'] = 'Showing only the attempt graded for each user.'; $string['showinggraded'] = 'Showing only the attempt graded for each user.';
$string['showinggradedandungraded'] = 'Showing graded and ungraded attempts for each user. The one attempt for each user that is graded is highlighted. The grading method for this quiz is {$a}.'; $string['showinggradedandungraded'] = 'Showing graded and ungraded attempts for each user. The one attempt for each user that is graded is highlighted. The grading method for this quiz is {$a}.';
$string['studentingroup'] = '\'{$a->coursestudent}\' in group \'{$a->groupname}\''; $string['studentingroup'] = '\'{$a->coursestudent}\' in group \'{$a->groupname}\'';

View file

@ -1,40 +1,62 @@
<?php <?php
class quiz_report_overview_table extends table_sql { // This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
var $useridfield = 'userid'; /**
* This file defines the quiz grades table.
*
* @package quiz_overview
* @copyright 2008 Jamie Pratt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
var $candelete;
var $reporturl;
var $displayoptions;
var $regradedqs = array();
function quiz_report_overview_table($quiz , $qmsubselect, $groupstudents, /**
$students, $detailedmarks, $questions, $candelete, $reporturl, $displayoptions, $context){ * This is a table subclass for displaying the quiz grades report.
parent::__construct('mod-quiz-report-overview-report'); *
$this->quiz = $quiz; * @copyright 2008 Jamie Pratt
$this->qmsubselect = $qmsubselect; * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
$this->groupstudents = $groupstudents; */
$this->students = $students; class quiz_report_overview_table extends quiz_attempt_report_table {
protected $candelete;
protected $regradedqs = array();
public function __construct($quiz, $context, $qmsubselect, $groupstudents,
$students, $detailedmarks, $questions, $candelete, $reporturl, $displayoptions) {
parent::__construct('mod-quiz-report-overview-report', $quiz , $context,
$qmsubselect, $groupstudents, $students, $questions, $candelete,
$reporturl, $displayoptions);
$this->detailedmarks = $detailedmarks; $this->detailedmarks = $detailedmarks;
$this->questions = $questions;
$this->candelete = $candelete;
$this->reporturl = $reporturl;
$this->displayoptions = $displayoptions;
$this->context = $context;
} }
function build_table(){
global $CFG, $DB; public function build_table() {
global $DB;
if ($this->rawdata) { if ($this->rawdata) {
// Define some things we need later to process raw data from db.
$this->strtimeformat = str_replace(',', '', get_string('strftimedatetime')); $this->strtimeformat = str_replace(',', '', get_string('strftimedatetime'));
parent::build_table(); parent::build_table();
//end of adding data from attempts data to table / download //end of adding data from attempts data to table / download
//now add averages at bottom of table : //now add averages at bottom of table :
$params = array($this->quiz->id); $params = array($this->quiz->id);
$averagesql = "SELECT AVG(qg.grade) AS grade " . $averagesql = '
"FROM {quiz_grades} qg " . SELECT AVG(qg.grade) AS grade, COUNT(qg.grade) AS numaveraged
"WHERE quiz=?"; FROM {quiz_grades} qg
WHERE quiz = ?';
$this->add_separator(); $this->add_separator();
if ($this->is_downloading()) { if ($this->is_downloading()) {
@ -43,53 +65,100 @@ class quiz_report_overview_table extends table_sql {
$namekey = 'fullname'; $namekey = 'fullname';
} }
if ($this->groupstudents) { if ($this->groupstudents) {
list($g_usql, $g_params) = $DB->get_in_or_equal($this->groupstudents); list($usql, $uparams) = $DB->get_in_or_equal($this->groupstudents);
$record = $DB->get_record_sql($averagesql . ' AND qg.userid ' . $usql, array_merge($params, $uparams));
$groupaveragesql = $averagesql." AND qg.userid $g_usql"; $groupaveragerow = array(
$groupaverage = $DB->get_record_sql($groupaveragesql, array_merge($params, $g_params)); $namekey => get_string('groupavg', 'grades'),
$groupaveragerow = array($namekey => get_string('groupavg', 'grades'), 'sumgrades' => $this->format_average($record),
'sumgrades' => quiz_format_grade($this->quiz, $groupaverage->grade), 'feedbacktext'=> strip_tags(quiz_report_feedback_for_grade($record->grade, $this->quiz->id)));
'feedbacktext'=> strip_tags(quiz_report_feedback_for_grade($groupaverage->grade, $this->quiz->id, $this->context))); if ($this->detailedmarks && ($this->quiz->attempts == 1 || $this->qmsubselect)) {
if($this->detailedmarks && ($this->qmsubselect || $this->quiz->attempts == 1)) { $avggradebyq = $this->load_average_question_grades($this->groupstudents);
$avggradebyq = quiz_get_average_grade_for_questions($this->quiz, $this->groupstudents); $groupaveragerow += $this->format_average_grade_for_questions($avggradebyq);
$groupaveragerow += quiz_format_average_grade_for_questions($avggradebyq, $this->questions, $this->quiz, $this->is_downloading());
} }
$this->add_data_keyed($groupaveragerow); $this->add_data_keyed($groupaveragerow);
} }
list($s_usql, $s_params) = $DB->get_in_or_equal($this->students); if ($this->students) {
$overallaverage = $DB->get_record_sql($averagesql." AND qg.userid $s_usql", array_merge($params, $s_params)); list($usql, $uparams) = $DB->get_in_or_equal($this->students);
$overallaveragerow = array($namekey => get_string('overallaverage', 'grades'), $record = $DB->get_record_sql($averagesql . ' AND qg.userid ' . $usql, array_merge($params, $uparams));
'sumgrades' => quiz_format_grade($this->quiz, $overallaverage->grade), $overallaveragerow = array(
'feedbacktext'=> strip_tags(quiz_report_feedback_for_grade($overallaverage->grade, $this->quiz->id, $this->context))); $namekey => get_string('overallaverage', 'grades'),
if($this->detailedmarks && ($this->qmsubselect || $this->quiz->attempts == 1)) { 'sumgrades' => $this->format_average($record),
$avggradebyq = quiz_get_average_grade_for_questions($this->quiz, $this->students); 'feedbacktext'=> strip_tags(quiz_report_feedback_for_grade($record->grade, $this->quiz->id, $this->context)));
$overallaveragerow += quiz_format_average_grade_for_questions($avggradebyq, $this->questions, $this->quiz, $this->is_downloading()); if ($this->detailedmarks && ($this->quiz->attempts == 1 || $this->qmsubselect)) {
$avggradebyq = $this->load_average_question_grades($this->students);
$overallaveragerow += $this->format_average_grade_for_questions($avggradebyq);
} }
$this->add_data_keyed($overallaveragerow); $this->add_data_keyed($overallaveragerow);
} }
} }
}
protected function format_average_grade_for_questions($gradeaverages) {
$row = array();
if (!$gradeaverages) {
$gradeaverages = array();
}
foreach ($this->questions as $question) {
if (isset($gradeaverages[$question->slot]) && $question->maxmark > 0) {
$record = $gradeaverages[$question->slot];
$record->grade = quiz_rescale_grade($record->averagefraction * $question->maxmark, $this->quiz, false);
} else {
$record = new stdClass;
$record->grade = null;
$record->numaveraged = null;
}
$row['qsgrade' . $question->slot] = $this->format_average($record, true);
}
return $row;
}
/**
* Format an entry in an average row.
* @param object $record with fields grade and numaveraged
*/
protected function format_average($record, $question = false) {
if (is_null($record->grade)) {
$average = '-';
} else if ($question) {
$average = quiz_format_question_grade($this->quiz, $record->grade);
} else {
$average = quiz_format_grade($this->quiz, $record->grade);
}
if ($this->download) {
return $average;
} else if (is_null($record->numaveraged)) {
return html_writer::tag('span', html_writer::tag('span',
$average, array('class' => 'average')), array('class' => 'avgcell'));
} else {
return html_writer::tag('span', html_writer::tag('span',
$average, array('class' => 'average')) . ' ' . html_writer::tag('span',
'(' . $record->numaveraged . ')', array('class' => 'count')), array('class' => 'avgcell'));
}
}
public function wrap_html_start() {
if ($this->is_downloading() || !$this->candelete) {
return;
}
function wrap_html_start(){
if (!$this->is_downloading()) {
if ($this->candelete) {
// Start form // Start form
$url = new moodle_url($this->reporturl, $this->displayoptions); $url = new moodle_url($this->reporturl, $this->displayoptions + array('sesskey' => sesskey()));
echo '<div id="tablecontainer" class="overview-tablecontainer">'; echo '<div id="tablecontainer" class="overview-tablecontainer">';
echo '<form id="attemptsform" method="post" action="' . $this->reporturl->out_omit_querystring() . '">'; echo '<form id="attemptsform" method="post" action="' . $this->reporturl->out_omit_querystring() . '">';
echo '<div style="display: none;">'; echo '<div style="display: none;">';
echo html_writer::input_hidden_params($url); echo html_writer::input_hidden_params($url);
echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey())) . "\n";
echo '</div>'; echo '</div>';
echo '<div>'; echo '<div>';
} }
public function wrap_html_finish() {
if ($this->is_downloading() || !$this->candelete) {
return;
} }
}
function wrap_html_finish(){ // TODO add back are you sure, and convert to html_writer.
if (!$this->is_downloading()) {
// Print "Select all" etc.
if ($this->candelete) {
$strreallydel = addslashes_js(get_string('deleteattemptcheck','quiz'));
echo '<div id="commands">'; echo '<div id="commands">';
echo '<a href="javascript:select_all_in(\'DIV\',null,\'tablecontainer\');">'. echo '<a href="javascript:select_all_in(\'DIV\',null,\'tablecontainer\');">'.
get_string('selectall', 'quiz').'</a> / '; get_string('selectall', 'quiz').'</a> / ';
@ -99,72 +168,14 @@ class quiz_report_overview_table extends table_sql {
if (has_capability('mod/quiz:regrade', $this->context)) { if (has_capability('mod/quiz:regrade', $this->context)) {
echo '<input type="submit" name="regrade" value="'.get_string('regradeselected', 'quiz_overview').'"/>'; echo '<input type="submit" name="regrade" value="'.get_string('regradeselected', 'quiz_overview').'"/>';
} }
echo '<input type="submit" onclick="return confirm(\''.$strreallydel.'\');" name="delete" value="'.get_string('deleteselected', 'quiz_overview').'"/>'; echo '<input type="submit" name="delete" value="'.get_string('deleteselected', 'quiz_overview').'"/>';
echo '</div>'; echo '</div>';
// Close form // Close form
echo '</div>'; echo '</div>';
echo '</form></div>'; echo '</form></div>';
} }
}
}
public function col_sumgrades($attempt) {
function col_checkbox($attempt){
if ($attempt->attempt){
return '<input type="checkbox" name="attemptid[]" value="'.$attempt->attempt.'" />';
} else {
return '';
}
}
function col_picture($attempt){
global $COURSE, $OUTPUT;
$user = new stdClass();
$user->id = $attempt->userid;
$user->lastname = $attempt->lastname;
$user->firstname = $attempt->firstname;
$user->imagealt = $attempt->imagealt;
$user->picture = $attempt->picture;
$user->email = $attempt->email;
return $OUTPUT->user_picture($user);
}
function col_fullname($attempt){
$html = parent::col_fullname($attempt);
if ($this->is_downloading()) {
return $html;
}
return $html . '<br /><a class="reviewlink" href="review.php?q='.$this->quiz->id.'&amp;attempt='.$attempt->attempt.
'">'.get_string('reviewattempt', 'quiz').'</a>';
}
function col_timestart($attempt){
if ($attempt->attempt) {
return userdate($attempt->timestart, $this->strtimeformat);
} else {
return '-';
}
}
function col_timefinish($attempt){
if ($attempt->attempt && $attempt->timefinish) {
return userdate($attempt->timefinish, $this->strtimeformat);
} else {
return '-';
}
}
function col_duration($attempt){
if ($attempt->timefinish) {
return format_time($attempt->timefinish - $attempt->timestart);
} elseif ($attempt->timestart) {
return get_string('unfinished', 'quiz');
} else {
return '-';
}
}
function col_sumgrades($attempt){
if (!$attempt->timefinish) { if (!$attempt->timefinish) {
return '-'; return '-';
} }
@ -174,29 +185,26 @@ class quiz_report_overview_table extends table_sql {
return $grade; return $grade;
} }
if (isset($this->regradedqs[$attempt->attemptuniqueid])){ if (isset($this->regradedqs[$attempt->usageid])) {
$newsumgrade = 0; $newsumgrade = 0;
$oldsumgrade = 0; $oldsumgrade = 0;
foreach ($this->questions as $question) { foreach ($this->questions as $question) {
if (isset($this->regradedqs[$attempt->attemptuniqueid][$question->id])){ if (isset($this->regradedqs[$attempt->usageid][$question->slot])) {
$newsumgrade += $this->regradedqs[$attempt->attemptuniqueid][$question->id]->newgrade; $newsumgrade += $this->regradedqs[$attempt->usageid][$question->slot]->newfraction * $question->maxmark;
$oldsumgrade += $this->regradedqs[$attempt->attemptuniqueid][$question->id]->oldgrade; $oldsumgrade += $this->regradedqs[$attempt->usageid][$question->slot]->oldfraction * $question->maxmark;
} else { } else {
$newsumgrade += $this->gradedstatesbyattempt[$attempt->attemptuniqueid][$question->id]->grade; $newsumgrade += $this->lateststeps[$attempt->usageid][$question->slot]->fraction * $question->maxmark;
$oldsumgrade += $this->gradedstatesbyattempt[$attempt->attemptuniqueid][$question->id]->grade; $oldsumgrade += $this->lateststeps[$attempt->usageid][$question->slot]->fraction * $question->maxmark;
} }
} }
$newsumgrade = quiz_rescale_grade($newsumgrade, $this->quiz); $newsumgrade = quiz_rescale_grade($newsumgrade, $this->quiz);
$oldsumgrade = quiz_rescale_grade($oldsumgrade, $this->quiz); $oldsumgrade = quiz_rescale_grade($oldsumgrade, $this->quiz);
$grade = "<del>$oldsumgrade</del><br />$newsumgrade"; $grade = html_writer::tag('del', $oldsumgrade) . '/' .
html_writer::empty_tag('br') . $newsumgrade;
} }
return html_writer::link(new moodle_url('/mod/quiz/review.php',
$gradehtml = '<a href="review.php?q='.$this->quiz->id.'&amp;attempt='.$attempt->attempt. array('attempt' => $attempt->attempt)), $grade,
'" title="'.get_string('reviewattempt', 'quiz').'">'.$grade.'</a>'; array('title' => get_string('reviewattempt', 'quiz')));
if ($this->qmsubselect && $attempt->gradedattempt){
$gradehtml = '<div class="highlight">'.$gradehtml.'</div>';
}
return $gradehtml;
} }
/** /**
@ -206,77 +214,52 @@ class quiz_report_overview_table extends table_sql {
* and what they are called. * and what they are called.
* @return string the contents of the cell. * @return string the contents of the cell.
*/ */
function other_cols($colname, $attempt){ public function other_cols($colname, $attempt) {
global $OUTPUT; if (!preg_match('/^qsgrade(\d+)$/', $colname, $matches)) {
if (preg_match('/^qsgrade([0-9]+)$/', $colname, $matches)){
$questionid = $matches[1];
$question = $this->questions[$questionid];
if (isset($this->gradedstatesbyattempt[$attempt->attemptuniqueid][$questionid])){
$stateforqinattempt = $this->gradedstatesbyattempt[$attempt->attemptuniqueid][$questionid];
} else {
$stateforqinattempt = false;
}
if ($stateforqinattempt && question_state_is_graded($stateforqinattempt)) {
$grade = quiz_rescale_grade($stateforqinattempt->grade, $this->quiz, 'question');
if (!$this->is_downloading()) {
if (isset($this->regradedqs[$attempt->attemptuniqueid][$questionid])){
$gradefromdb = $grade;
$newgrade = quiz_rescale_grade($this->regradedqs[$attempt->attemptuniqueid][$questionid]->newgrade, $this->quiz, 'question');
$oldgrade = quiz_rescale_grade($this->regradedqs[$attempt->attemptuniqueid][$questionid]->oldgrade, $this->quiz, 'question');
$grade = '<del>'.$oldgrade.'</del><br />'.
$newgrade;
}
$link = new moodle_url("/mod/quiz/reviewquestion.php?attempt=$attempt->attempt&question=$question->id");
$action = new popup_action('click', $link, 'reviewquestion', array('height' => 450, 'width' => 650));
$linktopopup = $OUTPUT->action_link($link, $grade, $action, array('title'=>get_string('reviewresponsetoq', 'quiz', $question->formattedname)));
if (($this->questions[$questionid]->maxgrade != 0)){
$fractionofgrade = $stateforqinattempt->grade
/ $this->questions[$questionid]->maxgrade;
$qclass = question_get_feedback_class($fractionofgrade);
$feedbackimg = question_get_feedback_image($fractionofgrade);
$questionclass = "que";
return "<span class=\"$questionclass\"><span class=\"$qclass\">".$linktopopup."</span></span>$feedbackimg";
} else {
return $linktopopup;
}
} else {
return $grade;
}
} else if ($stateforqinattempt && question_state_is_closed($stateforqinattempt)) {
$text = get_string('requiresgrading', 'quiz_overview');
if (!$this->is_downloading()) {
$link = new moodle_url("/mod/quiz/reviewquestion.php?attempt=$attempt->attempt&question=$question->id");
$action = new popup_action('click', $link, 'reviewquestion', array('height' => 450, 'width' => 650));
return $OUTPUT->action_link($link, $text, $action, array('title'=>get_string('reviewresponsetoq', 'quiz', $question->formattedname)));
} else {
return $text;
}
} else {
return '--';
}
} else {
return NULL; return NULL;
} }
} $slot = $matches[1];
$question = $this->questions[$slot];
function col_feedbacktext($attempt){ if (!isset($this->lateststeps[$attempt->usageid][$slot])) {
if ($attempt->timefinish) {
if (!$this->is_downloading()) {
return quiz_report_feedback_for_grade(quiz_rescale_grade($attempt->sumgrades, $this->quiz, false), $this->quiz->id, $this->context);
} else {
return strip_tags(quiz_report_feedback_for_grade(quiz_rescale_grade($attempt->sumgrades, $this->quiz, false), $this->quiz->id, $this->context));
}
} else {
return '-'; return '-';
} }
$stepdata = $this->lateststeps[$attempt->usageid][$slot];
$state = question_state::get($stepdata->state);
if ($question->maxmark == 0) {
$grade = '-';
} else if (is_null($stepdata->fraction)) {
if ($state == question_state::$needsgrading) {
$grade = get_string('requiresgrading', 'question');
} else {
$grade = '-';
} }
function col_regraded($attempt){ } else {
$grade = quiz_rescale_grade($stepdata->fraction * $question->maxmark, $this->quiz, 'question');
}
if ($this->is_downloading()) {
return $grade;
}
if (isset($this->regradedqs[$attempt->usageid][$slot])) {
$gradefromdb = $grade;
$newgrade = quiz_rescale_grade(
$this->regradedqs[$attempt->usageid][$slot]->newfraction * $question->maxmark,
$this->quiz, 'question');
$oldgrade = quiz_rescale_grade(
$this->regradedqs[$attempt->usageid][$slot]->oldfraction * $question->maxmark,
$this->quiz, 'question');
$grade = html_writer::tag('del', $oldgrade) . '/' .
html_writer::empty_tag('br') . $newgrade;
}
return $this->make_review_link($grade, $attempt, $slot);
}
public function col_regraded($attempt) {
if ($attempt->regraded == '') { if ($attempt->regraded == '') {
return ''; return '';
} else if ($attempt->regraded == 0) { } else if ($attempt->regraded == 0) {
@ -285,59 +268,64 @@ class quiz_report_overview_table extends table_sql {
return get_string('done', 'quiz_overview'); return get_string('done', 'quiz_overview');
} }
} }
function query_db($pagesize, $useinitialsbar=true){
// Add table joins so we can sort by question grade protected function requires_latest_steps_loaded() {
// unfortunately can't join all tables necessary to fetch all grades return $this->detailedmarks;
// to get the state for one question per attempt row we must join two tables
// and there is a limit to how many joins you can have in one query. In MySQL it
// is 61. This means that when having more than 29 questions the query will fail.
// So we join just the tables needed to sort the attempts.
if($sort = $this->get_sql_sort()) {
if ($this->detailedmarks) {
$this->sql->from .= ' ';
$sortparts = explode(',', $sort);
$matches = array();
foreach($sortparts as $sortpart) {
$sortpart = trim($sortpart);
if (preg_match('/^qsgrade([0-9]+)/', $sortpart, $matches)){
$qid = intval($matches[1]);
$this->sql->fields .= ", qs$qid.grade AS qsgrade$qid, qs$qid.event AS qsevent$qid, qs$qid.id AS qsid$qid";
$this->sql->from .= "LEFT JOIN {question_sessions} qns$qid ON qns$qid.attemptid = qa.uniqueid AND qns$qid.questionid = :qid$qid ";
$this->sql->from .= "LEFT JOIN {question_states} qs$qid ON qs$qid.id = qns$qid.newgraded ";
$this->sql->params['qid'.$qid] = $qid;
}
}
} else {
//unset any sort columns that sort on question grade as the
//grades are not being fetched as fields
$sess = &$this->sess;
foreach($sess->sortby as $column => $order) {
if (preg_match('/^qsgrade([0-9]+)/', trim($column))){
unset($sess->sortby[$column]);
} }
protected function is_latest_step_column($column) {
if (preg_match('/^qsgrade([0-9]+)/', $column, $matches)) {
return $matches[1];
} }
return false;
} }
protected function get_required_latest_state_fields($slot, $alias) {
return "$alias.fraction * $alias.maxmark AS qsgrade$slot";
} }
public function query_db($pagesize, $useinitialsbar = true) {
parent::query_db($pagesize, $useinitialsbar); parent::query_db($pagesize, $useinitialsbar);
//get all the attempt ids we want to display on this page
//or to export for download. if ($this->detailedmarks && has_capability('mod/quiz:regrade', $this->context)) {
if (!$this->is_downloading()) { $this->regradedqs = $this->get_regraded_questions();
$attemptids = array();
foreach ($this->rawdata as $attempt){
if ($attempt->attemptuniqueid > 0){
$attemptids[] = $attempt->attemptuniqueid;
}
}
$this->gradedstatesbyattempt = quiz_get_newgraded_states($attemptids, true, 'qs.id, qs.grade, qs.event, qs.question, qs.attempt');
if (has_capability('mod/quiz:regrade', $this->context)){
$this->regradedqs = quiz_get_regraded_qs($attemptids);
}
} else {
$this->gradedstatesbyattempt = quiz_get_newgraded_states($this->sql, true, 'qs.id, qs.grade, qs.event, qs.question, qs.attempt');
if (has_capability('mod/quiz:regrade', $this->context)){
$this->regradedqs = quiz_get_regraded_qs($this->sql);
}
}
} }
} }
/**
* Load the average grade for each question, averaged over particular users.
* @param array $userids the user ids to average over.
*/
protected function load_average_question_grades($userids) {
global $DB;
$qmfilter = '';
if ($this->quiz->attempts != 1) {
$qmfilter = '(' . quiz_report_qm_filter_select($this->quiz, 'quiza') . ') AND ';
}
list($usql, $params) = $DB->get_in_or_equal($userids, SQL_PARAMS_NAMED, 'u0000');
$params['quizid'] = $this->quiz->id;
$qubaids = new qubaid_join(
'{quiz_attempts} quiza',
'quiza.uniqueid',
"quiza.userid $usql AND quiza.quiz = :quizid",
$params);
$dm = new question_engine_data_mapper();
return $dm->load_average_marks($qubaids, array_keys($this->questions));
}
/**
* Get all the questions in all the attempts being displayed that need regrading.
* @return array A two dimensional array $questionusageid => $slot => $regradeinfo.
*/
protected function get_regraded_questions() {
global $DB;
$qubaids = $this->get_qubaids_condition();
$regradedqs = $DB->get_records_select('quiz_overview_regrades',
'questionusageid ' . $qubaids->usage_id_in(), $qubaids->usage_id_in_params());
return quiz_report_index_by_keys($regradedqs, array('questionusageid', 'slot'));
}
}

View file

@ -1,33 +1,60 @@
<?php <?php
include '../../../../config.php';
include $CFG->dirroot."/lib/graphlib.php"; // This file is part of Moodle - http://moodle.org/
include $CFG->dirroot."/mod/quiz/report/reportlib.php"; //
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This file renders the quiz overview graph.
*
* @package quiz_overview
* @copyright 2008 Jamie Pratt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once(dirname(__FILE__) . '/../../../../config.php');
require_once($CFG->libdir . '/graphlib.php');
require_once($CFG->dirroot . '/mod/quiz/locallib.php');
require_once($CFG->dirroot . '/mod/quiz/report/reportlib.php');
$quizid = required_param('id', PARAM_INT); $quizid = required_param('id', PARAM_INT);
$groupid = optional_param('groupid', 0, PARAM_INT); $groupid = optional_param('groupid', 0, PARAM_INT);
$quiz = $DB->get_record('quiz', array('id' => $quizid)); $quiz = $DB->get_record('quiz', array('id' => $quizid));
$course = $DB->get_record('course', array('id' => $quiz->course)); $course = $DB->get_record('course', array('id' => $quiz->course));
$cm = get_coursemodule_from_instance('quiz', $quizid); $cm = get_coursemodule_from_instance('quiz', $quizid);
require_login($course, true, $cm);
require_login($course, false, $cm);
$modcontext = get_context_instance(CONTEXT_MODULE, $cm->id); $modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
if ($groupid && $groupmode = groups_get_activity_groupmode($cm)) { // Groups are being used require_capability('mod/quiz:viewreports', $modcontext);
if ($groupid && $groupmode = groups_get_activity_groupmode($cm)) {
// Groups are being used
$groups = groups_get_activity_allowed_groups($cm); $groups = groups_get_activity_allowed_groups($cm);
if (array_key_exists($groupid, $groups)){ if (!array_key_exists($groupid, $groups)) {
$group = $groups[$groupid];
if (!$groupusers = get_users_by_capability($modcontext, array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'),'','','','',$group->id,'',false)){
print_error('nostudentsingroup');
} else {
$groupusers = array_keys($groupusers);
}
} else {
print_error('errorinvalidgroup', 'group', null, $groupid); print_error('errorinvalidgroup', 'group', null, $groupid);
} }
$group = $groups[$groupid];
$groupusers = get_users_by_capability($modcontext, array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'),'','','','',$group->id,'',false);
if (!$groupusers) {
print_error('nostudentsingroup');
}
$groupusers = array_keys($groupusers);
} else { } else {
$groups = false;
$group = false;
$groupusers = array(); $groupusers = array();
} }
require_capability('mod/quiz:viewreports', $modcontext);
$line = new graph(800,600); $line = new graph(800,600);
$line->parameter['title'] = ''; $line->parameter['title'] = '';
@ -48,52 +75,50 @@ $line->parameter['bar_spacing'] = 10; // don't forget to increase spacing so tha
$bands = $quiz->grade; $bands = $quiz->grade;
while ($bands > 20 || $bands <= 10) { while ($bands > 20 || $bands <= 10) {
if ($bands > 50) { if ($bands > 50) {
$bands = $bands /5; $bands /= 5;
} else if ($bands > 20) { } else if ($bands > 20) {
$bands = $bands /2; $bands /= 2;
} }
if ($bands < 4) { if ($bands < 4) {
$bands = $bands * 5; $bands *= 5;
} else if ($bands <= 10) { } else if ($bands <= 10) {
$bands = $bands * 2; $bands *= 2;
} }
} }
$bandwidth = $quiz->grade / $bands;
$bands = ceil($bands); $bands = ceil($bands);
$bandwidth = $quiz->grade / $bands;
$bandlabels = array(); $bandlabels = array();
for ($i=0;$i < $quiz->grade;$i += $bandwidth){ for ($i = 1; $i <= $bands; $i++) {
$label = quiz_format_grade($quiz, $i).' - '; $bandlabels[] = quiz_format_grade($quiz, ($i - 1) * $bandwidth) . ' - ' .
if ($quiz->grade > $i+$bandwidth){ quiz_format_grade($quiz, $i * $bandwidth);
$label .= quiz_format_grade($quiz, $i+$bandwidth);
} else {
$label .= quiz_format_grade($quiz, $quiz->grade);
}
$bandlabels[] = $label;
} }
$line->x_data = $bandlabels; $line->x_data = $bandlabels;
$line->y_format['allusers'] = $line->y_format['allusers'] = array(
array('colour' => 'red', 'bar' => 'fill', 'shadow_offset' => 1, 'legend' => get_string('allparticipants')); 'colour' => 'red',
'bar' => 'fill',
'shadow_offset' => 1,
'legend' => get_string('allparticipants')
);
$line->y_data['allusers'] = quiz_report_grade_bands($bandwidth, $bands, $quizid, $groupusers); $line->y_data['allusers'] = quiz_report_grade_bands($bandwidth, $bands, $quizid, $groupusers);
$line->y_order = array('allusers'); $line->y_order = array('allusers');
$ymax = max($line->y_data['allusers']);
$line->parameter['y_min_left'] = 0; // start at 0 $line->parameter['y_min_left'] = 0; // start at 0
$line->parameter['y_max_left'] = max($line->y_data['allusers']); $line->parameter['y_max_left'] = $ymax;
$line->parameter['y_decimal_left'] = 0; // 2 decimal places for y axis. $line->parameter['y_decimal_left'] = 0; // 2 decimal places for y axis.
//pick a sensible number of gridlines depending on max value on graph. //pick a sensible number of gridlines depending on max value on graph.
$gridlines = max($line->y_data['allusers']); $gridlines = $ymax;
while ($gridlines >= 10) { while ($gridlines >= 10) {
if ($gridlines >= 50) { if ($gridlines >= 50) {
$gridlines = $gridlines /5; $gridlines /= 5;
} else { } else {
$gridlines = $gridlines /2; $gridlines /= 2;
} }
} }
$line->parameter['y_axis_gridlines'] = $gridlines + 1; $line->parameter['y_axis_gridlines'] = $gridlines + 1;
$line->draw(); $line->draw();

View file

@ -1,11 +1,44 @@
<?php <?php
require_once "$CFG->libdir/formslib.php";
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* This file defines the setting form for the quiz overview report.
*
* @package quiz_overview
* @copyright 2008 Jamie Pratt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once($CFG->libdir . '/formslib.php');
/**
* Quiz overview report settings form.
*
* @copyright 2008 Jamie Pratt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class mod_quiz_report_overview_settings extends moodleform { class mod_quiz_report_overview_settings extends moodleform {
function definition() { function definition() {
global $COURSE; global $COURSE; // TODO get rid of this.
$mform =& $this->_form; $mform = $this->_form;
//-------------------------------------------------------------------------------
$mform->addElement('header', 'preferencespage', get_string('preferencespage', 'quiz_overview')); $mform->addElement('header', 'preferencespage', get_string('preferencespage', 'quiz_overview'));
if (!$this->_customdata['currentgroup']) { if (!$this->_customdata['currentgroup']) {
@ -43,7 +76,7 @@ class mod_quiz_report_overview_settings extends moodleform {
if ($showattemptsgrp) { if ($showattemptsgrp) {
$mform->addGroup($showattemptsgrp, null, get_string('showattempts', 'quiz_overview'), '<br />', false); $mform->addGroup($showattemptsgrp, null, get_string('showattempts', 'quiz_overview'), '<br />', false);
} }
//-------------------------------------------------------------------------------
$mform->addElement('header', 'preferencesuser', get_string('preferencesuser', 'quiz_overview')); $mform->addElement('header', 'preferencesuser', get_string('preferencesuser', 'quiz_overview'));
$mform->addElement('text', 'pagesize', get_string('pagesize', 'quiz_overview')); $mform->addElement('text', 'pagesize', get_string('pagesize', 'quiz_overview'));
@ -54,4 +87,3 @@ class mod_quiz_report_overview_settings extends moodleform {
$mform->addElement('submit', 'submitbutton', get_string('preferencessave', 'quiz_overview')); $mform->addElement('submit', 'submitbutton', get_string('preferencessave', 'quiz_overview'));
} }
} }

View file

@ -1,58 +1,52 @@
<?php <?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/** /**
* This script lists student attempts * This file defines the quiz overview report class.
* *
* @author Martin Dougiamas, Tim Hunt and others.
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
* @package quiz * @package quiz
* @subpackage overview
* @copyright 1999 onwards Martin Dougiamas and others {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/ */
require_once($CFG->libdir.'/tablelib.php');
require_once($CFG->dirroot.'/mod/quiz/report/attemptsreport.php');
require_once($CFG->dirroot.'/mod/quiz/report/overview/overviewsettings_form.php'); require_once($CFG->dirroot.'/mod/quiz/report/overview/overviewsettings_form.php');
require_once($CFG->dirroot.'/mod/quiz/report/overview/overview_table.php'); require_once($CFG->dirroot.'/mod/quiz/report/overview/overview_table.php');
class quiz_overview_report extends quiz_default_report {
/** /**
* Display the report. * Quiz report subclass for the overview (grades) report.
*
* @copyright 1999 onwards Martin Dougiamas and others {@link http://moodle.com}
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/ */
class quiz_overview_report extends quiz_attempt_report {
function display($quiz, $cm, $course) { function display($quiz, $cm, $course) {
global $CFG, $COURSE, $DB, $OUTPUT; global $CFG, $COURSE, $DB, $OUTPUT;
$this->context = get_context_instance(CONTEXT_MODULE, $cm->id); $this->context = get_context_instance(CONTEXT_MODULE, $cm->id);
// Work out some display options - whether there is feedback, and whether grades should be shown.
$hasfeedback = quiz_has_feedback($quiz);
$fakeattempt = new stdClass();
$fakeattempt->preview = false;
$fakeattempt->timefinish = $quiz->timeopen;
$fakeattempt->userid = 0;
$reviewoptions = quiz_get_review_options($quiz, $fakeattempt, $this->context);
$showgrades = quiz_has_grades($quiz) && $reviewoptions->scores;
$download = optional_param('download', '', PARAM_ALPHA); $download = optional_param('download', '', PARAM_ALPHA);
/// find out current groups mode list($currentgroup, $students, $groupstudents, $allowed) =
$currentgroup = groups_get_activity_group($cm, true); $this->load_relevant_students($cm);
if (!$students = get_users_by_capability($this->context, array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'),'u.id,1','','','','','',false)) {
$students = array();
} else {
$students = array_keys($students);
}
if (empty($currentgroup)) {
// all users who can attempt quizzes
$allowed = $students;
$groupstudents = array();
} else {
// all users who can attempt quizzes and who are in the currently selected group
if (!$groupstudents = get_users_by_capability($this->context, array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'),'u.id,1','','','',$currentgroup,'',false)) {
$groupstudents = array();
} else {
$groupstudents = array_keys($groupstudents);
}
$allowed = $groupstudents;
}
$pageoptions = array(); $pageoptions = array();
$pageoptions['id'] = $cm->id; $pageoptions['id'] = $cm->id;
@ -61,26 +55,25 @@ class quiz_overview_report extends quiz_default_report {
$reporturl = new moodle_url('/mod/quiz/report.php', $pageoptions); $reporturl = new moodle_url('/mod/quiz/report.php', $pageoptions);
$qmsubselect = quiz_report_qm_filter_select($quiz); $qmsubselect = quiz_report_qm_filter_select($quiz);
$mform = new mod_quiz_report_overview_settings($reporturl, array('qmsubselect'=> $qmsubselect, 'quiz'=>$quiz, $mform = new mod_quiz_report_overview_settings($reporturl,
'currentgroup'=>$currentgroup, 'context'=>$this->context)); array('qmsubselect' => $qmsubselect, 'quiz' => $quiz, 'currentgroup' => $currentgroup, 'context'=>$this->context));
if ($fromform = $mform->get_data()) { if ($fromform = $mform->get_data()) {
$regradeall = false; $regradeall = false;
$regradealldry = false; $regradealldry = false;
$regradealldrydo = false; $regradealldrydo = false;
$attemptsmode = $fromform->attemptsmode; $attemptsmode = $fromform->attemptsmode;
if ($qmsubselect) { if ($qmsubselect) {
//control is not on the form if
//the grading method is not set
//to grade one attempt per user eg. for average attempt grade.
$qmfilter = $fromform->qmfilter; $qmfilter = $fromform->qmfilter;
} else { } else {
$qmfilter = 0; $qmfilter = 0;
} }
$regradefilter = $fromform->regradefilter; $regradefilter = !empty($fromform->regradefilter);
set_user_preference('quiz_report_overview_detailedmarks', $fromform->detailedmarks); set_user_preference('quiz_report_overview_detailedmarks', $fromform->detailedmarks);
set_user_preference('quiz_report_pagesize', $fromform->pagesize); set_user_preference('quiz_report_pagesize', $fromform->pagesize);
$detailedmarks = $fromform->detailedmarks; $detailedmarks = $fromform->detailedmarks;
$pagesize = $fromform->pagesize; $pagesize = $fromform->pagesize;
} else { } else {
$regradeall = optional_param('regradeall', 0, PARAM_BOOL); $regradeall = optional_param('regradeall', 0, PARAM_BOOL);
$regradealldry = optional_param('regradealldry', 0, PARAM_BOOL); $regradealldry = optional_param('regradealldry', 0, PARAM_BOOL);
@ -92,82 +85,90 @@ class quiz_overview_report extends quiz_default_report {
$qmfilter = 0; $qmfilter = 0;
} }
$regradefilter = optional_param('regradefilter', 0, PARAM_INT); $regradefilter = optional_param('regradefilter', 0, PARAM_INT);
$detailedmarks = get_user_preferences('quiz_report_overview_detailedmarks', 1); $detailedmarks = get_user_preferences('quiz_report_overview_detailedmarks', 1);
$pagesize = get_user_preferences('quiz_report_pagesize', 0); $pagesize = get_user_preferences('quiz_report_pagesize', 0);
} }
if ($currentgroup) {
//default for when a group is selected
if ($attemptsmode === null || $attemptsmode == QUIZ_REPORT_ATTEMPTS_ALL) {
$attemptsmode = QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH;
}
} else if (!$currentgroup && $course->id == SITEID) {
//force report on front page to show all, unless a group is selected.
$attemptsmode = QUIZ_REPORT_ATTEMPTS_ALL;
} else if ($attemptsmode === null) {
//default
$attemptsmode = QUIZ_REPORT_ATTEMPTS_ALL;
}
if (!$reviewoptions->scores) {
$detailedmarks = 0;
}
if ($pagesize < 1) {
$pagesize = QUIZ_REPORT_DEFAULT_PAGE_SIZE;
}
// We only want to show the checkbox to delete attempts
// if the user has permissions and if the report mode is showing attempts.
$candelete = has_capability('mod/quiz:deleteattempts', $this->context)
&& ($attemptsmode != QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH_NO);
$this->validate_common_options($attemptsmode, $pagesize, $course, $currentgroup);
$displayoptions = array(); $displayoptions = array();
$displayoptions['attemptsmode'] = $attemptsmode; $displayoptions['attemptsmode'] = $attemptsmode;
$displayoptions['qmfilter'] = $qmfilter; $displayoptions['qmfilter'] = $qmfilter;
$displayoptions['regradefilter'] = $regradefilter; $displayoptions['regradefilter'] = $regradefilter;
$mform->set_data($displayoptions + array('detailedmarks' => $detailedmarks, 'pagesize' => $pagesize));
if (!$this->should_show_grades($quiz)) {
$detailedmarks = 0;
}
// We only want to show the checkbox to delete attempts
// if the user has permissions and if the report mode is showing attempts.
$candelete = has_capability('mod/quiz:deleteattempts', $this->context)
&& ($attemptsmode != QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH_NO);
if ($attemptsmode == QUIZ_REPORT_ATTEMPTS_ALL) { if ($attemptsmode == QUIZ_REPORT_ATTEMPTS_ALL) {
// This option is only available to users who can access all groups in
// groups mode, so setting allowed to empty (which means all quiz attempts
// are accessible, is not a security porblem.
$allowed = array(); $allowed = array();
} }
// Load the required questions.
if ($detailedmarks) {
$questions = quiz_report_get_significant_questions($quiz);
} else {
$questions = array();
}
$table = new quiz_report_overview_table($quiz, $this->context, $qmsubselect,
$groupstudents, $students, $detailedmarks, $questions, $candelete,
$reporturl, $displayoptions);
$filename = quiz_report_download_filename(get_string('overviewfilename', 'quiz_overview'),
$course->shortname, $quiz->name);
$table->is_downloading($download, $filename,
$COURSE->shortname . ' ' . format_string($quiz->name, true));
if ($table->is_downloading()) {
raise_memory_limit(MEMORY_EXTRA);
}
// Process actions.
if (empty($currentgroup) || $groupstudents) { if (empty($currentgroup) || $groupstudents) {
if (optional_param('delete', 0, PARAM_BOOL) && confirm_sesskey()) { if (optional_param('delete', 0, PARAM_BOOL) && confirm_sesskey()) {
if ($attemptids = optional_param('attemptid', array(), PARAM_INT)) { if ($attemptids = optional_param('attemptid', array(), PARAM_INT)) {
require_capability('mod/quiz:deleteattempts', $this->context); require_capability('mod/quiz:deleteattempts', $this->context);
$this->delete_selected_attempts($quiz, $cm, $attemptids, $allowed, $groupstudents); $this->delete_selected_attempts($quiz, $cm, $attemptids, $allowed);
redirect($reporturl->out(false, $displayoptions)); redirect($reporturl->out(false, $displayoptions));
} }
} else if (optional_param('regrade', 0, PARAM_BOOL) && confirm_sesskey()) {
if ($attemptids = optional_param('attemptid', array(), PARAM_INT)) {
$this->regrade_selected_attempts($quiz, $attemptids, $groupstudents);
redirect($reporturl->out(false, $displayoptions));
}
}
}
//work out the sql for this table. } else if (optional_param('regrade', 0, PARAM_BOOL) && confirm_sesskey()) {
if ($detailedmarks) { if ($attemptids = optional_param('attemptid', array(), PARAM_INT)) {
$questions = quiz_report_load_questions($quiz); require_capability('mod/quiz:regrade', $this->context);
} else { $this->regrade_attempts($quiz, false, $groupstudents, $attemptids);
$questions = array(); redirect($reporturl->out(false, $displayoptions));
}
} }
$table = new quiz_report_overview_table($quiz , $qmsubselect, $groupstudents,
$students, $detailedmarks, $questions, $candelete, $reporturl,
$displayoptions, $this->context);
$table->is_downloading($download, get_string('reportoverview','quiz'),
"$COURSE->shortname ".format_string($quiz->name,true));
if (!$table->is_downloading()) {
// Only print headers if not asked to download data
$this->print_header_and_tabs($cm, $course, $quiz, "overview");
} }
if ($regradeall && confirm_sesskey()) { if ($regradeall && confirm_sesskey()) {
$this->regrade_all(false, $quiz, $groupstudents); require_capability('mod/quiz:regrade', $this->context);
} else if ($regradealldry && confirm_sesskey()) { $this->regrade_attempts($quiz, false, $groupstudents);
$this->regrade_all(true, $quiz, $groupstudents);
} else if ($regradealldrydo && confirm_sesskey()) {
$this->regrade_all_needed($quiz, $groupstudents);
}
if ($regradeall || $regradealldry || $regradealldrydo) {
redirect($reporturl->out(false, $displayoptions), '', 5); redirect($reporturl->out(false, $displayoptions), '', 5);
} else if ($regradealldry && confirm_sesskey()) {
require_capability('mod/quiz:regrade', $this->context);
$this->regrade_attempts($quiz, true, $groupstudents);
redirect($reporturl->out(false, $displayoptions), '', 5);
} else if ($regradealldrydo && confirm_sesskey()) {
require_capability('mod/quiz:regrade', $this->context);
$this->regrade_attempts_needing_it($quiz, $groupstudents);
redirect($reporturl->out(false, $displayoptions), '', 5);
}
// Start output.
if (!$table->is_downloading()) {
// Only print headers if not asked to download data
$this->print_header_and_tabs($cm, $course, $quiz, 'overview');
} }
if ($groupmode = groups_get_activity_groupmode($cm)) { // Groups are being used if ($groupmode = groups_get_activity_groupmode($cm)) { // Groups are being used
@ -176,6 +177,13 @@ class quiz_overview_report extends quiz_default_report {
} }
} }
// Print information on the number of existing attempts
if (!$table->is_downloading()) { //do not print notices when downloading
if ($strattemptnum = quiz_num_attempt_summary($quiz, $cm, true, $currentgroup)) {
echo '<div class="quizattemptcounts">' . $strattemptnum . '</div>';
}
}
$nostudents = false; $nostudents = false;
if (!$students) { if (!$students) {
if (!$table->is_downloading()) { if (!$table->is_downloading()) {
@ -188,21 +196,15 @@ class quiz_overview_report extends quiz_default_report {
} }
$nostudents = true; $nostudents = true;
} }
if (!$table->is_downloading()) { if (!$table->is_downloading()) {
// Print display options // Print display options
$mform->set_data($displayoptions +compact('detailedmarks', 'pagesize'));
$mform->display(); $mform->display();
// Print information on the number of existing attempts
if ($strattemptnum = quiz_num_attempt_summary($quiz, $cm, true, $currentgroup)) {
echo '<div class="quizattemptcounts">' . $strattemptnum . '</div>';
}
} }
if (!$nostudents || ($attemptsmode == QUIZ_REPORT_ATTEMPTS_ALL)) { if (!$nostudents || ($attemptsmode == QUIZ_REPORT_ATTEMPTS_ALL)) {
// Construct the SQL // Construct the SQL
$fields = $DB->sql_concat('u.id', '\'#\'', 'COALESCE(qa.attempt, \'0\')').' AS uniqueid, '; $fields = $DB->sql_concat('u.id', "'#'", 'COALESCE(quiza.attempt, 0)') . ' AS uniqueid, ';
if ($qmsubselect) { if ($qmsubselect) {
$fields .= $fields .=
"(CASE " . "(CASE " .
@ -211,90 +213,44 @@ class quiz_overview_report extends quiz_default_report {
"END) AS gradedattempt, "; "END) AS gradedattempt, ";
} }
$fields .='qa.uniqueid AS attemptuniqueid, qa.id AS attempt, ' . list($fields, $from, $where, $params) =
'u.id AS userid, u.idnumber, u.firstname, u.lastname, u.picture, u.imagealt, u.email, '. $this->base_sql($quiz, $qmsubselect, $qmfilter, $attemptsmode, $allowed);
'qa.sumgrades, qa.timefinish, qa.timestart, qa.timefinish - qa.timestart AS duration ';
// This part is the same for all cases - join users and quiz_attempts tables
$from = '{user} u ';
$from .= 'LEFT JOIN {quiz_attempts} qa ON qa.userid = u.id AND qa.quiz = :quizid';
$params = array('quizid' => $quiz->id);
if ($qmsubselect && $qmfilter) {
$from .= ' AND '.$qmsubselect;
}
switch ($attemptsmode) {
case QUIZ_REPORT_ATTEMPTS_ALL:
// Show all attempts, including students who are no longer in the course
$where = 'qa.id IS NOT NULL AND qa.preview = 0';
break;
case QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH:
// Show only students with attempts
list($allowed_usql, $allowed_params) = $DB->get_in_or_equal($allowed, SQL_PARAMS_NAMED, 'u0000');
$params += $allowed_params;
$where = "u.id $allowed_usql AND qa.preview = 0 AND qa.id IS NOT NULL";
break;
case QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH_NO:
// Show only students without attempts
list($allowed_usql, $allowed_params) = $DB->get_in_or_equal($allowed, SQL_PARAMS_NAMED, 'u0000');
$params += $allowed_params;
$where = "u.id $allowed_usql AND qa.id IS NULL";
break;
case QUIZ_REPORT_ATTEMPTS_ALL_STUDENTS:
// Show all students with or without attempts
list($allowed_usql, $allowed_params) = $DB->get_in_or_equal($allowed, SQL_PARAMS_NAMED, 'u0000');
$params += $allowed_params;
$where = "u.id $allowed_usql AND (qa.preview = 0 OR qa.preview IS NULL)";
break;
}
$table->set_count_sql("SELECT COUNT(1) FROM $from WHERE $where", $params); $table->set_count_sql("SELECT COUNT(1) FROM $from WHERE $where", $params);
$sqlobject = new stdClass(); // Test to see if there are any regraded attempts to be listed.
$sqlobject->from = $from; $fields .= ", COALESCE((SELECT MAX(qqr.regraded) FROM {quiz_overview_regrades} qqr WHERE qqr.questionusageid = quiza.uniqueid), -1) AS regraded";
$sqlobject->where = $where;
$sqlobject->params = $params;
//test to see if there are any regraded attempts to be listed.
if (quiz_get_regraded_qs($sqlobject, 0, 1)) {
$regradedattempts = true;
} else {
$regradedattempts = false;
}
$fields .= ', COALESCE((SELECT MAX(qqr.regraded) FROM {quiz_question_regrade} qqr WHERE qqr.attemptid = qa.uniqueid),-1) AS regraded';
if ($regradefilter) { if ($regradefilter) {
$where .= ' AND COALESCE((SELECT MAX(qqr.regraded) FROM {quiz_question_regrade} qqr WHERE qqr.attemptid = qa.uniqueid),-1) !=\'-1\''; $where .= " AND COALESCE((SELECT MAX(qqr.regraded) FROM {quiz_overview_regrades} qqr WHERE qqr.questionusageid = quiza.uniqueid), -1) <> -1";
} }
$table->set_sql($fields, $from, $where, $params); $table->set_sql($fields, $from, $where, $params);
// Define table columns if (!$table->is_downloading()) {
$columns = array(); // Regrade buttons
$headers = array();
if (!$table->is_downloading()) { //do not print notices when downloading
//regrade buttons
if (has_capability('mod/quiz:regrade', $this->context)) { if (has_capability('mod/quiz:regrade', $this->context)) {
$countregradeneeded = $this->count_regrade_all_needed($quiz, $groupstudents); $regradesneeded = $this->count_question_attempts_needing_regrade(
$quiz, $groupstudents);
if ($currentgroup) { if ($currentgroup) {
$a= new stdClass(); $a= new stdClass();
$a->groupname = groups_get_group_name($currentgroup); $a->groupname = groups_get_group_name($currentgroup);
$a->coursestudents = get_string('participants'); $a->coursestudents = get_string('participants');
$a->countregradeneeded = $countregradeneeded; $a->countregradeneeded = $regradesneeded;
$regradealldrydolabel = get_string('regradealldrydogroup', 'quiz_overview', $a); $regradealldrydolabel = get_string('regradealldrydogroup', 'quiz_overview', $a);
$regradealldrylabel = get_string('regradealldrygroup', 'quiz_overview', $a); $regradealldrylabel = get_string('regradealldrygroup', 'quiz_overview', $a);
$regradealllabel = get_string('regradeallgroup', 'quiz_overview', $a); $regradealllabel = get_string('regradeallgroup', 'quiz_overview', $a);
} else { } else {
$regradealldrydolabel = get_string('regradealldrydo', 'quiz_overview', $countregradeneeded); $regradealldrydolabel = get_string('regradealldrydo', 'quiz_overview', $regradesneeded);
$regradealldrylabel = get_string('regradealldry', 'quiz_overview'); $regradealldrylabel = get_string('regradealldry', 'quiz_overview');
$regradealllabel = get_string('regradeall', 'quiz_overview'); $regradealllabel = get_string('regradeall', 'quiz_overview');
} }
$displayurl = new moodle_url($reporturl, $displayoptions); $displayurl = new moodle_url($reporturl, $displayoptions + array('sesskey' => sesskey()));
echo '<div class="mdl-align">'; echo '<div class="mdl-align">';
echo '<form action="'.$displayurl->out_omit_querystring().'">'; echo '<form action="'.$displayurl->out_omit_querystring().'">';
echo '<div>'; echo '<div>';
echo html_writer::input_hidden_params($displayurl); echo html_writer::input_hidden_params($displayurl);
echo html_writer::empty_tag('input', array('type' => 'hidden', 'name' => 'sesskey', 'value' => sesskey())) . "\n";
echo '<input type="submit" name="regradeall" value="'.$regradealllabel.'"/>'; echo '<input type="submit" name="regradeall" value="'.$regradealllabel.'"/>';
echo '<input type="submit" name="regradealldry" value="'.$regradealldrylabel.'"/>'; echo '<input type="submit" name="regradealldry" value="'.$regradealldrylabel.'"/>';
if ($countregradeneeded) { if ($regradesneeded) {
echo '<input type="submit" name="regradealldrydo" value="'.$regradealldrydolabel.'"/>'; echo '<input type="submit" name="regradealldrydo" value="'.$regradealldrydolabel.'"/>';
} }
echo '</div>'; echo '</div>';
@ -307,90 +263,49 @@ class quiz_overview_report extends quiz_default_report {
} }
} }
// Define table columns
$columns = array();
$headers = array();
if (!$table->is_downloading() && $candelete) { if (!$table->is_downloading() && $candelete) {
$columns[] = 'checkbox'; $columns[] = 'checkbox';
$headers[] = NULL; $headers[] = NULL;
} }
if (!$table->is_downloading() && $CFG->grade_report_showuserimage) { $this->add_user_columns($table, $columns, $headers);
$columns[]= 'picture';
$headers[]= '';
}
if (!$table->is_downloading()) {
$columns[]= 'fullname';
$headers[]= get_string('name');
} else {
$columns[]= 'lastname';
$headers[]= get_string('lastname');
$columns[]= 'firstname';
$headers[]= get_string('firstname');
}
if ($CFG->grade_report_showuseridnumber) { $this->add_time_columns($columns, $headers);
$columns[]= 'idnumber';
$headers[]= get_string('idnumber');
}
$columns[]= 'timestart';
$headers[]= get_string('startedon', 'quiz');
$columns[]= 'timefinish';
$headers[]= get_string('timecompleted','quiz');
$columns[]= 'duration';
$headers[]= get_string('attemptduration', 'quiz');
if ($detailedmarks) { if ($detailedmarks) {
foreach ($questions as $id => $question) { foreach ($questions as $slot => $question) {
// Ignore questions of zero length // Ignore questions of zero length
$columns[] = 'qsgrade'.$id; $columns[] = 'qsgrade' . $slot;
$header = '#'.$question->number; $header = get_string('qbrief', 'quiz', $question->number);
if (!$table->is_downloading()) { if (!$table->is_downloading()) {
$header .= '<br />'; $header .= '<br />';
} else { } else {
$header .= ' '; $header .= ' ';
} }
$header .='--/'.quiz_rescale_grade($question->maxgrade, $quiz, 'question'); $header .= '/' . quiz_rescale_grade($question->maxmark, $quiz, 'question');
$headers[] = $header; $headers[] = $header;
$question->formattedname = strip_tags(format_string($question->name));
} }
} }
if (!$table->is_downloading() && has_capability('mod/quiz:regrade', $this->context) && $regradedattempts) {
if (!$table->is_downloading() && has_capability('mod/quiz:regrade', $this->context) &&
$this->has_regraded_questions($from, $where, $params)) {
$columns[] = 'regraded'; $columns[] = 'regraded';
$headers[] = get_string('regrade', 'quiz_overview'); $headers[] = get_string('regrade', 'quiz_overview');
} }
if ($showgrades) {
$columns[] = 'sumgrades';
$headers[] = get_string('grade', 'quiz').'/'.quiz_format_grade($quiz, $quiz->grade);
}
if ($hasfeedback) { $this->add_grade_columns($quiz, $columns, $headers);
$columns[] = 'feedbacktext';
$headers[] = get_string('feedback', 'quiz');
}
$table->define_columns($columns); $this->set_up_table_columns($table, $columns, $headers, $reporturl, $displayoptions, false);
$table->define_headers($headers); $table->set_attribute('class', 'generaltable generalbox grades');
$table->sortable(true, 'uniqueid');
// Set up the table
$table->define_baseurl($reporturl->out(true, $displayoptions));
$table->collapsible(true);
$table->no_sorting('feedbacktext');
$table->column_class('picture', 'picture');
$table->column_class('lastname', 'bold');
$table->column_class('firstname', 'bold');
$table->column_class('fullname', 'bold');
$table->column_class('sumgrades', 'bold');
$table->set_attribute('id', 'attempts');
$table->out($pagesize, true); $table->out($pagesize, true);
} }
if (!$table->is_downloading() && $showgrades) {
if (!$table->is_downloading() && $this->should_show_grades($quiz)) {
if ($currentgroup && $groupstudents) { if ($currentgroup && $groupstudents) {
list($usql, $params) = $DB->get_in_or_equal($groupstudents); list($usql, $params) = $DB->get_in_or_equal($groupstudents);
$params[] = $quiz->id; $params[] = $quiz->id;
@ -401,6 +316,7 @@ class quiz_overview_report extends quiz_default_report {
echo '<div class="graph"><img src="'.$imageurl.'" alt="'.$graphname.'" /></div>'; echo '<div class="graph"><img src="'.$imageurl.'" alt="'.$graphname.'" /></div>';
} }
} }
if ($DB->record_exists('quiz_grades', array('quiz'=> $quiz->id))) { if ($DB->record_exists('quiz_grades', array('quiz'=> $quiz->id))) {
$graphname = get_string('overviewreportgraph', 'quiz_overview'); $graphname = get_string('overviewreportgraph', 'quiz_overview');
$imageurl = $CFG->wwwroot.'/mod/quiz/report/overview/overviewgraph.php?id='.$quiz->id; $imageurl = $CFG->wwwroot.'/mod/quiz/report/overview/overviewgraph.php?id='.$quiz->id;
@ -410,261 +326,226 @@ class quiz_overview_report extends quiz_default_report {
} }
return true; return true;
} }
/** /**
* @param bool changedb whether to change contents of state and grades * Regrade a particular quiz attempt. Either for real ($dryrun = false), or
* tables. * as a pretend regrade to see which fractions would change. The outcome is
* stored in the quiz_overview_regrades table.
*
* Note, $attempt is not upgraded in the database. The caller needs to do that.
* However, $attempt->sumgrades is updated, if this is not a dry run.
*
* @param object $attempt the quiz attempt to regrade.
* @param boolean $dryrun if true, do a pretend regrade, otherwise do it for real.
* @param array $slots if null, regrade all questions, otherwise, just regrade
* the quetsions with those slots.
*/ */
function regrade_all($dry, $quiz, $groupstudents) { protected function regrade_attempt($attempt, $dryrun = false, $slots = null) {
global $DB, $OUTPUT;
if (!has_capability('mod/quiz:regrade', $this->context)) {
echo $OUTPUT->notification(get_string('regradenotallowed', 'quiz'));
return true;
}
// Fetch all attempts
if ($groupstudents) {
list($usql, $params) = $DB->get_in_or_equal($groupstudents);
$select = "userid $usql AND ";
} else {
$select = '';
$params = array();
}
$select .= "quiz = ? AND preview = 0";
$params[] = $quiz->id;
if (!$attempts = $DB->get_records_select('quiz_attempts', $select, $params)) {
echo $OUTPUT->heading(get_string('noattempts', 'quiz'));
return true;
}
$this->clear_regrade_table($quiz, $groupstudents);
// Fetch all questions
$questions = question_load_questions(explode(',',quiz_questions_in_quiz($quiz->questions)), 'qqi.grade AS maxgrade, qqi.id AS instance',
'{quiz_question_instances} qqi ON qqi.quiz = ' . $quiz->id . ' AND q.id = qqi.question');
// Print heading
echo $OUTPUT->heading(get_string('regradingquiz', 'quiz', format_string($quiz->name)));
$qstodo = count($questions);
$qsdone = 0;
if ($qstodo > 1) {
$qpb = new progress_bar('qregradingbar', 500, true);
$qpb->update($qsdone, $qstodo, "Question $qsdone of $qstodo");
}
$apb = new progress_bar('aregradingbar', 500, true);
// Loop through all questions and all attempts and regrade while printing progress info
$attemptstodo = count($attempts);
foreach ($questions as $question) {
$attemptsdone = 0;
$apb->restart();
echo '<p class="mdl-align"><strong>'.get_string('regradingquestion', 'quiz', $question->name).'</strong></p>';
@flush();@ob_flush();
foreach ($attempts as $attempt) {
set_time_limit(30);
$changed = regrade_question_in_attempt($question, $attempt, $quiz, true, $dry);
$attemptsdone++;
$a = new stdClass();
$a->done = $attemptsdone;
$a->todo = $attemptstodo;
$apb->update($attemptsdone, $attemptstodo, get_string('attemptprogress', 'quiz_overview', $a));
}
$qsdone++;
if (isset($qpb)) {
$a = new stdClass();
$a->done = $qsdone;
$a->todo = $qstodo;
$qpb->update($qsdone, $qstodo, get_string('qprogress', 'quiz_overview', $a));
}
// the following makes sure that the output is sent immediately.
@flush();@ob_flush();
}
if (!$dry) {
$this->check_overall_grades($quiz, $groupstudents);
}
}
function count_regrade_all_needed($quiz, $groupstudents) {
global $DB; global $DB;
// Fetch all attempts that need regrading
$transaction = $DB->start_delegated_transaction();
$quba = question_engine::load_questions_usage_by_activity($attempt->uniqueid);
if (is_null($slots)) {
$slots = $quba->get_slots();
}
$finished = $attempt->timefinish > 0;
foreach ($slots as $slot) {
$qqr = new stdClass;
$qqr->oldfraction = $quba->get_question_fraction($slot);
$quba->regrade_question($slot, $finished);
$qqr->newfraction = $quba->get_question_fraction($slot);
if (abs($qqr->oldfraction - $qqr->newfraction) > 1e-7) {
$qqr->questionusageid = $quba->get_id();
$qqr->slot = $slot;
$qqr->regraded = empty($dryrun);
$qqr->timemodified = time();
$DB->insert_record('quiz_overview_regrades', $qqr, false);
}
}
if (!$dryrun) {
question_engine::save_questions_usage_by_activity($quba);
}
$transaction->allow_commit();
}
/**
* Regrade attempts for this quiz, exactly which attempts are regraded is
* controlled by the parameters.
* @param object $quiz the quiz settings.
* @param boolean $dryrun if true, do a pretend regrade, otherwise do it for real.
* @param array $groupstudents blank for all attempts, otherwise regrade attempts
* for these users.
* @param array $attemptids blank for all attempts, otherwise only regrade
* attempts whose id is in this list.
*/
protected function regrade_attempts($quiz, $dryrun = false,
$groupstudents = array(), $attemptids = array()) {
global $DB;
$where = "quiz = ? AND preview = 0";
$params = array($quiz->id);
if ($groupstudents) { if ($groupstudents) {
list($usql, $params) = $DB->get_in_or_equal($groupstudents); list($usql, $uparams) = $DB->get_in_or_equal($groupstudents);
$where = "qa.userid $usql AND "; $where .= " AND userid $usql";
} else { $params = array_merge($params, $uparams);
$where = '';
$params = array();
} }
$where .= "qa.quiz = ? AND qa.preview = 0 AND qa.uniqueid = qqr.attemptid AND qqr.regraded = 0";
$params[] = $quiz->id; if ($attemptids) {
return $DB->get_field_sql('SELECT COUNT(1) FROM {quiz_attempts} qa, {quiz_question_regrade} qqr WHERE '. $where, $params); list($asql, $aparams) = $DB->get_in_or_equal($attemptids);
$where .= " AND id $asql";
$params = array_merge($params, $aparams);
} }
function regrade_all_needed($quiz, $groupstudents) {
global $DB, $OUTPUT; $attempts = $DB->get_records_select('quiz_attempts', $where, $params);
if (!has_capability('mod/quiz:regrade', $this->context)) { if (!$attempts) {
echo $OUTPUT->notification(get_string('regradenotallowed', 'quiz'));
return; return;
} }
$this->clear_regrade_table($quiz, $groupstudents);
foreach ($attempts as $attempt) {
set_time_limit(30);
$this->regrade_attempt($attempt, $dryrun);
}
if (!$dryrun) {
$this->update_overall_grades($quiz);
}
}
/**
* Regrade those questions in those attempts that are marked as needing regrading
* in the quiz_overview_regrades table.
* @param object $quiz the quiz settings.
* @param array $groupstudents blank for all attempts, otherwise regrade attempts
* for these users.
*/
protected function regrade_attempts_needing_it($quiz, $groupstudents) {
global $DB;
$where = "quiza.quiz = ? AND quiza.preview = 0 AND qqr.regraded = 0";
$params = array($quiz->id);
// Fetch all attempts that need regrading // Fetch all attempts that need regrading
if ($groupstudents) { if ($groupstudents) {
list($usql, $params) = $DB->get_in_or_equal($groupstudents); list($usql, $uparams) = $DB->get_in_or_equal($groupstudents);
$where = "qa.userid $usql AND "; $where .= " AND quiza.userid $usql";
} else { $params += $uparams;
}
$toregrade = $DB->get_records_sql("
SELECT quiza.uniqueid, qqr.slot
FROM {quiz_attempts} quiza
JOIN {quiz_overview_regrades} qqr ON qqr.questionusageid = quiza.uniqueid
WHERE $where", $params);
if (!$toregrade) {
return;
}
$attemptquestions = array();
foreach ($toregrade as $row) {
$attemptquestions[$row->uniqueid][] = $row->slot;
}
$attempts = $DB->get_records_list('quiz_attempts', 'uniqueid', array_keys($attemptquestions));
$this->clear_regrade_table($quiz, $groupstudents);
foreach ($attempts as $attempt) {
set_time_limit(30);
$this->regrade_attempt($attempt, false, $attemptquestions[$attempt->uniqueid]);
}
$this->update_overall_grades($quiz);
}
/**
* Count the number of attempts in need of a regrade.
* @param object $quiz the quiz settings.
* @param array $groupstudents user ids. If this is given, only data relating
* to these users is cleared.
*/
protected function count_question_attempts_needing_regrade($quiz, $groupstudents) {
global $DB;
$usertest = '';
$params = array();
if ($groupstudents) {
list($usql, $params) = get_in_or_equal($groupstudents);
$usertest = "quiza.userid $usql AND ";
}
$params[] = $quiz->id;
$sql = "SELECT COUNT(DISTINCT quiza.id)
FROM {quiz_attempts} quiza
JOIN {quiz_overview_regrades} qqr ON quiza.uniqueid = qqr.questionusageid
WHERE
$usertest
quiza.quiz = ? AND
quiza.preview = 0 AND
qqr.regraded = 0";
return $DB->count_records_sql($sql, $params);
}
/**
* Are there any pending regrades in the table we are going to show?
* @param string $from tables used by the main query.
* @param string $where where clause used by the main query.
* @param array $params required by the SQL.
* @return bool whether there are pending regrades.
*/
protected function has_regraded_questions($from, $where, $params) {
global $DB;
$qubaids = new qubaid_join($from, 'uniqueid', $where, $params);
return $DB->record_exists_select('quiz_overview_regrades',
'questionusageid ' . $qubaids->usage_id_in(), $qubaids->usage_id_in_params());
}
/**
* Remove all information about pending/complete regrades from the database.
* @param object $quiz the quiz settings.
* @param array $groupstudents user ids. If this is given, only data relating
* to these users is cleared.
*/
protected function clear_regrade_table($quiz, $groupstudents) {
global $DB;
// Fetch all attempts that need regrading
$where = ''; $where = '';
$params = array(); $params = array();
}
$where .= "qa.quiz = ? AND qa.preview = 0 AND qa.uniqueid = qqr.attemptid AND qqr.regraded = 0";
$params[] = $quiz->id;
if (!$attempts = $DB->get_records_sql('SELECT qa.*, qqr.questionid FROM {quiz_attempts} qa, {quiz_question_regrade} qqr WHERE '. $where, $params)) {
echo $OUTPUT->heading(get_string('noattemptstoregrade', 'quiz_overview'));
return true;
}
$this->clear_regrade_table($quiz, $groupstudents);
// Fetch all questions
$questions = question_load_questions(explode(',',quiz_questions_in_quiz($quiz->questions)), 'qqi.grade AS maxgrade, qqi.id AS instance',
'{quiz_question_instances} qqi ON qqi.quiz = ' . $quiz->id . ' AND q.id = qqi.question');
// Print heading
echo $OUTPUT->heading(get_string('regradingquiz', 'quiz', format_string($quiz->name)));
$apb = new progress_bar('aregradingbar', 500, true);
// Loop through all questions and all attempts and regrade while printing progress info
$attemptstodo = count($attempts);
$attemptsdone = 0;
@flush();@ob_flush();
$attemptschanged = array();
foreach ($attempts as $attempt) {
$question = $questions[$attempt->questionid];
$changed = regrade_question_in_attempt($question, $attempt, $quiz, true);
if ($changed) {
$attemptschanged[] = $attempt->uniqueid;
$usersschanged[] = $attempt->userid;
}
if (!empty($apb)) {
$attemptsdone++;
$a = new stdClass();
$a->done = $attemptsdone;
$a->todo = $attemptstodo;
$apb->update($attemptsdone, $attemptstodo, get_string('attemptprogress', 'quiz_overview', $a));
}
}
$this->check_overall_grades($quiz, array(), $attemptschanged);
}
function clear_regrade_table($quiz, $groupstudents) {
global $DB;
// Fetch all attempts that need regrading
if ($groupstudents) { if ($groupstudents) {
list($usql, $params) = $DB->get_in_or_equal($groupstudents); list($usql, $params) = $DB->get_in_or_equal($groupstudents);
$where = "userid $usql AND "; $where = "userid $usql AND ";
} else {
$usql = '';
$where = '';
$params = array();
} }
$params[] = $quiz->id; $params[] = $quiz->id;
$delsql = 'DELETE FROM {quiz_question_regrade} WHERE attemptid IN $DB->delete_records_select('quiz_overview_regrades',
(SELECT uniqueid FROM {quiz_attempts} WHERE ' . $where . ' quiz = ?)'; "questionusageid IN (
if (!$DB->execute($delsql, $params)) { SELECT uniqueid
print_error('err_failedtodeleteregrades', 'quiz_overview'); FROM {quiz_attempts}
} WHERE $where quiz = ?
)", $params);
} }
function check_overall_grades($quiz, $userids=array(), $attemptids=array()) { /**
global $DB; * Update the final grades for all attempts. This method is used following
//recalculate $attempt->sumgrade * a regrade.
//already updated in regrade_question_in_attempt * @param object $quiz the quiz settings.
$sql = "UPDATE {quiz_attempts} SET sumgrades= " . * @param array $userids only update scores for these userids.
"COALESCE((SELECT SUM(qs.grade) FROM {question_sessions} qns, {question_states} qs " . * @param array $attemptids attemptids only update scores for these attempt ids.
"WHERE qns.newgraded = qs.id AND qns.attemptid = {quiz_attempts}.uniqueid ), 0) WHERE "; */
$attemptsql=''; protected function update_overall_grades($quiz) {
if (!$attemptids) { quiz_update_all_attempt_sumgrades($quiz);
if ($userids) { quiz_update_all_final_grades($quiz);
list($usql, $params) = $DB->get_in_or_equal($userids); quiz_update_grades($quiz);
$attemptsql .= "{quiz_attempts}.userid $usql AND ";
} else {
$params = array();
}
$attemptsql .= "{quiz_attempts}.quiz =? AND preview = 0";
$params[] = $quiz->id;
} else {
list($asql, $params) = $DB->get_in_or_equal($attemptids);
$attemptsql .= "{quiz_attempts}.uniqueid $asql";
}
$sql .= $attemptsql;
if (!$DB->execute($sql, $params)) {
print_error('err_failedtorecalculateattemptgrades', 'quiz_overview');
}
// Update the overall quiz grades
if ($attemptids) {
//make sure we fetch all attempts for users to calculate grade.
//not just those that have changed.
$sql = "SELECT qa2.* FROM {quiz_attempts} qa2 WHERE " .
"qa2.userid IN (SELECT DISTINCT userid FROM {quiz_attempts} WHERE $attemptsql) " .
"AND qa2.timefinish > 0";
} else {
$sql = "SELECT * FROM {quiz_attempts} WHERE $attemptsql AND timefinish > 0";
}
if ($attempts = $DB->get_records_sql($sql, $params)) {
$attemptsbyuser = quiz_report_index_by_keys($attempts, array('userid', 'id'));
foreach($attemptsbyuser as $userid => $attemptsforuser) {
quiz_save_best_grade($quiz, $userid, $attemptsforuser);
}
}
}
function delete_selected_attempts($quiz, $cm, $attemptids, $allowed, $groupstudents) {
global $DB, $COURSE;
foreach($attemptids as $attemptid) {
$attempt = $DB->get_record('quiz_attempts', array('id' => $attemptid));
if (!$attempt || $attempt->quiz != $quiz->id || $attempt->preview != 0) {
// Ensure the attempt exists, and belongs to this quiz. If not skip.
continue;
}
if ($allowed && !array_key_exists($attempt->userid, $allowed)) {
// Ensure the attempt belongs to a student included in the report. If not skip.
continue;
}
if ($groupstudents && !array_key_exists($attempt->userid, $groupstudents)) {
// Additional check in groups mode.
continue;
}
add_to_log($COURSE->id, 'quiz', 'delete attempt', 'report.php?id=' . $cm->id,
$attemptid, $cm->id);
quiz_delete_attempt($attempt, $quiz);
}
}
function regrade_selected_attempts($quiz, $attemptids, $groupstudents) {
global $DB;
require_capability('mod/quiz:regrade', $this->context);
if ($groupstudents) {
list($usql, $params) = $DB->get_in_or_equal($groupstudents);
$where = "qa.userid $usql AND ";
} else {
$params = array();
$where = '';
}
list($asql, $aparams) = $DB->get_in_or_equal($attemptids);
$where = "qa.id $asql AND ";
$params = array_merge($params, $aparams);
$where .= "qa.quiz = ? AND qa.preview = 0";
$params[] = $quiz->id;
if (!$attempts = $DB->get_records_sql('SELECT qa.* FROM {quiz_attempts} qa WHERE '. $where, $params)) {
print_error('noattemptstoregrade', 'quiz_overview');
}
// Fetch all questions
$questions = question_load_questions(explode(',',quiz_questions_in_quiz($quiz->questions)), 'qqi.grade AS maxgrade, qqi.id AS instance',
'{quiz_question_instances} qqi ON qqi.quiz = ' . $quiz->id . ' AND q.id = qqi.question');
$updateoverallgrades = array();
foreach($attempts as $attempt) {
foreach ($questions as $question) {
$changed = regrade_question_in_attempt($question, $attempt, $quiz, true);
}
$updateoverallgrades[] = $attempt->uniqueid;
}
$this->check_overall_grades($quiz, array(), $updateoverallgrades);
} }
} }

View file

@ -1,10 +1,27 @@
<?php <?php
//////////////////////////////////////////////////////////////////////////////// // This file is part of Moodle - http://moodle.org/
// Code fragment to define the version of quiz overview report //
// This fragment is called by moodle_needs_upgrading() and /admin/index.php // Moodle is free software: you can redistribute it and/or modify
//////////////////////////////////////////////////////////////////////////////// // it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
$plugin->version = 2009091400; // The (date) version of this module // (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Quiz overview report version information.
*
* @package quiz
* @subpackage overview
* @copyright 2008 Jamie Pratt
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$plugin->version = 2011021600; // The (date) version of this module

View file

@ -69,6 +69,7 @@ class quiz_report_responses_table extends quiz_attempt_report_table {
return; return;
} }
// TODO add back are you sure, and convert to html_writer.
echo '<div id="commands">'; echo '<div id="commands">';
echo '<a href="javascript:select_all_in(\'DIV\',null,\'tablecontainer\');">'. echo '<a href="javascript:select_all_in(\'DIV\',null,\'tablecontainer\');">'.
get_string('selectall', 'quiz').'</a> / '; get_string('selectall', 'quiz').'</a> / ';

View file

@ -177,7 +177,10 @@ class quiz_statistics_question_stats {
$this->initial_question_walker($subquestion->_stats); $this->initial_question_walker($subquestion->_stats);
if ($subquestionstats[$qid]->differentweights) { if ($subquestionstats[$qid]->differentweights) {
notify(get_string('erroritemappearsmorethanoncewithdifferentweight', 'quiz_statistics', $this->subquestions[$qid]->name)); // TODO output here really sucks, but throwing is too severe.
global $OUTPUT;
echo $OUTPUT->notification(get_string('erroritemappearsmorethanoncewithdifferentweight',
'quiz_statistics', $this->subquestions[$qid]->name));
} }
if ($subquestion->_stats->usedin) { if ($subquestion->_stats->usedin) {

View file

@ -540,7 +540,7 @@ class quiz_statistics_report extends quiz_default_report {
$OUTPUT->heading(get_string('statisticsreportgraph', 'quiz_statistics')); $OUTPUT->heading(get_string('statisticsreportgraph', 'quiz_statistics'));
echo html_writer::tag('div', html_writer::empty_tag('img', echo html_writer::tag('div', html_writer::empty_tag('img',
array('src' => $imageurl, 'alt' => get_string('statisticsreportgraph', 'quiz_statistics'))), array('src' => $imageurl, 'alt' => get_string('statisticsreportgraph', 'quiz_statistics'))),
array('class' => 'mdl-align')); array('class' => 'graph'));
} }
/** /**

View file

@ -476,13 +476,14 @@ $sqlorderby
*/ */
public function load_average_marks(qubaid_condition $qubaids, $slots = null) { public function load_average_marks(qubaid_condition $qubaids, $slots = null) {
if (!empty($slots)) { if (!empty($slots)) {
list($slottest, $params) = get_in_or_equal($slots, SQL_PARAMS_NAMED, 'slot0000'); list($slottest, $slotsparams) = $this->db->get_in_or_equal($slots, SQL_PARAMS_NAMED, 'slot0000');
$slotwhere = " AND qa.slot $slottest"; $slotwhere = " AND qa.slot $slottest";
} else { } else {
$slotwhere = ''; $slotwhere = '';
$params = array();
} }
list($statetest) = get_in_or_equal(array( list($statetest, $stateparams) = $this->db->get_in_or_equal(array(
question_state::$gaveup, question_state::$gaveup,
question_state::$gradedwrong, question_state::$gradedwrong,
question_state::$gradedpartial, question_state::$gradedpartial,
@ -490,9 +491,9 @@ $sqlorderby
question_state::$mangaveup, question_state::$mangaveup,
question_state::$mangrwrong, question_state::$mangrwrong,
question_state::$mangrpartial, question_state::$mangrpartial,
question_state::$mangrright)); question_state::$mangrright), SQL_PARAMS_NAMED, 'st00');
$records = get_records_sql(" return $this->db->get_records_sql("
SELECT SELECT
qa.slot, qa.slot,
AVG(COALESCE(qas.fraction, 0)) AS averagefraction, AVG(COALESCE(qas.fraction, 0)) AS averagefraction,
@ -510,9 +511,7 @@ WHERE
GROUP BY qa.slot GROUP BY qa.slot
ORDER BY qa.slot ORDER BY qa.slot
"); ", $slotsparams + $stateparams + $qubaids->from_where_params());
return $records;
} }
/** /**
@ -659,7 +658,7 @@ ORDER BY
if (empty($qaids)) { if (empty($qaids)) {
return; return;
} }
list($test, $params) = get_in_or_equal($qaids); list($test, $params) = $this->db->get_in_or_equal($qaids);
$this->db->delete_records_select('question_attempt_step_data', "attemptstepid IN ( $this->db->delete_records_select('question_attempt_step_data', "attemptstepid IN (
SELECT qas.id SELECT qas.id
FROM {question_attempt_steps} qas FROM {question_attempt_steps} qas
@ -1060,6 +1059,7 @@ class qubaid_join extends qubaid_condition {
* @param string $usageidcolumn the column in $from that should be * @param string $usageidcolumn the column in $from that should be
* made equal to the usageid column in the JOIN clause. * made equal to the usageid column in the JOIN clause.
* @param string $where SQL fragment to go in the where clause. * @param string $where SQL fragment to go in the where clause.
* @param array $params required by the SQL. You must use named parameters.
*/ */
public function __construct($from, $usageidcolumn, $where = '', $params = array()) { public function __construct($from, $usageidcolumn, $where = '', $params = array()) {
$this->from = $from; $this->from = $from;

View file

@ -27,7 +27,7 @@ DONE lib/simpletest/testsimpletestlib.php | 116 + Already in 2.0
DONE lib/simpletest/testweblib.php | 4 + Useful new test. See MDL-24060. DONE lib/simpletest/testweblib.php | 4 + Useful new test. See MDL-24060.
DONE lib/simpletestlib.php | 290 ++ Useful new assertions. DONE lib/simpletestlib.php | 290 ++ Useful new assertions.
DONE lib/simpletestlib/dumper.php | 3 + Not needed. KILL DONE lib/simpletestlib/dumper.php | 3 + Not needed. KILL
lib/tablelib.php | 1224 +++++++-- TODO. See MDL-25716 DONE lib/tablelib.php | 1224 +++++++-- TODO. See MDL-25716
DONE lib/weblib.php | 10 +- Improve html_to_text. See MDL-24060. DONE lib/weblib.php | 10 +- Improve html_to_text. See MDL-24060.
DONE lib/xmlize.php | 74 +- In 2.0 DONE lib/xmlize.php | 74 +- In 2.0
DONE mod/assignment/lib.php | 2 +- TODO MDL-25716 DONE mod/assignment/lib.php | 2 +- TODO MDL-25716
@ -129,15 +129,15 @@ DONE mod/quiz/report/grading/report.php | 842 ++++---
DONE mod/quiz/report/grading/version.php | 26 + DONE mod/quiz/report/grading/version.php | 26 +
DONE lang/en_utf8/quiz_grading.php | 18 - DONE lang/en_utf8/quiz_grading.php | 18 -
mod/quiz/report/overview/db/install.xml | 22 + DONE mod/quiz/report/overview/db/install.xml | 22 +
mod/quiz/report/overview/db/upgrade.php | 152 + DONE mod/quiz/report/overview/db/upgrade.php | 152 +
mod/quiz/report/overview/lang/en_utf8/quiz_overview.php | 54 + DONE mod/quiz/report/overview/lang/en_utf8/quiz_overview.php | 54 +
mod/quiz/report/overview/overview_table.php | 321 +++ DONE mod/quiz/report/overview/overview_table.php | 321 +++
mod/quiz/report/overview/overviewgraph.php | 168 +- DONE mod/quiz/report/overview/overviewgraph.php | 168 +-
mod/quiz/report/overview/overviewsettings_form.php | 69 +- DONE mod/quiz/report/overview/overviewsettings_form.php | 69 +-
mod/quiz/report/overview/report.php | 999 +++---- DONE mod/quiz/report/overview/report.php | 999 +++----
mod/quiz/report/overview/version.php | 26 + DONE mod/quiz/report/overview/version.php | 26 +
lang/en_utf8/quiz_overview.php | 30 - DONE lang/en_utf8/quiz_overview.php | 30 -
DONE mod/quiz/report/responses/lang/en_utf8/quiz_responses.php | 19 + DONE mod/quiz/report/responses/lang/en_utf8/quiz_responses.php | 19 +
DONE mod/quiz/report/responses/report.php | 242 ++ DONE mod/quiz/report/responses/report.php | 242 ++