From 6f51ed72be264b772d7a75f6e0ba3fba01917d88 Mon Sep 17 00:00:00 2001 From: jamiesensei Date: Mon, 16 Jun 2008 13:29:00 +0000 Subject: [PATCH] MDL-14204 "Content for Quiz Statistics report table - Random_guess_score" added method random_guess_score to question type objects and a new function in questionlib that calls the question type method. --- lang/en_utf8/quiz_statistics.php | 1 + lib/questionlib.php | 12 +- mod/quiz/report/statistics/report.php | 189 +++++++++--------- .../report/statistics/statistics_table.php | 9 + question/type/match/questiontype.php | 10 + question/type/multianswer/questiontype.php | 13 ++ question/type/multichoice/questiontype.php | 13 +- question/type/questiontype.php | 10 +- question/type/random/questiontype.php | 2 +- question/type/randomsamatch/questiontype.php | 15 +- question/type/shortanswer/questiontype.php | 15 +- question/type/truefalse/questiontype.php | 8 + 12 files changed, 201 insertions(+), 96 deletions(-) diff --git a/lang/en_utf8/quiz_statistics.php b/lang/en_utf8/quiz_statistics.php index f6341df9f3..7e2ef1abb4 100644 --- a/lang/en_utf8/quiz_statistics.php +++ b/lang/en_utf8/quiz_statistics.php @@ -32,4 +32,5 @@ $string['questionnumber'] = 'Q#'; $string['quizstructureanalysis'] = 'Quiz structure analysis'; $string['questiontype'] = 'Q type'; $string['intended_weight'] = 'Intended question weight'; +$string['random_guess_score'] = 'Random guess score'; ?> \ No newline at end of file diff --git a/lib/questionlib.php b/lib/questionlib.php index 153d7bf1a6..d47bace320 100644 --- a/lib/questionlib.php +++ b/lib/questionlib.php @@ -1794,8 +1794,16 @@ function get_question_fraction_grade($question, $state) { $r = $QTYPES[$question->qtype]->get_fractional_grade($question, $state); return $r; } - - +/** +* @return integer grade out of 1 that a random guess by a student might score. +*/ +// ULPGc ecastro +function get_random_guess_score($question) { + global $QTYPES; + + $r = $QTYPES[$question->qtype]->get_random_guess_score($question); + return $r; +} /// CATEGORY FUNCTIONS ///////////////////////////////////////////////////////////////// /** diff --git a/mod/quiz/report/statistics/report.php b/mod/quiz/report/statistics/report.php index ea931266da..50a4fd4d27 100644 --- a/mod/quiz/report/statistics/report.php +++ b/mod/quiz/report/statistics/report.php @@ -8,7 +8,6 @@ * @package quiz *//** */ -require_once($CFG->libdir.'/tablelib.php'); require_once($CFG->dirroot.'/mod/quiz/report/statistics/statistics_form.php'); require_once($CFG->dirroot.'/mod/quiz/report/statistics/statistics_table.php'); @@ -55,8 +54,12 @@ class quiz_report extends quiz_default_report { $allowedlist = $groupstudentslist; } - $questions = question_load_questions(quiz_questions_in_quiz($quiz->questions)); - + $questions = quiz_report_load_questions($quiz); + // Load the question type specific information + if (!get_question_options($questions)) { + print_error('cannotloadquestion', 'question'); + } + $table = new quiz_report_statistics_table(); $table->is_downloading($download, get_string('reportstatistics','quiz_statistics'), "$course->shortname ".format_string($quiz->name,true)); @@ -170,97 +173,105 @@ class quiz_report extends quiz_default_report { $median += array_shift($mediangrades); $median = $median /2; } - //fetch sum of squared, cubed and power 4d - //differences between grades and mean grade - $mean = $usingattempts->total / $usingattempts->countrecs; - $sql = "SELECT " . - "SUM(POWER((qa.sumgrades - ?),2)) AS power2, " . - "SUM(POWER((qa.sumgrades - ?),3)) AS power3, ". - "SUM(POWER((qa.sumgrades - ?),4)) AS power4 ". - 'FROM ' .$fromqa. - 'WHERE ' .$whereqa. - $usingattempts->sql; - $params = array($mean, $mean, $mean, $quiz->id); - if (!$powers = $DB->get_record_sql($sql, $params)){ - print_error('errorpowers', 'quiz_statistics'); - } $s = $usingattempts->countrecs; - - //Standard_Deviation - //see http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#Standard_Deviation - - $sd = sqrt($powers->power2 / ($s -1)); - - //Skewness_and_Kurtosis - //see http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#Skewness_and_Kurtosis - $m2= $powers->power2 / $s; - $m3= $powers->power3 / $s; - $m4= $powers->power4 / $s; - - $k2= $s*$m2/($s-1); - $k3= $s*$s*$m3/(($s-1)*($s-2)); - $k4= (($s*$s*$s)/(($s-1)*($s-2)*($s-3)))*((($s+1)*$m4)-(3*($s-1)*$m2*$m2)); - - $skewness = $k3 / (pow($k2, 2/3)); - $kurtosis = $k4 / ($k2*$k2); - - $quizattsstatistics = new object(); - $quizattsstatistics->align = array('center', 'center'); - $quizattsstatistics->width = '60%'; - $quizattsstatistics->class = 'generaltable titlesleft'; - $quizattsstatistics->data = array(); - - $quizattsstatistics->data[] = array(get_string('median', 'quiz_statistics'), quiz_report_scale_sumgrades_as_percentage($median, $quiz)); - $quizattsstatistics->data[] = array(get_string('standarddeviation', 'quiz_statistics'), quiz_report_scale_sumgrades_as_percentage($sd, $quiz)); - $quizattsstatistics->data[] = array(get_string('skewness', 'quiz_statistics'), $skewness); - $quizattsstatistics->data[] = array(get_string('kurtosis', 'quiz_statistics'), $kurtosis); - - //CIC, ER and SE. - //http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#CIC.2C_ER_and_SE - $qgradeavgsql = "SELECT qs.question, AVG(qs.grade) FROM " . - "{question_sessions} qns, " . - "{question_states} qs, " . - "{question} q, " . - $fromqa.' '. - 'WHERE ' .$whereqa. - 'AND qns.attemptid = qa.uniqueid '. - 'AND qs.question = q.id ' . - 'AND q.length > 0 '. - $usingattempts->sql. - 'AND qns.newgraded = qs.id GROUP BY qs.question'; - $qgradeavgs = $DB->get_records_sql_menu($qgradeavgsql, array($quiz->id)); - $sum = 0; - $sql = 'SELECT ' . - 'SUM(POWER((qs.grade - ?),2)) AS power2 ' . - 'FROM ' . - '{question_sessions} qns, ' . - '{question_states} qs, ' . - '{question} q, ' . - $fromqa.' '. + if ($s>1){ + $quizattsstatistics = new object(); + $quizattsstatistics->align = array('center', 'center'); + $quizattsstatistics->width = '60%'; + $quizattsstatistics->class = 'generaltable titlesleft'; + $quizattsstatistics->data = array(); + $quizattsstatistics->data[] = array(get_string('median', 'quiz_statistics'), quiz_report_scale_sumgrades_as_percentage($median, $quiz)); + //fetch sum of squared, cubed and power 4d + //differences between grades and mean grade + $mean = $usingattempts->total / $usingattempts->countrecs; + $sql = "SELECT " . + "SUM(POWER((qa.sumgrades - ?),2)) AS power2, " . + "SUM(POWER((qa.sumgrades - ?),3)) AS power3, ". + "SUM(POWER((qa.sumgrades - ?),4)) AS power4 ". + 'FROM ' .$fromqa. 'WHERE ' .$whereqa. - 'AND qns.attemptid = qa.uniqueid '. - 'AND qs.question = ? ' . - $usingattempts->sql. - 'AND qns.newgraded = qs.id'; - foreach ($qgradeavgs as $qid => $qgradeavg){ - $params = array($qgradeavg, $quiz->id, $qid); - $power = $DB->get_field_sql($sql, $params); - if ($power === false){ - print_error('errorpowerquestions', 'quiz_statistics'); + $usingattempts->sql; + $params = array($mean, $mean, $mean, $quiz->id); + if (!$powers = $DB->get_record_sql($sql, $params)){ + print_error('errorpowers', 'quiz_statistics'); + } + + //Standard_Deviation + //see http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#Standard_Deviation + + $sd = sqrt($powers->power2 / ($s -1)); + $quizattsstatistics->data[] = array(get_string('standarddeviation', 'quiz_statistics'), quiz_report_scale_sumgrades_as_percentage($sd, $quiz)); + + + //Skewness_and_Kurtosis + if ($s>2){ + //see http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#Skewness_and_Kurtosis + $m2= $powers->power2 / $s; + $m3= $powers->power3 / $s; + $m4= $powers->power4 / $s; + + $k2= $s*$m2/($s-1); + $k3= $s*$s*$m3/(($s-1)*($s-2)); + + $skewness = $k3 / (pow($k2, 2/3)); + $quizattsstatistics->data[] = array(get_string('skewness', 'quiz_statistics'), $skewness); + } + + + if ($s>3){ + $k4= (($s*$s*$s)/(($s-1)*($s-2)*($s-3)))*((($s+1)*$m4)-(3*($s-1)*$m2*$m2)); + + $kurtosis = $k4 / ($k2*$k2); + + $quizattsstatistics->data[] = array(get_string('kurtosis', 'quiz_statistics'), $kurtosis); + } + //CIC, ER and SE. + //http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#CIC.2C_ER_and_SE + $qgradeavgsql = "SELECT qs.question, AVG(qs.grade) FROM " . + "{question_sessions} qns, " . + "{question_states} qs, " . + "{question} q, " . + $fromqa.' '. + 'WHERE ' .$whereqa. + 'AND qns.attemptid = qa.uniqueid '. + 'AND qs.question = q.id ' . + 'AND q.length > 0 '. + $usingattempts->sql. + 'AND qns.newgraded = qs.id GROUP BY qs.question'; + $qgradeavgs = $DB->get_records_sql_menu($qgradeavgsql, array($quiz->id)); + $sum = 0; + $sql = 'SELECT ' . + 'SUM(POWER((qs.grade - ?),2)) AS power2 ' . + 'FROM ' . + '{question_sessions} qns, ' . + '{question_states} qs, ' . + '{question} q, ' . + $fromqa.' '. + 'WHERE ' .$whereqa. + 'AND qns.attemptid = qa.uniqueid '. + 'AND qs.question = ? ' . + $usingattempts->sql. + 'AND qns.newgraded = qs.id'; + foreach ($qgradeavgs as $qid => $qgradeavg){ + $params = array($qgradeavg, $quiz->id, $qid); + $power = $DB->get_field_sql($sql, $params); + if ($power === false){ + print_error('errorpowerquestions', 'quiz_statistics'); + } + $sum += $power; } - $sum += $power; + $sumofvarianceforallpositions = $sum / ($usingattempts->countrecs -1); + $p = count($qgradeavgs);//no of positions + $cic = (100 * $p / ($p -1)) * (1 - ($sumofvarianceforallpositions/$k2)); + $quizattsstatistics->data[] = array(get_string('cic', 'quiz_statistics'), number_format($cic, $quiz->decimalpoints).' %'); + $errorratio = 100 * sqrt(1-($cic/100)); + $quizattsstatistics->data[] = array(get_string('errorratio', 'quiz_statistics'), number_format($errorratio, $quiz->decimalpoints).' %'); + $standarderror = ($errorratio * $sd / 100); + $quizattsstatistics->data[] = array(get_string('standarderror', 'quiz_statistics'), + quiz_report_scale_sumgrades_as_percentage($standarderror, $quiz)); + print_table($quizattsstatistics); } - $sumofvarianceforallpositions = $sum / ($usingattempts->countrecs -1); - $p = count($qgradeavgs);//no of positions - $cic = (100 * $p / ($p -1)) * (1 - ($sumofvarianceforallpositions/$k2)); - $quizattsstatistics->data[] = array(get_string('cic', 'quiz_statistics'), number_format($cic, $quiz->decimalpoints).' %'); - $errorratio = 100 * sqrt(1-($cic/100)); - $quizattsstatistics->data[] = array(get_string('errorratio', 'quiz_statistics'), number_format($errorratio, $quiz->decimalpoints).' %'); - $standarderror = ($errorratio * $sd / 100); - $quizattsstatistics->data[] = array(get_string('standarderror', 'quiz_statistics'), - quiz_report_scale_sumgrades_as_percentage($standarderror, $quiz)); - print_table($quizattsstatistics); } if (!$table->is_downloading()){ diff --git a/mod/quiz/report/statistics/statistics_table.php b/mod/quiz/report/statistics/statistics_table.php index 6d4889c36a..6d308a1b31 100644 --- a/mod/quiz/report/statistics/statistics_table.php +++ b/mod/quiz/report/statistics/statistics_table.php @@ -1,4 +1,5 @@ libdir.'/tablelib.php'); class quiz_report_statistics_table extends flexible_table { @@ -37,6 +38,9 @@ class quiz_report_statistics_table extends flexible_table { $columns[]= 'intended_weight'; $headers[]= get_string('intended_weight', 'quiz_statistics'); + $columns[]= 'random_guess_score'; + $headers[]= get_string('random_guess_score', 'quiz_statistics'); + $this->define_columns($columns); $this->define_headers($headers); $this->sortable(false); @@ -59,6 +63,7 @@ class quiz_report_statistics_table extends flexible_table { $this->column_class('sumgrades', 'bold');*/ $this->column_class('intended_weight', 'numcol'); + $this->column_class('random_guess_score', 'numcol'); $this->set_attribute('id', 'questionstatistics'); $this->set_attribute('class', 'generaltable generalbox boxaligncenter'); @@ -89,6 +94,10 @@ class quiz_report_statistics_table extends flexible_table { return quiz_report_scale_sumgrades_as_percentage($question->grade, $this->quiz); } + function col_random_guess_score($question){ + return number_format(get_random_guess_score($question) * 100, 2).' %'; + } + } ?> diff --git a/question/type/match/questiontype.php b/question/type/match/questiontype.php index 9eb5893696..f0c56b55cf 100644 --- a/question/type/match/questiontype.php +++ b/question/type/match/questiontype.php @@ -425,6 +425,16 @@ class question_match_qtype extends default_questiontype { // This should almost certainly be overridden return substr(implode(', ', $this->get_actual_response($question, $state)), 0, $length); } + + + /** + * @param object $question + * @return integer a score out of 1 that the average random guess by a + * student might give. + */ + function get_random_guess_score($question) { + return 1 / count($question->options->subquestions); + } /// BACKUP FUNCTIONS //////////////////////////// diff --git a/question/type/multianswer/questiontype.php b/question/type/multianswer/questiontype.php index b92324f8a6..7e01145c94 100644 --- a/question/type/multianswer/questiontype.php +++ b/question/type/multianswer/questiontype.php @@ -514,6 +514,19 @@ class embedded_cloze_qtype extends default_questiontype { } return $responses; } + + /** + * @param object $question + * @return integer a score out of 1 that the average random guess by a + * student might give. + */ + function get_random_guess_score($question) { + $totalfraction = 0; + foreach (array_keys($question->options->questions) as $key){ + $totalfraction += get_random_guess_score($question->options->questions[$key]); + } + return $totalfraction / count($question->options->questions); + } /// BACKUP FUNCTIONS //////////////////////////// diff --git a/question/type/multichoice/questiontype.php b/question/type/multichoice/questiontype.php index f5da87a84a..d81ff88d28 100644 --- a/question/type/multichoice/questiontype.php +++ b/question/type/multichoice/questiontype.php @@ -396,7 +396,18 @@ class question_multichoice_qtype extends default_questiontype { function response_summary($question, $state, $length = 80) { return implode(',', $this->get_actual_response($question, $state)); } - + /** + * @param object $question + * @return integer a score out of 1 that the average random guess by a + * student might give. + */ + function get_random_guess_score($question) { + $totalfraction = 0; + foreach ($question->options->answers as $answer){ + $totalfraction += $answer->fraction; + } + return $totalfraction / count($question->options->answers); + } /// BACKUP FUNCTIONS //////////////////////////// /* diff --git a/question/type/questiontype.php b/question/type/questiontype.php index 3118e4d534..9c8d20209b 100644 --- a/question/type/questiontype.php +++ b/question/type/questiontype.php @@ -674,7 +674,15 @@ class default_questiontype { return null; } } - + + /** + * @param object $question + * @return integer a score out of 1 that the average random guess by a + * student might give. + */ + function get_random_guess_score($question) { + return 0; + } /** * Return the actual response to the question in a given state * for the question diff --git a/question/type/random/questiontype.php b/question/type/random/questiontype.php index a4ab63a738..e0dbb459e8 100644 --- a/question/type/random/questiontype.php +++ b/question/type/random/questiontype.php @@ -101,7 +101,7 @@ class random_qtype extends default_questiontype { AND parent = '0' AND hidden = '0' AND id NOT IN ($cmoptions->questionsinuse) - AND qtype NOT IN ($QTYPE_EXCLUDE_FROM_RANDOM)", '', 'id')) { + AND qtype NOT IN ($QTYPE_EXCLUDE_FROM_RANDOM)", array(), '', 'id')) { $this->catrandoms[$question->category][$question->questiontext] = draw_rand_array($catrandoms, count($catrandoms)); } else { diff --git a/question/type/randomsamatch/questiontype.php b/question/type/randomsamatch/questiontype.php index 3350f67baf..cf034ece1f 100644 --- a/question/type/randomsamatch/questiontype.php +++ b/question/type/randomsamatch/questiontype.php @@ -272,7 +272,20 @@ class question_randomsamatch_qtype extends question_match_qtype { $result->responses = $answers; return $result; } - + /** + * @param object $question + * @return integer a score out of 1 that the average random guess by a + * student might give. + */ + function get_random_guess_score($question) { + //Effectively $subquestions multi choice questions with equal weighting + //assuming a student has the intelligence to not select the same answer twice + //there is in each subquestion factorial($subquestions-1) chance of getting + //the answer right. There are factorial($subquestions) possible combinations of + //answers and it works out to an average grade of 1/$subquestions. + $subquestions = count($question->options->subquestions); + return 1/$subquestions; + } /// BACKUP FUNCTIONS //////////////////////////// /* diff --git a/question/type/shortanswer/questiontype.php b/question/type/shortanswer/questiontype.php index c6e0904be4..99b1054656 100644 --- a/question/type/shortanswer/questiontype.php +++ b/question/type/shortanswer/questiontype.php @@ -239,7 +239,20 @@ class question_shortanswer_qtype extends default_questiontype { } return $response; } - + /** + * @param object $question + * @return integer a score out of 1 that the average random guess by a + * student might give. + */ + function get_random_guess_score($question) { + $answers = &$question->options->answers; + foreach($answers as $aid => $answer) { + if ('*' == trim($answer->answer)){ + return $answer->fraction; + } + } + return 0; + } /// BACKUP FUNCTIONS //////////////////////////// /* diff --git a/question/type/truefalse/questiontype.php b/question/type/truefalse/questiontype.php index 5f3c00eb5f..8017f5fa4d 100644 --- a/question/type/truefalse/questiontype.php +++ b/question/type/truefalse/questiontype.php @@ -245,6 +245,14 @@ class question_truefalse_qtype extends default_questiontype { } return $responses; } + /** + * @param object $question + * @return integer a score out of 1 that the average random guess by a + * student might give. + */ + function get_random_guess_score($question) { + return 0.5; + } /// BACKUP FUNCTIONS //////////////////////////// -- 2.39.5