Merge branch 'MDL-30704_22' of git://github.com/timhunt/moodle into MOODLE_22_STABLE

This commit is contained in:
Sam Hemelryk
2011-12-21 14:30:27 +13:00
5 changed files with 167 additions and 176 deletions
+91 -91
View File
@@ -121,7 +121,7 @@ abstract class quiz_attempt_report extends quiz_default_report {
protected function validate_common_options(&$attemptsmode, &$pagesize, $course, $currentgroup) {
if ($currentgroup) {
//default for when a group is selected
if ($attemptsmode === null || $attemptsmode == QUIZ_REPORT_ATTEMPTS_ALL) {
if ($attemptsmode === null || $attemptsmode == QUIZ_REPORT_ATTEMPTS_ALL) {
$attemptsmode = QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH;
}
} else if (!$currentgroup && $course->id == SITEID) {
@@ -137,94 +137,6 @@ abstract class quiz_attempt_report extends quiz_default_report {
}
}
/**
* Contruct all the parts of the main database query.
* @param object $quiz the quiz settings.
* @param string $qmsubselect SQL fragment from {@link quiz_report_qm_filter_select()}.
* @param bool $qmfilter whether to show all, or only the final grade attempt.
* @param int $attemptsmode which attempts to show.
* One of the QUIZ_REPORT_ATTEMPTS_... constants.
* @param array $reportstudents list if userids of users to include in the report.
* @return array with 4 elements ($fields, $from, $where, $params) that can be used to
* build the actual database query.
*/
protected function base_sql($quiz, $qmsubselect, $qmfilter, $attemptsmode, $reportstudents) {
global $DB;
$fields = $DB->sql_concat('u.id', "'#'", 'COALESCE(quiza.attempt, 0)') . ' AS uniqueid,';
if ($qmsubselect) {
$fields .= "\n(CASE WHEN $qmsubselect THEN 1 ELSE 0 END) AS gradedattempt,";
}
$extrafields = get_extra_user_fields_sql($this->context, 'u', '',
array('id', 'idnumber', 'firstname', 'lastname', 'picture',
'imagealt', 'institution', 'department', 'email'));
$fields .= '
quiza.uniqueid AS usageid,
quiza.id AS attempt,
u.id AS userid,
u.idnumber,
u.firstname,
u.lastname,
u.picture,
u.imagealt,
u.institution,
u.department,
u.email' . $extrafields . ',
quiza.sumgrades,
quiza.timefinish,
quiza.timestart,
CASE WHEN quiza.timefinish = 0 THEN null
WHEN quiza.timefinish > quiza.timestart THEN quiza.timefinish - quiza.timestart
ELSE 0 END AS duration';
// To explain that last bit, in MySQL, qa.timestart and qa.timefinish
// are unsigned. Since MySQL 5.5.5, when they introduced strict mode,
// subtracting a larger unsigned int from a smaller one gave an error.
// Therefore, we avoid doing that. timefinish can be non-zero and less
// than timestart when you have two load-balanced servers with very
// badly synchronised clocks, and a student does a really quick attempt.';
// This part is the same for all cases - join users and quiz_attempts tables
$from = "\n{user} u";
$from .= "\nLEFT JOIN {quiz_attempts} quiza ON
quiza.userid = u.id AND quiza.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 = 'quiza.id IS NOT NULL AND quiza.preview = 0';
break;
case QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH:
// Show only students with attempts
list($usql, $uparams) = $DB->get_in_or_equal(
$reportstudents, SQL_PARAMS_NAMED, 'u');
$params += $uparams;
$where = "u.id $usql AND quiza.preview = 0 AND quiza.id IS NOT NULL";
break;
case QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH_NO:
// Show only students without attempts
list($usql, $uparams) = $DB->get_in_or_equal(
$reportstudents, SQL_PARAMS_NAMED, 'u');
$params += $uparams;
$where = "u.id $usql AND quiza.id IS NULL";
break;
case QUIZ_REPORT_ATTEMPTS_ALL_STUDENTS:
// Show all students with or without attempts
list($usql, $uparams) = $DB->get_in_or_equal(
$reportstudents, SQL_PARAMS_NAMED, 'u');
$params += $uparams;
$where = "u.id $usql AND (quiza.preview = 0 OR quiza.preview IS NULL)";
break;
}
return array($fields, $from, $where, $params);
}
/**
* Add all the user-related columns to the $columns and $headers arrays.
* @param table_sql $table the table being constructed.
@@ -401,17 +313,22 @@ abstract class quiz_attempt_report_table extends table_sql {
protected $quiz;
protected $context;
protected $qmsubselect;
protected $qmfilter;
protected $attemptsmode;
protected $groupstudents;
protected $students;
protected $questions;
protected $includecheckboxes;
public function __construct($uniqueid, $quiz, $context, $qmsubselect, $groupstudents,
$students, $questions, $includecheckboxes, $reporturl, $displayoptions) {
public function __construct($uniqueid, $quiz, $context, $qmsubselect, $qmfilter,
$attemptsmode, $groupstudents, $students, $questions, $includecheckboxes,
$reporturl, $displayoptions) {
parent::__construct($uniqueid);
$this->quiz = $quiz;
$this->context = $context;
$this->qmsubselect = $qmsubselect;
$this->qmfilter = $qmfilter;
$this->attemptsmode = $attemptsmode;
$this->groupstudents = $groupstudents;
$this->students = $students;
$this->questions = $questions;
@@ -609,6 +526,89 @@ abstract class quiz_attempt_report_table extends table_sql {
return '';
}
/**
* Contruct all the parts of the main database query.
* @param array $reportstudents list if userids of users to include in the report.
* @return array with 4 elements ($fields, $from, $where, $params) that can be used to
* build the actual database query.
*/
public function base_sql($reportstudents) {
global $DB;
$fields = $DB->sql_concat('u.id', "'#'", 'COALESCE(quiza.attempt, 0)') . ' AS uniqueid,';
if ($this->qmsubselect) {
$fields .= "\n(CASE WHEN $this->qmsubselect THEN 1 ELSE 0 END) AS gradedattempt,";
}
$extrafields = get_extra_user_fields_sql($this->context, 'u', '',
array('id', 'idnumber', 'firstname', 'lastname', 'picture',
'imagealt', 'institution', 'department', 'email'));
$fields .= '
quiza.uniqueid AS usageid,
quiza.id AS attempt,
u.id AS userid,
u.idnumber,
u.firstname,
u.lastname,
u.picture,
u.imagealt,
u.institution,
u.department,
u.email' . $extrafields . ',
quiza.sumgrades,
quiza.timefinish,
quiza.timestart,
CASE WHEN quiza.timefinish = 0 THEN null
WHEN quiza.timefinish > quiza.timestart THEN quiza.timefinish - quiza.timestart
ELSE 0 END AS duration';
// To explain that last bit, in MySQL, qa.timestart and qa.timefinish
// are unsigned. Since MySQL 5.5.5, when they introduced strict mode,
// subtracting a larger unsigned int from a smaller one gave an error.
// Therefore, we avoid doing that. timefinish can be non-zero and less
// than timestart when you have two load-balanced servers with very
// badly synchronised clocks, and a student does a really quick attempt.';
// This part is the same for all cases - join users and quiz_attempts tables
$from = "\n{user} u";
$from .= "\nLEFT JOIN {quiz_attempts} quiza ON
quiza.userid = u.id AND quiza.quiz = :quizid";
$params = array('quizid' => $this->quiz->id);
if ($this->qmsubselect && $this->qmfilter) {
$from .= " AND $this->qmsubselect";
}
switch ($this->attemptsmode) {
case QUIZ_REPORT_ATTEMPTS_ALL:
// Show all attempts, including students who are no longer in the course
$where = 'quiza.id IS NOT NULL AND quiza.preview = 0';
break;
case QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH:
// Show only students with attempts
list($usql, $uparams) = $DB->get_in_or_equal(
$reportstudents, SQL_PARAMS_NAMED, 'u');
$params += $uparams;
$where = "u.id $usql AND quiza.preview = 0 AND quiza.id IS NOT NULL";
break;
case QUIZ_REPORT_ATTEMPTS_STUDENTS_WITH_NO:
// Show only students without attempts
list($usql, $uparams) = $DB->get_in_or_equal(
$reportstudents, SQL_PARAMS_NAMED, 'u');
$params += $uparams;
$where = "u.id $usql AND quiza.id IS NULL";
break;
case QUIZ_REPORT_ATTEMPTS_ALL_STUDENTS:
// Show all students with or without attempts
list($usql, $uparams) = $DB->get_in_or_equal(
$reportstudents, SQL_PARAMS_NAMED, 'u');
$params += $uparams;
$where = "u.id $usql AND (quiza.preview = 0 OR quiza.preview IS NULL)";
break;
}
return array($fields, $from, $where, $params);
}
/**
* Add the information about the latest state of the question with slot
* $slot to the query.
+65 -74
View File
@@ -37,86 +37,101 @@ class quiz_report_overview_table extends quiz_attempt_report_table {
protected $regradedqs = array();
public function __construct($quiz, $context, $qmsubselect, $groupstudents,
$students, $detailedmarks, $questions, $includecheckboxes, $reporturl, $displayoptions) {
public function __construct($quiz, $context, $qmsubselect, $qmfilter,
$attemptsmode, $groupstudents, $students, $detailedmarks,
$questions, $includecheckboxes, $reporturl, $displayoptions) {
parent::__construct('mod-quiz-report-overview-report', $quiz , $context,
$qmsubselect, $groupstudents, $students, $questions, $includecheckboxes,
$reporturl, $displayoptions);
$qmsubselect, $qmfilter, $attemptsmode, $groupstudents, $students,
$questions, $includecheckboxes, $reporturl, $displayoptions);
$this->detailedmarks = $detailedmarks;
}
public function build_table() {
global $DB;
if ($this->rawdata) {
$this->strtimeformat = str_replace(',', '', get_string('strftimedatetime'));
parent::build_table();
if (!$this->rawdata) {
return;
}
//end of adding data from attempts data to table / download
//now add averages at bottom of table :
$params = array($this->quiz->id);
$averagesql = '
SELECT AVG(qg.grade) AS grade, COUNT(qg.grade) AS numaveraged
FROM {quiz_grades} qg
WHERE quiz = ?';
$this->strtimeformat = str_replace(',', '', get_string('strftimedatetime'));
parent::build_table();
$this->add_separator();
if ($this->is_downloading()) {
$namekey = 'lastname';
} else {
$namekey = 'fullname';
}
if ($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));
$groupaveragerow = array(
$namekey => get_string('groupavg', 'grades'),
'sumgrades' => $this->format_average($record),
'feedbacktext'=> strip_tags(quiz_report_feedback_for_grade(
$record->grade, $this->quiz->id, $this->context)));
if ($this->detailedmarks && ($this->quiz->attempts == 1 || $this->qmsubselect)) {
$avggradebyq = $this->load_average_question_grades($this->groupstudents);
$groupaveragerow += $this->format_average_grade_for_questions($avggradebyq);
}
$this->add_data_keyed($groupaveragerow);
}
// End of adding the data from attempts. Now add averages at bottom.
$this->add_separator();
if ($this->students) {
list($usql, $uparams) = $DB->get_in_or_equal($this->students);
$record = $DB->get_record_sql($averagesql . ' AND qg.userid ' . $usql,
array_merge($params, $uparams));
$overallaveragerow = array(
$namekey => get_string('overallaverage', 'grades'),
'sumgrades' => $this->format_average($record),
'feedbacktext'=> strip_tags(quiz_report_feedback_for_grade(
$record->grade, $this->quiz->id, $this->context)));
if ($this->detailedmarks && ($this->quiz->attempts == 1 || $this->qmsubselect)) {
$avggradebyq = $this->load_average_question_grades($this->students);
$overallaveragerow += $this->format_average_grade_for_questions($avggradebyq);
}
$this->add_data_keyed($overallaveragerow);
}
if ($this->groupstudents) {
$this->add_average_row(get_string('groupavg', 'grades'), $this->groupstudents);
}
if ($this->students) {
$this->add_average_row(get_string('overallaverage', 'grades'), $this->students);
}
}
/**
* Add an average grade over the attempts of a set of users.
* @param string $label the title ot use for this row.
* @param array $users the users to average over.
*/
protected function add_average_row($label, $users) {
global $DB;
list($fields, $from, $where, $params) = $this->base_sql($users);
$record = $DB->get_record_sql("
SELECT AVG(quiza.sumgrades) AS grade, COUNT(quiza.sumgrades) AS numaveraged
FROM $from
WHERE $where", $params);
if ($this->is_downloading()) {
$namekey = 'lastname';
} else {
$namekey = 'fullname';
}
$averagerow = array(
$namekey => $label,
'sumgrades' => $this->format_average($record),
'feedbacktext'=> strip_tags(quiz_report_feedback_for_grade(
$record->grade, $this->quiz->id, $this->context))
);
if ($this->detailedmarks) {
$dm = new question_engine_data_mapper();
$qubaids = new qubaid_join($from, 'quiza.uniqueid', $where, $params);
$avggradebyq = $dm->load_average_marks($qubaids, array_keys($this->questions));
$averagerow += $this->format_average_grade_for_questions($avggradebyq);
}
$this->add_data_keyed($averagerow);
}
/**
* Helper userd by {@link add_average_row()}.
* @param array $gradeaverages the raw grades.
* @return array the (partial) row of data.
*/
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;
}
@@ -276,30 +291,6 @@ class quiz_report_overview_table extends quiz_attempt_report_table {
}
}
/**
* 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, 'u');
$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.
+3 -4
View File
@@ -129,8 +129,8 @@ class quiz_overview_report extends quiz_attempt_report {
$questions = quiz_report_get_significant_questions($quiz);
$table = new quiz_report_overview_table($quiz, $this->context, $qmsubselect,
$groupstudents, $students, $detailedmarks, $questions, $includecheckboxes,
$reporturl, $displayoptions);
$qmfilter, $attemptsmode, $groupstudents, $students, $detailedmarks,
$questions, $includecheckboxes, $reporturl, $displayoptions);
$filename = quiz_report_download_filename(get_string('overviewfilename', 'quiz_overview'),
$courseshortname, $quiz->name);
$table->is_downloading($download, $filename,
@@ -219,8 +219,7 @@ class quiz_overview_report extends quiz_attempt_report {
"END) AS gradedattempt, ";
}
list($fields, $from, $where, $params) =
$this->base_sql($quiz, $qmsubselect, $qmfilter, $attemptsmode, $allowed);
list($fields, $from, $where, $params) = $table->base_sql($allowed);
$table->set_count_sql("SELECT COUNT(1) FROM $from WHERE $where", $params);
+3 -3
View File
@@ -146,7 +146,8 @@ class quiz_responses_report extends quiz_attempt_report {
array('context' => $displaycoursecontext));
$table = new quiz_report_responses_table($quiz, $this->context, $qmsubselect,
$groupstudents, $students, $questions, $includecheckboxes, $reporturl, $displayoptions);
$qmfilter, $attemptsmode, $groupstudents, $students, $questions,
$includecheckboxes, $reporturl, $displayoptions);
$filename = quiz_report_download_filename(get_string('responsesfilename', 'quiz_responses'),
$courseshortname, $quiz->name);
$table->is_downloading($download, $filename,
@@ -197,8 +198,7 @@ class quiz_responses_report extends quiz_attempt_report {
}
}
list($fields, $from, $where, $params) =
$this->base_sql($quiz, $qmsubselect, $qmfilter, $attemptsmode, $allowed);
list($fields, $from, $where, $params) = $table->base_sql($allowed);
$table->set_count_sql("SELECT COUNT(1) FROM $from WHERE $where", $params);
@@ -35,11 +35,12 @@ defined('MOODLE_INTERNAL') || die();
*/
class quiz_report_responses_table extends quiz_attempt_report_table {
public function __construct($quiz, $context, $qmsubselect, $groupstudents,
$students, $questions, $includecheckboxes, $reporturl, $displayoptions) {
public function __construct($quiz, $context, $qmsubselect, $qmfilter,
$attemptsmode, $groupstudents, $students,
$questions, $includecheckboxes, $reporturl, $displayoptions) {
parent::__construct('mod-quiz-report-responses-report', $quiz, $context,
$qmsubselect, $groupstudents, $students, $questions, $includecheckboxes,
$reporturl, $displayoptions);
$qmsubselect, $qmfilter, $attemptsmode, $groupstudents, $students,
$questions, $includecheckboxes, $reporturl, $displayoptions);
}
public function build_table() {