mirror of
https://github.com/moodle/moodle.git
synced 2025-08-04 08:26:37 +02:00
MDL-20636 Convert the overview report.
This commit is contained in:
parent
2a3bdbf98e
commit
e24ee794b2
14 changed files with 959 additions and 845 deletions
|
@ -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 '-';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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}\'';
|
||||||
|
|
|
@ -1,170 +1,181 @@
|
||||||
<?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()) {
|
||||||
$namekey = 'lastname';
|
$namekey = 'lastname';
|
||||||
} else {
|
} else {
|
||||||
$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);
|
||||||
$this->add_data_keyed($overallaveragerow);
|
$overallaveragerow += $this->format_average_grade_for_questions($avggradebyq);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
function wrap_html_start(){
|
|
||||||
if (!$this->is_downloading()) {
|
|
||||||
if ($this->candelete) {
|
|
||||||
// Start form
|
|
||||||
$url = new moodle_url($this->reporturl, $this->displayoptions);
|
|
||||||
echo '<div id="tablecontainer" class="overview-tablecontainer">';
|
|
||||||
echo '<form id="attemptsform" method="post" action="' . $this->reporturl->out_omit_querystring() .'">';
|
|
||||||
echo '<div style="display: none;">';
|
|
||||||
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>';
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
function wrap_html_finish(){
|
|
||||||
if (!$this->is_downloading()) {
|
|
||||||
// Print "Select all" etc.
|
|
||||||
if ($this->candelete) {
|
|
||||||
$strreallydel = addslashes_js(get_string('deleteattemptcheck','quiz'));
|
|
||||||
echo '<div id="commands">';
|
|
||||||
echo '<a href="javascript:select_all_in(\'DIV\',null,\'tablecontainer\');">'.
|
|
||||||
get_string('selectall', 'quiz').'</a> / ';
|
|
||||||
echo '<a href="javascript:deselect_all_in(\'DIV\',null,\'tablecontainer\');">'.
|
|
||||||
get_string('selectnone', 'quiz').'</a> ';
|
|
||||||
echo ' ';
|
|
||||||
if (has_capability('mod/quiz:regrade', $this->context)){
|
|
||||||
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').'"/>';
|
$this->add_data_keyed($overallaveragerow);
|
||||||
echo '</div>';
|
|
||||||
// Close form
|
|
||||||
echo '</div>';
|
|
||||||
echo '</form></div>';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
function col_checkbox($attempt){
|
/**
|
||||||
if ($attempt->attempt){
|
* Format an entry in an average row.
|
||||||
return '<input type="checkbox" name="attemptid[]" value="'.$attempt->attempt.'" />';
|
* @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 {
|
} else {
|
||||||
return '';
|
$average = quiz_format_grade($this->quiz, $record->grade);
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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.'&attempt='.$attempt->attempt.
|
if ($this->download) {
|
||||||
'">'.get_string('reviewattempt', 'quiz').'</a>';
|
return $average;
|
||||||
}
|
} else if (is_null($record->numaveraged)) {
|
||||||
|
return html_writer::tag('span', html_writer::tag('span',
|
||||||
function col_timestart($attempt){
|
$average, array('class' => 'average')), array('class' => 'avgcell'));
|
||||||
if ($attempt->attempt) {
|
|
||||||
return userdate($attempt->timestart, $this->strtimeformat);
|
|
||||||
} else {
|
} else {
|
||||||
return '-';
|
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'));
|
||||||
function col_timefinish($attempt){
|
|
||||||
if ($attempt->attempt && $attempt->timefinish) {
|
|
||||||
return userdate($attempt->timefinish, $this->strtimeformat);
|
|
||||||
} else {
|
|
||||||
return '-';
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function col_duration($attempt){
|
public function wrap_html_start() {
|
||||||
if ($attempt->timefinish) {
|
if ($this->is_downloading() || !$this->candelete) {
|
||||||
return format_time($attempt->timefinish - $attempt->timestart);
|
return;
|
||||||
} elseif ($attempt->timestart) {
|
|
||||||
return get_string('unfinished', 'quiz');
|
|
||||||
} else {
|
|
||||||
return '-';
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Start form
|
||||||
|
$url = new moodle_url($this->reporturl, $this->displayoptions + array('sesskey' => sesskey()));
|
||||||
|
echo '<div id="tablecontainer" class="overview-tablecontainer">';
|
||||||
|
echo '<form id="attemptsform" method="post" action="' . $this->reporturl->out_omit_querystring() . '">';
|
||||||
|
echo '<div style="display: none;">';
|
||||||
|
echo html_writer::input_hidden_params($url);
|
||||||
|
echo '</div>';
|
||||||
|
echo '<div>';
|
||||||
}
|
}
|
||||||
|
|
||||||
function col_sumgrades($attempt){
|
public function wrap_html_finish() {
|
||||||
|
if ($this->is_downloading() || !$this->candelete) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO add back are you sure, and convert to html_writer.
|
||||||
|
echo '<div id="commands">';
|
||||||
|
echo '<a href="javascript:select_all_in(\'DIV\',null,\'tablecontainer\');">'.
|
||||||
|
get_string('selectall', 'quiz').'</a> / ';
|
||||||
|
echo '<a href="javascript:deselect_all_in(\'DIV\',null,\'tablecontainer\');">'.
|
||||||
|
get_string('selectnone', 'quiz').'</a> ';
|
||||||
|
echo ' ';
|
||||||
|
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="delete" value="'.get_string('deleteselected', 'quiz_overview').'"/>';
|
||||||
|
echo '</div>';
|
||||||
|
// Close form
|
||||||
|
echo '</div>';
|
||||||
|
echo '</form></div>';
|
||||||
|
}
|
||||||
|
|
||||||
|
public 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.'&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 = '-';
|
||||||
|
}
|
||||||
|
} 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);
|
||||||
}
|
}
|
||||||
function col_regraded($attempt){
|
|
||||||
|
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.
|
protected function is_latest_step_column($column) {
|
||||||
// So we join just the tables needed to sort the attempts.
|
if (preg_match('/^qsgrade([0-9]+)/', $column, $matches)) {
|
||||||
if($sort = $this->get_sql_sort()) {
|
return $matches[1];
|
||||||
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]);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
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'));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -1,36 +1,63 @@
|
||||||
<?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
|
|
||||||
$groups = groups_get_activity_allowed_groups($cm);
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
$groups = false;
|
|
||||||
$group = false;
|
|
||||||
$groupusers = array();
|
|
||||||
}
|
|
||||||
require_capability('mod/quiz:viewreports', $modcontext);
|
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);
|
||||||
|
if (!array_key_exists($groupid, $groups)) {
|
||||||
|
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 {
|
||||||
|
$groupusers = array();
|
||||||
|
}
|
||||||
|
|
||||||
$line = new graph(800,600);
|
$line = new graph(800,600);
|
||||||
$line->parameter['title'] = '';
|
$line->parameter['title'] = '';
|
||||||
$line->parameter['y_label_left'] = get_string('participants');
|
$line->parameter['y_label_left'] = get_string('participants');
|
||||||
$line->parameter['x_label'] = get_string('grade');
|
$line->parameter['x_label'] = get_string('grade');
|
||||||
$line->parameter['y_label_angle'] = 90;
|
$line->parameter['y_label_angle'] = 90;
|
||||||
|
@ -41,59 +68,57 @@ $line->parameter['x_axis_angle'] = 60;
|
||||||
$line->y_tick_labels = null;
|
$line->y_tick_labels = null;
|
||||||
$line->offset_relation = null;
|
$line->offset_relation = null;
|
||||||
|
|
||||||
$line->parameter['bar_size'] = 1; // will make size > 1 to get overlap effect when showing groups
|
$line->parameter['bar_size'] = 1; // will make size > 1 to get overlap effect when showing groups
|
||||||
$line->parameter['bar_spacing'] = 10; // don't forget to increase spacing so that graph doesn't become one big block of colour
|
$line->parameter['bar_spacing'] = 10; // don't forget to increase spacing so that graph doesn't become one big block of colour
|
||||||
|
|
||||||
//pick a sensible number of bands depending on quiz maximum grade.
|
//pick a sensible number of bands depending on quiz maximum grade.
|
||||||
$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();
|
||||||
|
|
||||||
|
|
|
@ -1,27 +1,60 @@
|
||||||
<?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']) {
|
||||||
$studentsstring = get_string('participants');
|
$studentsstring = get_string('participants');
|
||||||
} else {
|
} else {
|
||||||
$a = new stdClass();
|
$a = new stdClass();
|
||||||
$a->coursestudent = get_string('participants');
|
$a->coursestudent = get_string('participants');
|
||||||
$a->groupname = groups_get_group_name($this->_customdata['currentgroup']);
|
$a->groupname = groups_get_group_name($this->_customdata['currentgroup']);
|
||||||
if (20 < strlen($a->groupname)){
|
if (20 < strlen($a->groupname)) {
|
||||||
$studentsstring = get_string('studentingrouplong', 'quiz_overview', $a);
|
$studentsstring = get_string('studentingrouplong', 'quiz_overview', $a);
|
||||||
} else {
|
} else {
|
||||||
$studentsstring = get_string('studentingroup', 'quiz_overview', $a);
|
$studentsstring = get_string('studentingroup', 'quiz_overview', $a);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
$options = array();
|
$options = array();
|
||||||
if (!$this->_customdata['currentgroup']){
|
if (!$this->_customdata['currentgroup']) {
|
||||||
$options[QUIZ_REPORT_ATTEMPTS_ALL] = get_string('optallattempts','quiz_overview');
|
$options[QUIZ_REPORT_ATTEMPTS_ALL] = get_string('optallattempts','quiz_overview');
|
||||||
}
|
}
|
||||||
if ($this->_customdata['currentgroup'] || $COURSE->id != SITEID) {
|
if ($this->_customdata['currentgroup'] || $COURSE->id != SITEID) {
|
||||||
|
@ -33,17 +66,17 @@ class mod_quiz_report_overview_settings extends moodleform {
|
||||||
$mform->addElement('select', 'attemptsmode', get_string('show', 'quiz_overview'), $options);
|
$mform->addElement('select', 'attemptsmode', get_string('show', 'quiz_overview'), $options);
|
||||||
|
|
||||||
$showattemptsgrp = array();
|
$showattemptsgrp = array();
|
||||||
if ($this->_customdata['qmsubselect']){
|
if ($this->_customdata['qmsubselect']) {
|
||||||
$gm = '<span class="highlight">'.quiz_get_grading_option_name($this->_customdata['quiz']->grademethod).'</span>';
|
$gm = '<span class="highlight">'.quiz_get_grading_option_name($this->_customdata['quiz']->grademethod).'</span>';
|
||||||
$showattemptsgrp[] =& $mform->createElement('advcheckbox', 'qmfilter', get_string('showattempts', 'quiz_overview'), get_string('optonlygradedattempts', 'quiz_overview', $gm), null, array(0,1));
|
$showattemptsgrp[] =& $mform->createElement('advcheckbox', 'qmfilter', get_string('showattempts', 'quiz_overview'), get_string('optonlygradedattempts', 'quiz_overview', $gm), null, array(0,1));
|
||||||
}
|
}
|
||||||
if (has_capability('mod/quiz:regrade', $this->_customdata['context'])){
|
if (has_capability('mod/quiz:regrade', $this->_customdata['context'])) {
|
||||||
$showattemptsgrp[] =& $mform->createElement('advcheckbox', 'regradefilter', get_string('showattempts', 'quiz_overview'), get_string('optonlyregradedattempts', 'quiz_overview'), null, array(0,1));
|
$showattemptsgrp[] =& $mform->createElement('advcheckbox', 'regradefilter', get_string('showattempts', 'quiz_overview'), get_string('optonlyregradedattempts', 'quiz_overview'), null, array(0,1));
|
||||||
}
|
}
|
||||||
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'));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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.
|
* @package quiz
|
||||||
* @license http://www.gnu.org/copyleft/gpl.html GNU Public License
|
* @subpackage overview
|
||||||
* @package quiz
|
* @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()) {
|
} else if (optional_param('regrade', 0, PARAM_BOOL) && confirm_sesskey()) {
|
||||||
if ($attemptids = optional_param('attemptid', array(), PARAM_INT)) {
|
if ($attemptids = optional_param('attemptid', array(), PARAM_INT)) {
|
||||||
$this->regrade_selected_attempts($quiz, $attemptids, $groupstudents);
|
require_capability('mod/quiz:regrade', $this->context);
|
||||||
|
$this->regrade_attempts($quiz, false, $groupstudents, $attemptids);
|
||||||
redirect($reporturl->out(false, $displayoptions));
|
redirect($reporturl->out(false, $displayoptions));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//work out the sql for this table.
|
if ($regradeall && confirm_sesskey()) {
|
||||||
if ($detailedmarks) {
|
require_capability('mod/quiz:regrade', $this->context);
|
||||||
$questions = quiz_report_load_questions($quiz);
|
$this->regrade_attempts($quiz, false, $groupstudents);
|
||||||
} else {
|
redirect($reporturl->out(false, $displayoptions), '', 5);
|
||||||
$questions = array();
|
|
||||||
}
|
} else if ($regradealldry && confirm_sesskey()) {
|
||||||
$table = new quiz_report_overview_table($quiz , $qmsubselect, $groupstudents,
|
require_capability('mod/quiz:regrade', $this->context);
|
||||||
$students, $detailedmarks, $questions, $candelete, $reporturl,
|
$this->regrade_attempts($quiz, true, $groupstudents);
|
||||||
$displayoptions, $this->context);
|
redirect($reporturl->out(false, $displayoptions), '', 5);
|
||||||
$table->is_downloading($download, get_string('reportoverview','quiz'),
|
|
||||||
"$COURSE->shortname ".format_string($quiz->name,true));
|
} else if ($regradealldrydo && confirm_sesskey()) {
|
||||||
if (!$table->is_downloading()) {
|
require_capability('mod/quiz:regrade', $this->context);
|
||||||
// Only print headers if not asked to download data
|
$this->regrade_attempts_needing_it($quiz, $groupstudents);
|
||||||
$this->print_header_and_tabs($cm, $course, $quiz, "overview");
|
redirect($reporturl->out(false, $displayoptions), '', 5);
|
||||||
}
|
}
|
||||||
|
|
||||||
if ($regradeall && confirm_sesskey()) {
|
// Start output.
|
||||||
$this->regrade_all(false, $quiz, $groupstudents);
|
if (!$table->is_downloading()) {
|
||||||
} else if ($regradealldry && confirm_sesskey()) {
|
// Only print headers if not asked to download data
|
||||||
$this->regrade_all(true, $quiz, $groupstudents);
|
$this->print_header_and_tabs($cm, $course, $quiz, 'overview');
|
||||||
} else if ($regradealldrydo && confirm_sesskey()) {
|
|
||||||
$this->regrade_all_needed($quiz, $groupstudents);
|
|
||||||
}
|
|
||||||
if ($regradeall || $regradealldry || $regradealldrydo) {
|
|
||||||
redirect($reporturl->out(false, $displayoptions), '', 5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
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
|
|
||||||
if ($groupstudents) {
|
$transaction = $DB->start_delegated_transaction();
|
||||||
list($usql, $params) = $DB->get_in_or_equal($groupstudents);
|
|
||||||
$where = "qa.userid $usql AND ";
|
$quba = question_engine::load_questions_usage_by_activity($attempt->uniqueid);
|
||||||
} else {
|
|
||||||
$where = '';
|
if (is_null($slots)) {
|
||||||
$params = array();
|
$slots = $quba->get_slots();
|
||||||
}
|
}
|
||||||
$where .= "qa.quiz = ? AND qa.preview = 0 AND qa.uniqueid = qqr.attemptid AND qqr.regraded = 0";
|
|
||||||
$params[] = $quiz->id;
|
$finished = $attempt->timefinish > 0;
|
||||||
return $DB->get_field_sql('SELECT COUNT(1) FROM {quiz_attempts} qa, {quiz_question_regrade} qqr WHERE '. $where, $params);
|
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();
|
||||||
}
|
}
|
||||||
function regrade_all_needed($quiz, $groupstudents) {
|
|
||||||
global $DB, $OUTPUT;
|
/**
|
||||||
if (!has_capability('mod/quiz:regrade', $this->context)) {
|
* Regrade attempts for this quiz, exactly which attempts are regraded is
|
||||||
echo $OUTPUT->notification(get_string('regradenotallowed', 'quiz'));
|
* 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) {
|
||||||
|
list($usql, $uparams) = $DB->get_in_or_equal($groupstudents);
|
||||||
|
$where .= " AND userid $usql";
|
||||||
|
$params = array_merge($params, $uparams);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ($attemptids) {
|
||||||
|
list($asql, $aparams) = $DB->get_in_or_equal($attemptids);
|
||||||
|
$where .= " AND id $asql";
|
||||||
|
$params = array_merge($params, $aparams);
|
||||||
|
}
|
||||||
|
|
||||||
|
$attempts = $DB->get_records_select('quiz_attempts', $where, $params);
|
||||||
|
if (!$attempts) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
// Fetch all attempts that need regrading
|
|
||||||
if ($groupstudents) {
|
|
||||||
list($usql, $params) = $DB->get_in_or_equal($groupstudents);
|
|
||||||
$where = "qa.userid $usql AND ";
|
|
||||||
} else {
|
|
||||||
$where = '';
|
|
||||||
$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);
|
$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) {
|
foreach ($attempts as $attempt) {
|
||||||
$question = $questions[$attempt->questionid];
|
set_time_limit(30);
|
||||||
$changed = regrade_question_in_attempt($question, $attempt, $quiz, true);
|
$this->regrade_attempt($attempt, $dryrun);
|
||||||
if ($changed) {
|
}
|
||||||
$attemptschanged[] = $attempt->uniqueid;
|
|
||||||
$usersschanged[] = $attempt->userid;
|
if (!$dryrun) {
|
||||||
}
|
$this->update_overall_grades($quiz);
|
||||||
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) {
|
/**
|
||||||
|
* 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;
|
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) {
|
||||||
|
list($usql, $uparams) = $DB->get_in_or_equal($groupstudents);
|
||||||
|
$where .= " AND quiza.userid $usql";
|
||||||
|
$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 = '';
|
||||||
|
$params = array();
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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> / ';
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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'));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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 ++
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue