as well as for first or all attempts.
This commit drops a database table and then recreates it. This is in
order to be able to have some new columns in the table be NOT NULL with
no default value, which would not be possible if I added fields to an
existing table.
The quiz_statistics table is used for caching calculated values only
and thus we can safely drop it and recreate it.
$this->qmsubselect = quiz_report_qm_filter_select($quiz);
$this->form = new $formclass($this->get_base_url(),
- array('qmsubselect' => $this->qmsubselect, 'quiz' => $quiz,
- 'currentgroup' => $currentgroup, 'context' => $this->context));
+ array('quiz' => $quiz, 'currentgroup' => $currentgroup, 'context' => $this->context));
return array($currentgroup, $students, $groupstudents, $allowed);
}
* Get information about which students to show in the report.
* @param object $cm the coures module.
* @param object $course the course settings.
- * @return an array with four elements:
+ * @return array with four elements:
* 0 => integer the current group id (0 for none).
* 1 => array ids of all the students in this course.
* 2 => array ids of all the students in the current group.
}
/**
- * Given the quiz grading method return sub select sql to find the id of the
- * one attempt that will be graded for each user. Or return
- * empty string if all attempts contribute to final grade.
+ * This is a wrapper for {@link quiz_report_grade_method_sql} that takes the whole quiz object instead of just the grading method
+ * as a param. See definition for {@link quiz_report_grade_method_sql} below.
+ *
+ * @param object $quiz
+ * @param string $quizattemptsalias sql alias for 'quiz_attempts' table
+ * @return string sql to test if this is an attempt that will contribute towards the grade of the user
*/
function quiz_report_qm_filter_select($quiz, $quizattemptsalias = 'quiza') {
if ($quiz->attempts == 1) {
// This quiz only allows one attempt.
return '';
}
+ return quiz_report_grade_method_sql($quiz->grademethod, $quizattemptsalias);
+}
- switch ($quiz->grademethod) {
+/**
+ * Given a quiz grading method return sql to test if this is an
+ * attempt that will be contribute towards the grade of the user. Or return an
+ * empty string if the grading method is QUIZ_GRADEAVERAGE and thus all attempts
+ * contribute to final grade.
+ *
+ * @param string $grademethod quiz grading method.
+ * @param string $quizattemptsalias sql alias for 'quiz_attempts' table
+ * @return string sql to test if this is an attempt that will contribute towards the graded of the user
+ */
+function quiz_report_grade_method_sql($grademethod, $quizattemptsalias = 'quiza') {
+ switch ($grademethod) {
case QUIZ_GRADEHIGHEST :
return "$quizattemptsalias.id = (
- SELECT MIN(qa2.id)
- FROM {quiz_attempts} qa2
- WHERE qa2.quiz = $quizattemptsalias.quiz AND
- qa2.userid = $quizattemptsalias.userid AND
- COALESCE(qa2.sumgrades, 0) = (
- SELECT MAX(COALESCE(qa3.sumgrades, 0))
- FROM {quiz_attempts} qa3
- WHERE qa3.quiz = $quizattemptsalias.quiz AND
- qa3.userid = $quizattemptsalias.userid
- )
- )";
+ SELECT MIN(qa2.id)
+ FROM {quiz_attempts} qa2
+ WHERE qa2.quiz = $quizattemptsalias.quiz AND
+ qa2.userid = $quizattemptsalias.userid AND
+ COALESCE(qa2.sumgrades, 0) = (
+ SELECT MAX(COALESCE(qa3.sumgrades, 0))
+ FROM {quiz_attempts} qa3
+ WHERE qa3.quiz = $quizattemptsalias.quiz AND
+ qa3.userid = $quizattemptsalias.userid
+ )
+ )";
case QUIZ_GRADEAVERAGE :
return '';
case QUIZ_ATTEMPTFIRST :
- return "$quizattemptsalias.id = (
- SELECT MIN(qa2.id)
- FROM {quiz_attempts} qa2
- WHERE qa2.quiz = $quizattemptsalias.quiz AND
- qa2.userid = $quizattemptsalias.userid)";
+ return "$quizattemptsalias.attempt = 1";
case QUIZ_ATTEMPTLAST :
return "$quizattemptsalias.id = (
- SELECT MAX(qa2.id)
- FROM {quiz_attempts} qa2
- WHERE qa2.quiz = $quizattemptsalias.quiz AND
- qa2.userid = $quizattemptsalias.userid)";
+ SELECT MAX(qa2.id)
+ FROM {quiz_attempts} qa2
+ WHERE qa2.quiz = $quizattemptsalias.quiz AND
+ qa2.userid = $quizattemptsalias.userid)";
}
}
/**
- * Get the nuber of students whose score was in a particular band for this quiz.
+ * Get the number of students whose score was in a particular band for this quiz.
* @param number $bandwidth the width of each band.
* @param int $bands the number of bands
* @param int $quizid the quiz id.
$data = $DB->get_records_sql_menu($sql, $params);
// We need to create array elements with values 0 at indexes where there is no element.
- $data = $data + array_fill(0, $bands + 1, 0);
+ $data = $data + array_fill(0, $bands + 1, 0);
ksort($data);
- // Place the maximum (prefect grade) into the last band i.e. make last
+ // Place the maximum (perfect grade) into the last band i.e. make last
// band for example 9 <= g <=10 (where 10 is the perfect grade) rather than
// just 9 <= g <10.
$data[$bands - 1] += $data[$bands];
*/
class quiz_statistics_calculated {
- public function __construct($allattempts = null) {
- if ($allattempts !== null) {
- $this->allattempts = $allattempts;
+ /**
+ * @param string $whichattempts which attempts to use, represented internally as one of the constants as used in
+ * $quiz->grademethod ie.
+ * QUIZ_GRADEAVERAGE, QUIZ_GRADEHIGHEST, QUIZ_ATTEMPTLAST or QUIZ_ATTEMPTFIRST
+ * we calculate stats based on which attempts would affect the grade for each student,
+ * the default null value is used when constructing an instance whose values will be
+ * populated from a db record.
+ */
+ public function __construct($whichattempts = null) {
+ if ($whichattempts !== null) {
+ $this->whichattempts = $whichattempts;
}
}
/**
- * @var bool whether we are calculating calculate stats from all attempts.
+ * @var int which attempts we are calculating calculate stats from.
*/
- public $allattempts;
+ public $whichattempts;
/* Following stats all described here : http://docs.moodle.org/dev/Quiz_statistics_calculations#Test_statistics */
public $allattemptscount = 0;
+ public $lastattemptscount = 0;
+
+ public $highestattemptscount = 0;
+
public $firstattemptsavg;
public $allattemptsavg;
+ public $lastattemptsavg;
+
+ public $highestattemptsavg;
+
public $median;
public $standarddeviation;
*/
public $timemodified;
+ /**
+ * Count of attempts selected by $this->whichattempts
+ *
+ * @return int
+ */
public function s() {
- if ($this->allattempts) {
- return $this->allattemptscount;
- } else {
- return $this->firstattemptscount;
- }
+ return $this->get_field('count');
}
+ /**
+ * Average grade for the attempts selected by $this->whichattempts
+ *
+ * @return float
+ */
public function avg() {
- if ($this->allattempts) {
- return $this->allattemptsavg;
- } else {
- return $this->firstattemptsavg;
- }
+ return $this->get_field('avg');
+ }
+
+ /**
+ * Get the right field name to fetch a stat for these attempts that is calculated for more than one $whichattempts (count or
+ * avg).
+ *
+ * @param string $field name of field
+ * @return int|float
+ */
+ protected function get_field($field) {
+ $fieldname = quiz_statistics_calculator::using_attempts_string_id($this->whichattempts).$field;
+ return $this->{$fieldname};
}
/**
'allattemptscount' => 'number',
'firstattemptsavg' => 'summarks_as_percentage',
'allattemptsavg' => 'summarks_as_percentage',
+ 'lastattemptsavg' => 'summarks_as_percentage',
+ 'highestattemptsavg' => 'summarks_as_percentage',
'median' => 'summarks_as_percentage',
'standarddeviation' => 'summarks_as_percentage',
'skewness' => 'number_format',
$formattedvalue = $value;
}
- $quizinfo[get_string($property, 'quiz_statistics', $this->using_attempts_string())] = $formattedvalue;
+ $quizinfo[get_string($property, 'quiz_statistics',
+ quiz_statistics_calculator::using_attempts_lang_string($this->whichattempts))] = $formattedvalue;
}
return $quizinfo;
}
- /**
- * @return string the appropriate lang string to describe this option.
- */
- protected function using_attempts_string() {
- if ($this->allattempts) {
- return get_string('allattempts', 'quiz_statistics');
- } else {
- return get_string('firstattempts', 'quiz_statistics');
- }
- }
-
/**
* @var array of names of properties of this class that are cached in db record.
*/
- protected $fieldsindb = array('allattempts', 'firstattemptscount', 'allattemptscount', 'firstattemptsavg', 'allattemptsavg',
+ protected $fieldsindb = array('whichattempts', 'firstattemptscount', 'allattemptscount', 'firstattemptsavg', 'allattemptsavg',
+ 'lastattemptscount', 'highestattemptscount', 'lastattemptsavg', 'highestattemptsavg',
'median', 'standarddeviation', 'skewness',
'kurtosis', 'cic', 'errorratio', 'standarderror');
* Compute the quiz statistics.
*
* @param int $quizid the quiz id.
- * @param int $currentgroup the current group. 0 for none.
- * @param bool $useallattempts use all attempts, or just first attempts.
+ * @param int $whichattempts which attempts to use, represented internally as one of the constants as used in
+ * $quiz->grademethod ie.
+ * QUIZ_GRADEAVERAGE, QUIZ_GRADEHIGHEST, QUIZ_ATTEMPTLAST or QUIZ_ATTEMPTFIRST
+ * we calculate stats based on which attempts would affect the grade for each student.
* @param array $groupstudents students in this group.
* @param int $p number of positions (slots).
* @param float $sumofmarkvariance sum of mark variance, calculated as part of question statistics
* @return quiz_statistics_calculated $quizstats The statistics for overall attempt scores.
*/
- public function calculate($quizid, $currentgroup, $useallattempts, $groupstudents, $p, $sumofmarkvariance) {
+ public function calculate($quizid, $whichattempts, $groupstudents, $p, $sumofmarkvariance) {
- $quizstats = $this->attempt_counts_and_averages($quizid, $currentgroup, $useallattempts, $groupstudents);
+
+
+ $quizstats = new quiz_statistics_calculated($whichattempts);
+
+ $countsandaverages = $this->attempt_counts_and_averages($quizid, $groupstudents);
+
+ foreach ($countsandaverages as $propertyname => $value) {
+ $quizstats->{$propertyname} = $value;
+ }
$s = $quizstats->s();
// Recalculate sql again this time possibly including test for first attempt.
list($fromqa, $whereqa, $qaparams) =
- quiz_statistics_attempts_sql($quizid, $currentgroup, $groupstudents, $useallattempts);
+ quiz_statistics_attempts_sql($quizid, $groupstudents, $whichattempts);
$quizstats->median = $this->median($s, $fromqa, $whereqa, $qaparams);
}
if ($p > 1) {
- $quizstats->cic = (100 * $p / ($p -1)) * (1 - ($sumofmarkvariance / $k2));
+ $quizstats->cic = (100 * $p / ($p - 1)) * (1 - ($sumofmarkvariance / $k2));
$quizstats->errorratio = 100 * sqrt(1 - ($quizstats->cic / 100));
$quizstats->standarderror = $quizstats->errorratio *
$quizstats->standarddeviation / 100;
}
}
- $quizstats->cache(quiz_statistics_qubaids_condition($quizid, $currentgroup, $groupstudents, $useallattempts));
+ $quizstats->cache(quiz_statistics_qubaids_condition($quizid, $groupstudents, $whichattempts));
return $quizstats;
}
array($qubaids->get_hash_code(), $timemodified));
}
+ /**
+ * Given a particular quiz grading method return a lang string describing which attempts contribute to grade.
+ *
+ * Note internally we use the grading method constants to represent which attempts we are calculating statistics for, each
+ * grading method corresponds to different attempts for each user.
+ *
+ * @param int $whichattempts which attempts to use, represented internally as one of the constants as used in
+ * $quiz->grademethod ie.
+ * QUIZ_GRADEAVERAGE, QUIZ_GRADEHIGHEST, QUIZ_ATTEMPTLAST or QUIZ_ATTEMPTFIRST
+ * we calculate stats based on which attempts would affect the grade for each student.
+ * @return string the appropriate lang string to describe this option.
+ */
+ public static function using_attempts_lang_string($whichattempts) {
+ return get_string(static::using_attempts_string_id($whichattempts), 'quiz_statistics');
+ }
+
+ /**
+ * Given a particular quiz grading method return a string id for use as a field name prefix in mdl_quiz_statistics or to
+ * fetch the appropriate language string describing which attempts contribute to grade.
+ *
+ * Note internally we use the grading method constants to represent which attempts we are calculating statistics for, each
+ * grading method corresponds to different attempts for each user.
+ *
+ * @param int $whichattempts which attempts to use, represented internally as one of the constants as used in
+ * $quiz->grademethod ie.
+ * QUIZ_GRADEAVERAGE, QUIZ_GRADEHIGHEST, QUIZ_ATTEMPTLAST or QUIZ_ATTEMPTFIRST
+ * we calculate stats based on which attempts would affect the grade for each student.
+ * @return string the string id for this option.
+ */
+ public static function using_attempts_string_id($whichattempts) {
+ switch ($whichattempts) {
+ case QUIZ_ATTEMPTFIRST :
+ return 'firstattempts';
+ case QUIZ_GRADEHIGHEST :
+ return 'highestattempts';
+ case QUIZ_ATTEMPTLAST :
+ return 'lastattempts';
+ case QUIZ_GRADEAVERAGE :
+ return 'allattempts';
+ }
+ }
+
/**
* Calculating count and mean of marks for first and ALL attempts by students.
*
* See : http://docs.moodle.org/dev/Quiz_item_analysis_calculations_in_practise
* #Calculating_MEAN_of_grades_for_all_attempts_by_students
* @param int $quizid
- * @param int $currentgroup
- * @param bool $useallattempts
* @param array $groupstudents
- * @return quiz_statistics_calculated containing calculated counts, totals and averages.
+ * @return stdClass with properties with count and avg with prefixes firstattempts, highestattempts, etc.
*/
- protected function attempt_counts_and_averages($quizid, $currentgroup, $useallattempts, $groupstudents) {
+ protected function attempt_counts_and_averages($quizid, $groupstudents) {
global $DB;
- $quizstats = new quiz_statistics_calculated($useallattempts);
-
- list($fromqa, $whereqa, $qaparams) = quiz_statistics_attempts_sql($quizid, $currentgroup, $groupstudents, true);
-
- $attempttotals = $DB->get_records_sql("
- SELECT
- CASE WHEN attempt = 1 THEN 1 ELSE 0 END AS isfirst,
- COUNT(1) AS countrecs,
- SUM(sumgrades) AS total
- FROM $fromqa
- WHERE $whereqa
- GROUP BY CASE WHEN attempt = 1 THEN 1 ELSE 0 END", $qaparams);
-
- // Above query that returns sums and counts for first attempt and other non first attempts.
- // We want to work out stats for first attempt or ALL attempts.
-
- if (isset($attempttotals[1])) {
- $quizstats->firstattemptscount = $attempttotals[1]->countrecs;
- $firstattemptstotal = $attempttotals[1]->total;
- } else {
- $quizstats->firstattemptscount = 0;
- $firstattemptstotal = 0;
- }
+ list($fromqa, $whereqa, $qaparams) = quiz_statistics_attempts_sql($quizid, $groupstudents);
- if (isset($attempttotals[0])) {
- $quizstats->allattemptscount = $quizstats->firstattemptscount + $attempttotals[0]->countrecs;
- $allattemptstotal = $firstattemptstotal + $attempttotals[0]->total;
- } else {
- $quizstats->allattemptscount = $quizstats->firstattemptscount;
- $allattemptstotal = $firstattemptstotal;
+ $selects = array();
+ foreach (array_keys(quiz_get_grading_options()) as $which) {
+ $fieldprefix = static::using_attempts_string_id($which);
+ $condition = quiz_report_grade_method_sql($which);
+ if ($condition == '') {
+ $case = '1';
+ } else {
+ $case = "CASE WHEN ($condition) THEN 1 ELSE 0 END";
+ }
+ $selects[] = "
+ SUM($case) AS {$fieldprefix}count,
+ SUM(sumgrades * $case) AS {$fieldprefix}total";
}
+ $select = join(',', $selects);
- if ($quizstats->allattemptscount !== 0) {
- $quizstats->allattemptsavg = $allattemptstotal / $quizstats->allattemptscount;
- }
+ $attempttotals = $DB->get_record_sql("
+ SELECT {$select}
+ FROM $fromqa
+ WHERE $whereqa", $qaparams);
- if ($quizstats->firstattemptscount !== 0) {
- $quizstats->firstattemptsavg = $firstattemptstotal / $quizstats->firstattemptscount;
+ foreach (array_keys(quiz_get_grading_options()) as $which) {
+ $fieldprefix = static::using_attempts_string_id($which);
+ $attempttotals->{$fieldprefix.'avg'} = $attempttotals->{$fieldprefix.'total'} / $attempttotals->{$fieldprefix.'count'};
+ unset($attempttotals->{$fieldprefix.'total'});
}
-
- return $quizstats;
+ return $attempttotals;
}
/**
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true"/>
<FIELD NAME="hashcode" TYPE="char" LENGTH="40" NOTNULL="true" SEQUENCE="false" COMMENT="sha1 hash of serialized qubaids_condition class. Unique for every combination of class name and property."/>
- <FIELD NAME="allattempts" TYPE="int" LENGTH="4" NOTNULL="true" SEQUENCE="false" COMMENT="bool used to indicate whether these stats are for all attempts or just for the first."/>
+ <FIELD NAME="whichattempts" TYPE="int" LENGTH="4" NOTNULL="true" SEQUENCE="false" COMMENT="bool used to indicate whether these stats are for all attempts or just for the first."/>
<FIELD NAME="timemodified" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="firstattemptscount" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="highestattemptscount" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
+ <FIELD NAME="lastattemptscount" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="allattemptscount" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false"/>
<FIELD NAME="firstattemptsavg" TYPE="number" LENGTH="15" NOTNULL="false" SEQUENCE="false" DECIMALS="5"/>
+ <FIELD NAME="highestattemptsavg" TYPE="number" LENGTH="15" NOTNULL="false" SEQUENCE="false" DECIMALS="5"/>
+ <FIELD NAME="lastattemptsavg" TYPE="number" LENGTH="15" NOTNULL="false" SEQUENCE="false" DECIMALS="5"/>
<FIELD NAME="allattemptsavg" TYPE="number" LENGTH="15" NOTNULL="false" SEQUENCE="false" DECIMALS="5"/>
<FIELD NAME="median" TYPE="number" LENGTH="15" NOTNULL="false" SEQUENCE="false" DECIMALS="5"/>
<FIELD NAME="standarddeviation" TYPE="number" LENGTH="15" NOTNULL="false" SEQUENCE="false" DECIMALS="5"/>
upgrade_plugin_savepoint(true, 2013092000, 'quiz', 'statistics');
}
+ if ($oldversion < 2013093000) {
+ // Define table quiz_statistics to be dropped.
+ $table = new xmldb_table('quiz_statistics');
+
+ // Conditionally launch drop table for quiz_statistics.
+ if ($dbman->table_exists($table)) {
+ $dbman->drop_table($table);
+ }
+
+ // Define table quiz_statistics to be created.
+ $table = new xmldb_table('quiz_statistics');
+
+ // Adding fields to table quiz_statistics.
+ $table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
+ $table->add_field('hashcode', XMLDB_TYPE_CHAR, '40', null, XMLDB_NOTNULL, null, null);
+ $table->add_field('whichattempts', XMLDB_TYPE_INTEGER, '4', null, XMLDB_NOTNULL, null, null);
+ $table->add_field('timemodified', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+ $table->add_field('firstattemptscount', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+ $table->add_field('highestattemptscount', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+ $table->add_field('lastattemptscount', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+ $table->add_field('allattemptscount', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
+ $table->add_field('firstattemptsavg', XMLDB_TYPE_NUMBER, '15, 5', null, null, null, null);
+ $table->add_field('highestattemptsavg', XMLDB_TYPE_NUMBER, '15, 5', null, null, null, null);
+ $table->add_field('lastattemptsavg', XMLDB_TYPE_NUMBER, '15, 5', null, null, null, null);
+ $table->add_field('allattemptsavg', XMLDB_TYPE_NUMBER, '15, 5', null, null, null, null);
+ $table->add_field('median', XMLDB_TYPE_NUMBER, '15, 5', null, null, null, null);
+ $table->add_field('standarddeviation', XMLDB_TYPE_NUMBER, '15, 5', null, null, null, null);
+ $table->add_field('skewness', XMLDB_TYPE_NUMBER, '15, 10', null, null, null, null);
+ $table->add_field('kurtosis', XMLDB_TYPE_NUMBER, '15, 5', null, null, null, null);
+ $table->add_field('cic', XMLDB_TYPE_NUMBER, '15, 10', null, null, null, null);
+ $table->add_field('errorratio', XMLDB_TYPE_NUMBER, '15, 10', null, null, null, null);
+ $table->add_field('standarderror', XMLDB_TYPE_NUMBER, '15, 10', null, null, null, null);
+
+ // Adding keys to table quiz_statistics.
+ $table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
+
+ // Conditionally launch create table for quiz_statistics.
+ if (!$dbman->table_exists($table)) {
+ $dbman->create_table($table);
+ }
+
+ // Statistics savepoint reached.
+ upgrade_plugin_savepoint(true, 2013093000, 'quiz', 'statistics');
+ }
+
return true;
}
$string['firstattemptsavg'] = 'Average grade of first attempts';
$string['firstattemptscount'] = 'Number of complete graded first attempts';
$string['frequency'] = 'Frequency';
+$string['highestattempts'] = 'highest graded attempt';
+$string['highestattemptsavg'] = 'Average grade of highest graded attempts';
$string['intended_weight'] = 'Intended weight';
$string['kurtosis'] = 'Score distribution kurtosis (for {$a})';
+$string['lastattempts'] = 'last attempt';
+$string['lastattemptsavg'] = 'Average grade of last attempts';
$string['lastcalculated'] = 'Last calculated {$a->lastcalculated} ago there have been {$a->count} attempts since then.';
$string['median'] = 'Median grade (for {$a})';
$string['modelresponse'] = 'Model response';
$string['quizstructureanalysis'] = 'Quiz structure analysis';
$string['random_guess_score'] = 'Random guess score';
$string['recalculatenow'] = 'Recalculate now';
+$string['reportsettings'] = 'Statistics calculation settings';
$string['response'] = 'Response';
$string['skewness'] = 'Score distribution skewness (for {$a})';
$string['standarddeviation'] = 'Standard deviation (for {$a})';
// A qid paramter indicates we should display the detailed analysis of a sub question.
$qid = optional_param('qid', 0, PARAM_INT);
$slot = optional_param('slot', 0, PARAM_INT);
+ $whichattempts = optional_param('whichattempts', $quiz->grademethod, PARAM_INT);
$pageoptions = array();
$pageoptions['id'] = $cm->id;
$reporturl = new moodle_url('/mod/quiz/report.php', $pageoptions);
$mform = new quiz_statistics_settings_form($reporturl);
+
+ $mform->set_data(array('whichattempts' => $whichattempts));
+
if ($fromform = $mform->get_data()) {
- $useallattempts = $fromform->useallattempts;
- if ($fromform->useallattempts) {
- set_user_preference('quiz_report_statistics_useallattempts',
- $fromform->useallattempts);
- } else {
- unset_user_preference('quiz_report_statistics_useallattempts');
- }
+ $whichattempts = $fromform->whichattempts;
+ }
- } else {
- $useallattempts = get_user_preferences('quiz_report_statistics_useallattempts', 0);
+ if ($whichattempts != $quiz->grademethod) {
+ $reporturl->param('whichattempts', $whichattempts);
}
// Find out current groups mode.
}
}
- $qubaids = quiz_statistics_qubaids_condition($quiz->id, $currentgroup, $groupstudents, $useallattempts);
+ $qubaids = quiz_statistics_qubaids_condition($quiz->id, $groupstudents, $whichattempts);
// If recalculate was requested, handle that.
if ($recalculate && confirm_sesskey()) {
if (!$nostudentsingroup) {
// Get the data to be displayed.
list($quizstats, $questionstats, $subquestionstats) =
- $this->get_quiz_and_questions_stats($quiz, $currentgroup, $useallattempts, $groupstudents, $questions);
+ $this->get_quiz_and_questions_stats($quiz, $whichattempts, $groupstudents, $questions);
} else {
// Or create empty stats containers.
- $quizstats = new quiz_statistics_calculated($useallattempts);
+ $quizstats = new quiz_statistics_calculated($whichattempts);
$questionstats = array();
$subquestionstats = array();
}
}
// Print display options form.
- $mform->set_data(array('useallattempts' => $useallattempts));
$mform->display();
}
$this->output_quiz_structure_analysis_table($quizstats->s(), $questionstats, $subquestionstats);
if ($this->table->is_downloading() == 'xhtml' && $quizstats->s() != 0) {
- $this->output_statistics_graph($quiz->id, $currentgroup, $useallattempts);
+ $this->output_statistics_graph($quiz->id, $currentgroup, $whichattempts);
}
foreach ($questions as $slot => $question) {
} else {
// On-screen display of overview report.
echo $OUTPUT->heading(get_string('quizinformation', 'quiz_statistics'));
- echo $this->output_caching_info($quizstats, $quiz->id, $currentgroup,
- $groupstudents, $useallattempts, $reporturl);
+ echo $this->output_caching_info($quizstats, $quiz->id, $groupstudents, $whichattempts, $reporturl);
echo $this->everything_download_options();
$quizinfo = $quizstats->get_formatted_quiz_info_data($course, $cm, $quiz);
echo $this->output_quiz_info_table($quizinfo);
if ($quizstats->s()) {
echo $OUTPUT->heading(get_string('quizstructureanalysis', 'quiz_statistics'));
$this->output_quiz_structure_analysis_table($quizstats->s(), $questionstats, $subquestionstats);
- $this->output_statistics_graph($quiz->id, $currentgroup, $useallattempts);
+ $this->output_statistics_graph($quiz->id, $currentgroup, $whichattempts);
}
}
* Output the HTML needed to show the statistics graph.
* @param $quizid
* @param $currentgroup
- * @param $useallattempts
+ * @param $whichattempts
*/
- protected function output_statistics_graph($quizid, $currentgroup, $useallattempts) {
+ protected function output_statistics_graph($quizid, $currentgroup, $whichattempts) {
global $PAGE;
$output = $PAGE->get_renderer('mod_quiz');
$imageurl = new moodle_url('/mod/quiz/report/statistics/statistics_graph.php',
- compact('quizid', 'currentgroup', 'useallattempts'));
+ compact('quizid', 'currentgroup', 'whichattempts'));
$graphname = get_string('statisticsreportgraph', 'quiz_statistics');
echo $output->graph($imageurl, $graphname);
}
* or by recomputing them.
*
* @param object $quiz the quiz settings.
- * @param int $currentgroup the current group. 0 for none.
- * @param bool $useallattempts use all attempts, or just first attempts.
+ * @param string $whichattempts which attempts to use, represented internally as one of the constants as used in
+ * $quiz->grademethod ie.
+ * QUIZ_GRADEAVERAGE, QUIZ_GRADEHIGHEST, QUIZ_ATTEMPTLAST or QUIZ_ATTEMPTFIRST
+ * we calculate stats based on which attempts would affect the grade for each student.
* @param array $groupstudents students in this group.
- * @param array $questions question definitions.
+ * @param array $questions full question data.
* @return array with 4 elements:
* - $quizstats The statistics for overall attempt scores.
* - $questionstats array of \core_question\statistics\questions\calculated objects keyed by slot.
* - $subquestionstats array of \core_question\statistics\questions\calculated_for_subquestion objects keyed by question id.
*/
- protected function get_quiz_and_questions_stats($quiz, $currentgroup, $useallattempts, $groupstudents, $questions) {
+ protected function get_quiz_and_questions_stats($quiz, $whichattempts, $groupstudents, $questions) {
- $qubaids = quiz_statistics_qubaids_condition($quiz->id, $currentgroup, $groupstudents, $useallattempts);
+ $qubaids = quiz_statistics_qubaids_condition($quiz->id, $groupstudents, $whichattempts);
$qcalc = new \core_question\statistics\questions\calculator($questions);
// Recalculate now.
list($questionstats, $subquestionstats) = $qcalc->calculate($qubaids);
- $quizstats = $quizcalc->calculate($quiz->id, $currentgroup, $useallattempts,
- $groupstudents, count($questions), $qcalc->get_sum_of_mark_variance());
+ $quizstats = $quizcalc->calculate($quiz->id, $whichattempts, $groupstudents, count($questions),
+ $qcalc->get_sum_of_mark_variance());
if ($quizstats->s()) {
$this->analyse_responses_for_all_questions_and_subquestions($qubaids, $questions, $subquestionstats);
* with a recalcuate now button.
* @param object $quizstats the overall quiz statistics.
* @param int $quizid the quiz id.
- * @param int $currentgroup the id of the currently selected group, or 0.
- * @param array $groupstudents ids of students in the group.
- * @param bool $useallattempts whether to use all attempts, instead of just
- * first attempts.
+ * @param array $groupstudents ids of students in the group or empty array if groups not used.
+ * @param string $whichattempts which attempts to use, represented internally as one of the constants as used in
+ * $quiz->grademethod ie.
+ * QUIZ_GRADEAVERAGE, QUIZ_GRADEHIGHEST, QUIZ_ATTEMPTLAST or QUIZ_ATTEMPTFIRST
+ * we calculate stats based on which attempts would affect the grade for each student.
* @param moodle_url $reporturl url for this report
* @return string a HTML snipped saying when the stats were last computed,
* or blank if that is not appropriate.
*/
- protected function output_caching_info($quizstats, $quizid, $currentgroup,
- $groupstudents, $useallattempts, $reporturl) {
+ protected function output_caching_info($quizstats, $quizid, $groupstudents, $whichattempts, $reporturl) {
global $DB, $OUTPUT;
if (empty($quizstats->timemodified)) {
}
// Find the number of attempts since the cached statistics were computed.
- list($fromqa, $whereqa, $qaparams) = quiz_statistics_attempts_sql(
- $quizid, $currentgroup, $groupstudents, $useallattempts, true);
+ list($fromqa, $whereqa, $qaparams) = quiz_statistics_attempts_sql($quizid, $groupstudents, $whichattempts, true);
$count = $DB->count_records_sql("
SELECT COUNT(1)
FROM $fromqa
protected function definition() {
$mform = $this->_form;
- $mform->addElement('header', 'preferencespage',
- get_string('preferencespage', 'quiz_overview'));
+ $mform->addElement('header', 'preferencespage', get_string('reportsettings', 'quiz_statistics'));
$options = array();
- $options[0] = get_string('attemptsfirst', 'quiz_statistics');
- $options[1] = get_string('attemptsall', 'quiz_statistics');
- $mform->addElement('select', 'useallattempts',
- get_string('calculatefrom', 'quiz_statistics'), $options);
- $mform->setDefault('useallattempts', 0);
-
- $mform->addElement('submit', 'submitbutton',
- get_string('preferencessave', 'quiz_overview'));
+ foreach (array_keys(quiz_get_grading_options()) as $which) {
+ $options[$which] = \quiz_statistics_calculator::using_attempts_lang_string($which);
+ }
+
+ $mform->addElement('select', 'whichattempts', get_string('calculatefrom', 'quiz_statistics'), $options);
+
+ $mform->addElement('submit', 'submitbutton', get_string('preferencessave', 'quiz_overview'));
}
}
// Get the parameters.
$quizid = required_param('quizid', PARAM_INT);
$currentgroup = required_param('currentgroup', PARAM_INT);
-$useallattempts = required_param('useallattempts', PARAM_INT);
+$whichattempts = required_param('whichattempts', PARAM_INT);
$quiz = $DB->get_record('quiz', array('id' => $quizid), '*', MUST_EXIST);
$cm = get_coursemodule_from_instance('quiz', $quiz->id);
if ($currentgroup && !in_array($currentgroup, array_keys($groups))) {
print_error('groupnotamember', 'group');
}
-$groupstudents = get_users_by_capability($modcontext, array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'),
- '', '', '', '', $currentgroup, '', false);
-$qubaids = quiz_statistics_qubaids_condition($quizid, $currentgroup, $groupstudents, $useallattempts);
+if (empty($currentgroup)) {
+ $groupstudents = array();
+} else {
+ $groupstudents = get_users_by_capability($modcontext, array('mod/quiz:reviewmyattempts', 'mod/quiz:attempt'),
+ '', '', '', '', $currentgroup, '', false);
+}
+$qubaids = quiz_statistics_qubaids_condition($quizid, $groupstudents, $whichattempts);
// Load the rest of the required data.
$questions = quiz_report_get_significant_questions($quiz);
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
-function quiz_statistics_attempts_sql($quizid, $currentgroup, $groupstudents,
- $allattempts = true, $includeungraded = false) {
+/**
+ * SQL to fetch relevant 'quiz_attempts' records.
+ *
+ * @param int $quizid quiz id to get attempts for
+ * @param array $groupstudents empty array if not using groups or array of students in current group.
+ * @param string $whichattempts which attempts to use, represented internally as one of the constants as used in
+ * $quiz->grademethod ie.
+ * QUIZ_GRADEAVERAGE, QUIZ_GRADEHIGHEST, QUIZ_ATTEMPTLAST or QUIZ_ATTEMPTFIRST
+ * we calculate stats based on which attempts would affect the grade for each student.
+ * @param bool $includeungraded whether to fetch ungraded attempts too
+ * @return array FROM and WHERE sql fragments and sql params
+ */
+function quiz_statistics_attempts_sql($quizid, $groupstudents, $whichattempts = QUIZ_GRADEAVERAGE, $includeungraded = false) {
global $DB;
$fromqa = '{quiz_attempts} quiza ';
$whereqa = 'quiza.quiz = :quizid AND quiza.preview = 0 AND quiza.state = :quizstatefinished';
$qaparams = array('quizid' => (int)$quizid, 'quizstatefinished' => quiz_attempt::FINISHED);
- if (!empty($currentgroup) && $groupstudents) {
+ if ($groupstudents) {
+ ksort($groupstudents);
list($grpsql, $grpparams) = $DB->get_in_or_equal(array_keys($groupstudents),
SQL_PARAMS_NAMED, 'u');
$whereqa .= " AND quiza.userid $grpsql";
$qaparams += $grpparams;
}
- if (!$allattempts) {
- $whereqa .= ' AND quiza.attempt = 1';
+ $whichattemptsql = quiz_report_grade_method_sql($whichattempts);
+ if ($whichattemptsql) {
+ $whereqa .= ' AND '.$whichattemptsql;
}
if (!$includeungraded) {
* Return a {@link qubaid_condition} from the values returned by {@link quiz_statistics_attempts_sql}.
*
* @param int $quizid
- * @param int $currentgroup
* @param array $groupstudents
- * @param bool $allattempts
+ * @param string $whichattempts which attempts to use, represented internally as one of the constants as used in
+ * $quiz->grademethod ie.
+ * QUIZ_GRADEAVERAGE, QUIZ_GRADEHIGHEST, QUIZ_ATTEMPTLAST or QUIZ_ATTEMPTFIRST
+ * we calculate stats based on which attempts would affect the grade for each student.
* @param bool $includeungraded
* @return \qubaid_join
*/
-function quiz_statistics_qubaids_condition($quizid, $currentgroup, $groupstudents,
- $allattempts = true, $includeungraded = false) {
- list($fromqa, $whereqa, $qaparams) = quiz_statistics_attempts_sql($quizid, $currentgroup,
- $groupstudents, $allattempts, $includeungraded);
+function quiz_statistics_qubaids_condition($quizid, $groupstudents, $whichattempts = QUIZ_GRADEAVERAGE, $includeungraded = false) {
+ list($fromqa, $whereqa, $qaparams) = quiz_statistics_attempts_sql($quizid, $groupstudents, $whichattempts, $includeungraded);
return new qubaid_join($fromqa, 'quiza.uniqueid', $whereqa, $qaparams);
}
*/
class testable_quiz_statistics_report extends quiz_statistics_report {
- public function get_stats($quiz, $useallattempts = true,
- $currentgroup = 0, $groupstudents = array(), $nostudentsingroup = false) {
- $qubaids = quiz_statistics_qubaids_condition($quiz->id, $currentgroup, $groupstudents, $useallattempts);
+ public function get_stats($quiz, $whichattempts = QUIZ_GRADEAVERAGE, $groupstudents = array()) {
+ $qubaids = quiz_statistics_qubaids_condition($quiz->id, $groupstudents, $whichattempts);
$this->clear_cached_data($qubaids);
$questions = $this->load_and_initialise_questions_for_calculations($quiz);
- return $this->get_quiz_and_questions_stats($quiz, $currentgroup, $useallattempts, $groupstudents, $questions);
+ return $this->get_quiz_and_questions_stats($quiz, $whichattempts, $groupstudents, $questions);
}
}
defined('MOODLE_INTERNAL') || die();
-$plugin->version = 2013092000;
-$plugin->requires = 2013092000;
+$plugin->version = 2013093000;
+$plugin->requires = 2013092700;
$plugin->cron = 18000;
$plugin->component = 'quiz_statistics';