]> git.mjollnir.org Git - moodle.git/commitdiff
MDL-15268 "Content for Quiz Statistics report table" further work on quiz statistics...
authorjamiesensei <jamiesensei>
Wed, 30 Jul 2008 09:02:44 +0000 (09:02 +0000)
committerjamiesensei <jamiesensei>
Wed, 30 Jul 2008 09:02:44 +0000 (09:02 +0000)
12 files changed:
lang/en_utf8/quiz_statistics.php
lib/questionlib.php
lib/tablelib.php
mod/quiz/report/reportlib.php
mod/quiz/report/statistics/cron.php
mod/quiz/report/statistics/db/install.xml
mod/quiz/report/statistics/db/upgrade.php
mod/quiz/report/statistics/qstats.php
mod/quiz/report/statistics/report.php
mod/quiz/report/statistics/statistics_graph.php [new file with mode: 0644]
mod/quiz/report/statistics/statistics_table.php
mod/quiz/report/statistics/version.php

index 88f0f8b97f92d458ce9e1911dd2b016d30bd93fc..2dc0dd77fa0ecd6b1b8b9d109683eeb5ec9e2f65 100644 (file)
@@ -45,4 +45,16 @@ $string['discriminative_efficiency'] = 'Discriminative Efficiency';
 $string['effective_weight'] = 'Effective weight';
 $string['errorrandom'] = 'Error getting sub item data';
 $string['erroritemappearsmorethanoncewithdifferentweight'] = 'Question ($a) appears more than once with different weights in different positions of the test. This is not currently supported by the statistics report and may make the statistics for this question unreliable.';
+$string['lastcalculated'] = 'Last calculated $a->lastcalculated ago there have been $a->count attempts since then.';
+$string['recalculatenow'] = 'Recalculate now';
+$string['detailedanalysis'] = 'More detailed analysis of the responses to this question';
+$string['errordeletingquizstats'] = 'Error deleting old quiz_statistics records.';
+$string['errordeletingqstats'] = 'Error deleting old quiz_question_statistics records.';
+$string['questionname'] = 'Question Name';
+$string['questiontype'] = 'Question Type';
+$string['positions'] = 'Position(s)';
+$string['position'] = 'Position';
+$string['questioninformation'] = 'Question information';
+$string['questionstatistics'] = 'Question statistics';
+$string['analysisofresponses'] = 'Analysis of responses';
 ?>
\ No newline at end of file
index ae735b681eb4b134e1fe90cab1569c2dbcf0ce6d..a041b857f30bd542f3983b8fe42188ed0b48c7c2 100644 (file)
@@ -2483,18 +2483,23 @@ function get_filesdir_from_context($context){
     return $courseid;
 }
 /**
- * Get the real question id for a random question.
+ * Get the real state - the correct question id and answer - for a random
+ * question.
  * @param object $state with property answer.
  * @return mixed return integer real question id or false if there was an
  * error..
  */
-function question_get_real_questionid($state){
+function question_get_real_state($state){
+    $realstate = clone($state);
     $matches = array();
-    if (!preg_match('|^random([0-9]+)-|', $state->answer, $matches)){
+    if (!preg_match('|^random([0-9]+)-(.+)|', $state->answer, $matches)){
         notify(get_string('errorrandom', 'quiz_statistics'));
         return false;
     } else {
-        return $matches[1];
+        $realstate->question = $matches[1];
+        $realstate->answer = $matches[2];
+        return $realstate;
     }
 }
+
 ?>
index e682d0289826be362d6233affe749a45deaa4c1b..0da006d9158e76c8afa48350310374d3f8331ce6 100644 (file)
@@ -108,17 +108,27 @@ class flexible_table {
      */
     function is_downloading($download = null, $filename='', $sheettitle=''){
         if ($download!==null){
-            $this->filename = clean_filename($filename);
             $this->sheettitle = $sheettitle;
             $this->is_downloadable(true);
             $this->download = $download;
-            if (!empty($download)){
-                $classname = 'table_'.$download.'_export_format';
-                $this->exportclass = new $classname($this);
-            }
+            $this->filename = clean_filename($filename);
+            $this->export_class_instance();
         }
         return $this->download;
     }
+    
+    function export_class_instance(){
+        if (is_null($this->exportclass) && !empty($this->download)){
+            $classname = 'table_'.$this->download.'_export_format';
+            $this->exportclass = new $classname($this);
+            if (!$this->exportclass->document_started()){
+                $this->exportclass->start_document($this->filename);
+            }
+        }
+        return $this->exportclass;
+    }
+    
+    
     /**
      * Probably don't need to call this directly. Calling is_downloading with a
      * param automatically sets table as downloadable.
@@ -625,9 +635,12 @@ class flexible_table {
      * data to the table with add_data or add_data_keyed.
      *
      */
-    function finish_output(){
+    function finish_output($closeexportclassdoc = true){
         if ($this->exportclass!==null){
-            $this->exportclass->finish_output();
+            $this->exportclass->finish_table();
+            if ($closeexportclassdoc){
+                $this->exportclass->finish_document();
+            }
         }else{
             $this->finish_html();
         }
@@ -853,7 +866,7 @@ class flexible_table {
     function start_output(){
         $this->started_output = true;
         if ($this->exportclass!==null){
-            $this->exportclass->start_output($this->filename, $this->sheettitle);
+            $this->exportclass->start_table($this->sheettitle);
             $this->exportclass->output_headers($this->headers);
         } else {
             $this->start_html();
@@ -1234,9 +1247,19 @@ class table_default_export_format_parent{
      * object from which to export data.
      */
     var $table;
+    
+    /**
+     * @var boolean output started. Keeps track of whether any output has been
+     * started yet.
+     */
+    var $documentstarted = false;
     function table_default_export_format_parent(&$table){
         $this->table =& $table;
     }
+    
+    function set_table(&$table){
+        $this->table =& $table;
+    }
 
     function add_data($row) {
         return false;
@@ -1244,7 +1267,8 @@ class table_default_export_format_parent{
     function add_seperator() {
         return false;
     }
-    function finish_output(){
+    function document_started(){
+        return $this->documentstarted;
     }
 }
 
@@ -1271,22 +1295,21 @@ class table_spreadsheet_export_format_parent extends table_default_export_format
      */
     function define_workbook(){
     }
-    function start_output($filename, $sheettitle){
-        $this->filename = $filename.'.'.$this->fileextension;
+    function start_document($filename){
+        $filename = $filename.'.'.$this->fileextension;
         $this->define_workbook();
-        // Creating the first worksheet
-        $this->worksheet =& $this->workbook->add_worksheet();
         // format types
         $this->formatnormal =& $this->workbook->add_format();
         $this->formatnormal->set_bold(0);
         $this->formatheaders =& $this->workbook->add_format();
         $this->formatheaders->set_bold(1);
         $this->formatheaders->set_align('center');
-
         // Sending HTTP headers
-        $this->workbook->send($this->filename);
-        // Creating the first worksheet
-
+        $this->workbook->send($filename);
+        $this->documentstarted = true;
+    }
+    function start_table($sheettitle){
+        $this->worksheet =& $this->workbook->add_worksheet($sheettitle);
         $this->rownum=0;
     }
     function output_headers($headers){
@@ -1310,7 +1333,10 @@ class table_spreadsheet_export_format_parent extends table_default_export_format
         $this->rownum++;
         return true;
     }
-    function finish_output(){
+
+    function finish_table(){
+    }
+    function finish_document(){
         $this->workbook->close();
         exit;
     }
@@ -1340,23 +1366,29 @@ class table_ods_export_format extends table_spreadsheet_export_format_parent{
 
 class table_text_export_format_parent extends table_default_export_format_parent{
     var $seperator = "\t";
-    function start_output($filename, $sheettitle){
+    function start_document($filename){
         $this->filename = $filename.".txt";
-
         header("Content-Type: application/download\n");
-        header("Content-Disposition: attachment; filename=\"$this->filename\"");
+        header("Content-Disposition: attachment; filename=\"{$filename}.txt\"");
         header("Expires: 0");
         header("Cache-Control: must-revalidate,post-check=0,pre-check=0");
         header("Pragma: public");
+        $this->documentstarted = true;
+    }
+    function start_table($sheettitle){
+        //nothing to do here
     }
     function output_headers($headers){
-        echo implode($this->seperator, $headers)." \n";
+        echo implode($this->seperator, $headers)."\n";
     }
     function add_data($row){
-        echo implode($this->seperator, $row)." \n";
+        echo implode($this->seperator, $row)."\n";
         return true;
     }
-    function finish_output(){
+    function finish_table(){
+        echo "\n\n";
+    }
+    function finish_document(){
         exit;
     }
 }
@@ -1372,20 +1404,13 @@ class table_csv_export_format extends table_text_export_format_parent{
 }
 
 class table_xhtml_export_format extends table_default_export_format_parent{
-    var $seperator = "\t";
-    function start_output($filename, $sheettitle){
-        $this->table->sortable(false);
-        $this->table->collapsible(false);
-        $this->filename = $filename.".html";
-
+    function start_document($filename){
         header("Content-Type: application/download\n");
-        header("Content-Disposition: attachment; filename=\"$this->filename\"");
+        header("Content-Disposition: attachment; filename=\"$filename.html\"");
         header("Expires: 0");
         header("Cache-Control: must-revalidate,post-check=0,pre-check=0");
         header("Pragma: public");
-
         //html headers
-
         echo <<<EOF
 <?xml version="1.0" encoding="UTF-8"?>
 <!DOCTYPE html
@@ -1431,7 +1456,7 @@ table {
     margin:auto;
 }
 
-h1{
+h1, h2{
     text-align:center;
 }
 .bold {
@@ -1441,14 +1466,18 @@ font-weight:bold;
 
 
 /*]]>*/</style>
-<head>
-  <title>$sheettitle</title>
-</head>
 <body>
-<h1>$sheettitle</h1>
 EOF;
+        $this->documentstarted = true;
+    }
+    function start_table($sheettitle){
+        $this->table->sortable(false);
+        $this->table->collapsible(false);
+        echo "<h2>{$sheettitle}</h2>";
         $this->table->start_html();
     }
+
+    
     function output_headers($headers){
         $this->table->print_headers();
     }
@@ -1460,8 +1489,10 @@ EOF;
         $this->table->print_row(NULL);
         return true;
     }
-    function finish_output(){
+    function finish_table(){
         $this->table->finish_html();
+    }
+    function finish_document(){
         echo '</body>';
         exit;
     }
index 41b50f3ede802d2ed09be5dacf4a192b176b1346..417b71dd2f89bcdb3dd86cea0bf939cb0fbf1e69 100644 (file)
@@ -312,6 +312,6 @@ function quiz_report_scale_sumgrades_as_percentage($rawgrade, $quiz, $round = tr
     } else {
         $grade = 0;
     }
-    return $grade.' %';
+    return $grade.'%';
 }
 ?>
index 0c4537b04b921598d7f120d07822f3b590ba7f06..c6f6aab077c838ded67a28daafd12784bcfadd08 100644 (file)
@@ -2,7 +2,7 @@
 function quiz_report_statistics_cron(){
     global $DB;
     if ($todelete = $DB->get_records_select_menu('quiz_statistics', 'timemodified < ?', array(time()-5*HOURSECS))){
-        list($todeletesql, $todeleteparams) = $DB->get_in_or_equal($todelete);
+        list($todeletesql, $todeleteparams) = $DB->get_in_or_equal(array_keys($todelete));
         if (!$DB->delete_records_select('quiz_statistics', "id $todeletesql", $todeleteparams)){
             mtrace('Error deleting out of date quiz_statistics records.');
         }
index 1410e39837bbe7d83d4bd7cfeff4fb04465b939e..89f61b7098e9b3142edd3b9867a124d5d7894a8e 100644 (file)
@@ -1,5 +1,5 @@
 <?xml version="1.0" encoding="UTF-8" ?>
-<XMLDB PATH="mod/quiz/report/statistics/db" VERSION="20080725" COMMENT="XMLDB file for Moodle mod/quiz/report/statistics"
+<XMLDB PATH="mod/quiz/report/statistics/db" VERSION="20080728" COMMENT="XMLDB file for Moodle mod/quiz/report/statistics"
     xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
     xsi:noNamespaceSchemaLocation="../../../../../lib/xmldb/xmldb.xsd"
 >
@@ -39,7 +39,9 @@
         <FIELD NAME="discriminativeefficiency" TYPE="number" LENGTH="15" NOTNULL="false" UNSIGNED="false" SEQUENCE="false" ENUM="false" DECIMALS="5" PREVIOUS="discriminationindex" NEXT="sd"/>
         <FIELD NAME="sd" TYPE="number" LENGTH="15" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" ENUM="false" DECIMALS="10" PREVIOUS="discriminativeefficiency" NEXT="facility"/>
         <FIELD NAME="facility" TYPE="number" LENGTH="15" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" ENUM="false" DECIMALS="10" PREVIOUS="sd" NEXT="subquestions"/>
-        <FIELD NAME="subquestions" TYPE="text" LENGTH="medium" NOTNULL="false" SEQUENCE="false" ENUM="false" PREVIOUS="facility"/>
+        <FIELD NAME="subquestions" TYPE="text" LENGTH="medium" NOTNULL="false" SEQUENCE="false" ENUM="false" PREVIOUS="facility" NEXT="maxgrade"/>
+        <FIELD NAME="maxgrade" TYPE="int" LENGTH="10" NOTNULL="false" UNSIGNED="true" SEQUENCE="false" ENUM="false" PREVIOUS="subquestions" NEXT="positions"/>
+        <FIELD NAME="positions" TYPE="text" LENGTH="medium" NOTNULL="false" SEQUENCE="false" ENUM="false" COMMENT="positions in which this item appears. Only used for random questions." PREVIOUS="maxgrade"/>
       </FIELDS>
       <KEYS>
         <KEY NAME="primary" TYPE="primary" FIELDS="id"/>
index b2893f9584326e4cd98633d30145863158e3a33b..10489e9b656d80c0e89eb22dd1fe93c395580a60 100644 (file)
@@ -26,6 +26,32 @@ function xmldb_quizreport_statistics_upgrade($oldversion=0) {
         }
 
     }
+    if ($result && $oldversion < 2008072800) {
+
+    /// Define field maxgrade to be added to quiz_question_statistics
+        $table = new xmldb_table('quiz_question_statistics');
+        $field = new xmldb_field('maxgrade', XMLDB_TYPE_INTEGER, '10', XMLDB_UNSIGNED, null, null, null, null, null, 'subquestions');
+
+    /// Conditionally launch add field maxgrade
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+    }
+
+    if ($result && $oldversion < 2008072801) {
+
+    /// Define field positions to be added to quiz_question_statistics
+        $table = new xmldb_table('quiz_question_statistics');
+        $field = new xmldb_field('positions', XMLDB_TYPE_TEXT, 'medium', null, null, null, null, null, null, 'maxgrade');
+
+    /// Conditionally launch add field positions
+        if (!$dbman->field_exists($table, $field)) {
+            $dbman->add_field($table, $field);
+        }
+
+    }
+    
     return $result;
 }
 
index 3177e2ba828b20739a9cb94be5bd41ecd83247b8..aeca387e9b52e88345f906f5b5bddef26cbc9699 100644 (file)
@@ -71,9 +71,11 @@ class qstats{
         } else {
             $stats->othergradesarray[] = $state->sumgrades;
         }
+
     }
 
     function _secondary_states_walker($state, &$stats){
+        global $QTYPES;
         $gradedifference = ($state->grade - $stats->gradeaverage);
         $othergradedifference = (($state->sumgrades - $state->grade) - $stats->othergradeaverage);
         $overallgradedifference = $state->sumgrades - $this->sumgradesavg;
@@ -84,23 +86,34 @@ class qstats{
         $stats->covariancesum += $gradedifference * $othergradedifference;
         $stats->covariancemaxsum += $sortedgradedifference * $sortedothergradedifference;
         $stats->covariancewithoverallgradesum += $gradedifference * $overallgradedifference;
+
+
     }
 
 
-    function _initial_question_walker(&$stats, $grade){
+    function _initial_question_walker(&$stats){
         $stats->gradeaverage = $stats->totalgrades / $stats->s;
-        $stats->facility = $stats->gradeaverage / $grade;
+        $stats->facility = $stats->gradeaverage / $stats->maxgrade;
         $stats->othergradeaverage = $stats->totalothergrades / $stats->s;
         sort($stats->gradearray, SORT_NUMERIC);
         sort($stats->othergradesarray, SORT_NUMERIC);
     }
     function _secondary_question_walker(&$stats){
-        $stats->gradevariance = $stats->gradevariancesum / ($stats->s -1);
-        $stats->othergradevariance = $stats->othergradevariancesum / ($stats->s -1);
-        $stats->covariance = $stats->covariancesum / ($stats->s -1);
-        $stats->covariancemax = $stats->covariancemaxsum / ($stats->s -1);
-        $stats->covariancewithoverallgrade = $stats->covariancewithoverallgradesum / ($stats->s-1);
-        $stats->sd = sqrt($stats->gradevariancesum / ($stats->s -1));
+        if ($stats->s > 1){
+            $stats->gradevariance = $stats->gradevariancesum / ($stats->s -1);
+            $stats->othergradevariance = $stats->othergradevariancesum / ($stats->s -1);
+            $stats->covariance = $stats->covariancesum / ($stats->s -1);
+            $stats->covariancemax = $stats->covariancemaxsum / ($stats->s -1);
+            $stats->covariancewithoverallgrade = $stats->covariancewithoverallgradesum / ($stats->s-1);
+            $stats->sd = sqrt($stats->gradevariancesum / ($stats->s -1));
+        } else {
+            $stats->gradevariance = null;
+            $stats->othergradevariance = null;
+            $stats->covariance = null;
+            $stats->covariancemax = null;
+            $stats->covariancewithoverallgrade = null;
+            $stats->sd = null;
+        }
         //avoid divide by zero
         if ($stats->gradevariance * $stats->othergradevariance){
             $stats->discriminationindex = 100*$stats->covariance 
@@ -121,23 +134,24 @@ class qstats{
             $this->_initial_states_walker($state, $this->questions[$state->question]->_stats);
             //if this is a random question what is the real item being used?
             if ($this->questions[$state->question]->qtype == 'random'){
-                if ($itemid = question_get_real_questionid($state)){
-                    if (!isset($subquestionstats[$itemid])){
-                        $subquestionstats[$itemid] = $this->stats_init_object();
-                        $subquestionstats[$itemid]->usedin = array();
-                        $subquestionstats[$itemid]->subquestion = true;
-                        $subquestionstats[$itemid]->differentweights = false;
-                        $subquestionstats[$itemid]->maxgrade = $this->questions[$state->question]->maxgrade;
-                    } else if ($subquestionstats[$itemid]->maxgrade != $this->questions[$state->question]->maxgrade){
-                        $subquestionstats[$itemid]->differentweights = true;
+                if ($realstate = question_get_real_state($state)){
+                    if (!isset($subquestionstats[$realstate->question])){
+                        $subquestionstats[$realstate->question] = $this->stats_init_object();
+                        $subquestionstats[$realstate->question]->usedin = array();
+                        $subquestionstats[$realstate->question]->subquestion = true;
+                        $subquestionstats[$realstate->question]->differentweights = false;
+                        $subquestionstats[$realstate->question]->maxgrade = $this->questions[$state->question]->maxgrade;
+                    } else if ($subquestionstats[$realstate->question]->maxgrade != $this->questions[$state->question]->maxgrade){
+                        $subquestionstats[$realstate->question]->differentweights = true;
                     }
-                    $this->_initial_states_walker($state, $subquestionstats[$itemid], false);
-                    $subquestionstats[$itemid]->usedin[$state->question] = $state->question;
+                    $this->_initial_states_walker($realstate, $subquestionstats[$realstate->question], false);
+                    $number = $this->questions[$state->question]->number;
+                    $subquestionstats[$realstate->question]->usedin[$number] = $number;
                     $randomselectorstring = $this->questions[$state->question]->category.'/'.$this->questions[$state->question]->questiontext;
                     if (!isset($this->randomselectors[$randomselectorstring])){
                         $this->randomselectors[$randomselectorstring] = array();
                     }
-                    $this->randomselectors[$randomselectorstring][$itemid] = $itemid;
+                    $this->randomselectors[$randomselectorstring][$realstate->question] = $realstate->question;
                 }
             }
         }
@@ -148,18 +162,25 @@ class qstats{
         foreach (array_keys($this->subquestions) as $qid){
             $this->subquestions[$qid]->_stats = $subquestionstats[$qid];
             $this->subquestions[$qid]->_stats->questionid = $qid;
-            $this->subquestions[$qid]->maxgrade = $this->subquestions[$qid]->_stats->maxgrade;
-            $this->_initial_question_walker($this->subquestions[$qid]->_stats, $this->subquestions[$qid]->_stats->maxgrade);
+            $this->_initial_question_walker($this->subquestions[$qid]->_stats);
             if ($subquestionstats[$qid]->differentweights){
                 notify(get_string('erroritemappearsmorethanoncewithdifferentweight', 'quiz_statistics', $this->subquestions[$qid]->name));
             }
+            if ($this->subquestions[$qid]->_stats->usedin){
+                sort($this->subquestions[$qid]->_stats->usedin, SORT_NUMERIC);
+                $this->subquestions[$qid]->_stats->positions = join($this->subquestions[$qid]->_stats->usedin, ',');
+            } else {
+                $this->subquestions[$qid]->_stats->positions = '';
+            }
         }
         reset($this->questions);
         do{
             list($qid, $question) = each($this->questions);
             $nextquestion = current($this->questions);
             $this->questions[$qid]->_stats->questionid = $qid;
-            $this->_initial_question_walker($this->questions[$qid]->_stats, $this->questions[$qid]->maxgrade);
+            $this->questions[$qid]->_stats->positions = $this->questions[$qid]->number;
+            $this->questions[$qid]->_stats->maxgrade = $question->maxgrade;
+            $this->_initial_question_walker($this->questions[$qid]->_stats);
             if ($question->qtype == 'random'){
                 $randomselectorstring = $question->category.'/'.$question->questiontext;
                 if ($nextquestion){
@@ -177,8 +198,8 @@ class qstats{
         foreach ($this->states as $state){
             $this->_secondary_states_walker($state, $this->questions[$state->question]->_stats);
             if ($this->questions[$state->question]->qtype == 'random'){
-                if ($itemid = question_get_real_questionid($state)){
-                    $this->_secondary_states_walker($state, $this->subquestions[$itemid]->_stats);
+                if ($realstate = question_get_real_state($state)){
+                    $this->_secondary_states_walker($realstate, $this->subquestions[$realstate->question]->_stats);
                 }
             }
         }
@@ -192,8 +213,12 @@ class qstats{
             $this->_secondary_question_walker($this->subquestions[$qid]->_stats);
         }
         foreach (array_keys($this->questions) as $qid){
-            $this->questions[$qid]->_stats->effectiveweight = 100 * sqrt($this->questions[$qid]->_stats->covariancewithoverallgrade)
-                        /   $sumofcovariancewithoverallgrade;
+            if ($sumofcovariancewithoverallgrade){
+                $this->questions[$qid]->_stats->effectiveweight = 100 * sqrt($this->questions[$qid]->_stats->covariancewithoverallgrade)
+                            /   $sumofcovariancewithoverallgrade;
+            } else {
+                $this->questions[$qid]->_stats->effectiveweight = null;
+            }
         }
     }
     /**
index fab9d3a889003ac89e8734352b6f732af0570313..3f9ad7556cd901b789829e3f9e98864dad97e124 100644 (file)
@@ -13,6 +13,11 @@ require_once($CFG->dirroot.'/mod/quiz/report/statistics/statistics_form.php');
 require_once($CFG->dirroot.'/mod/quiz/report/statistics/statistics_table.php');
 
 class quiz_statistics_report extends quiz_default_report {
+    
+    /**
+     * @var object instance of table class used for main questions stats table.
+     */
+    var $table;
 
     /**
      * Display the report.
@@ -24,6 +29,8 @@ class quiz_statistics_report extends quiz_default_report {
 
         $download = optional_param('download', '', PARAM_ALPHA);
         $recalculate = optional_param('recalculate', 0, PARAM_BOOL);
+        //pass the question id for detailed analysis question
+        $qid = optional_param('qid', 0, PARAM_INT);
         $pageoptions = array();
         $pageoptions['id'] = $cm->id;
         $pageoptions['q'] = $quiz->id;
@@ -52,6 +59,7 @@ class quiz_statistics_report extends quiz_default_report {
 
         /// find out current groups mode
         $currentgroup = groups_get_activity_group($cm, true);
+        
 
         $nostudentsingroup = false;//true if a group is selected and their is noeone in it.
         if (!empty($currentgroup)) {
@@ -63,132 +71,196 @@ class quiz_statistics_report extends quiz_default_report {
         } else {
             $groupstudents = array();
         }
-
         
-        $table = new quiz_report_statistics_table();
-        $table->is_downloading($download, get_string('reportstatistics','quiz_statistics'),
-                    "$course->shortname ".format_string($quiz->name,true));
-        if (!$table->is_downloading()) {
+        if ($recalculate){
+            if ($todelete = $DB->get_records_menu('quiz_statistics', array('quizid' => $quiz->id, 'groupid'=> (int)$currentgroup, 'allattempts'=>$useallattempts))){
+                list($todeletesql, $todeleteparams) = $DB->get_in_or_equal(array_keys($todelete));
+                if (!$DB->delete_records_select('quiz_statistics', "id $todeletesql", $todeleteparams)){
+                    print_error('errordeletingquizstats', 'quiz_statistics');
+                }
+                if (!$DB->delete_records_select('quiz_question_statistics', "quizstatisticsid $todeletesql", $todeleteparams)){
+                    print_error('errordeletingqstats', 'quiz_statistics');
+                }
+            }
+            redirect($reporturl->out());
+        }
+        
+        
+        $this->table = new quiz_report_statistics_table();
+        $filename = "$course->shortname-".format_string($quiz->name,true);
+        $this->table->is_downloading($download, $filename, get_string('quizstructureanalysis', 'quiz_statistics'));
+        if (!$this->table->is_downloading()) {
             // Only print headers if not asked to download data
             $this->print_header_and_tabs($cm, $course, $quiz, "statistics");
         }
 
         if ($groupmode = groups_get_activity_groupmode($cm)) {   // Groups are being used
-            if (!$table->is_downloading()) {
+            if (!$this->table->is_downloading()) {
                 groups_print_activity_menu($cm, $reporturl->out());
+                echo '<br />';
                 if ($currentgroup && !$groupstudents){
                     notify(get_string('nostudentsingroup', 'quiz_statistics'));
                 }
             }
         }
 
-        if (!$table->is_downloading()) {
+        if (!$this->table->is_downloading()) {
             // Print display options
             $mform->set_data(array('useallattempts' => $useallattempts));
             $mform->display();
         }
-        // Print information on the number of existing attempts
-        if (!$table->is_downloading()) { //do not print notices when downloading
-            print_heading(get_string('quizinformation', 'quiz_statistics'));
-            $quizinformationtable = new object();
-            $quizinformationtable->align = array('center', 'center');
-            $quizinformationtable->width = '60%';
-            $quizinformationtable->class = 'generaltable titlesleft';
-            $quizinformationtable->data = array();
-            $quizinformationtable->data[] = array(get_string('quizname', 'quiz_statistics'), $quiz->name);
-            $quizinformationtable->data[] = array(get_string('coursename', 'quiz_statistics'), $course->fullname);
-            if ($cm->idnumber){
-                $quizinformationtable->data[] = array(get_string('coursename', 'quiz_statistics'), $cm->idnumber);
-            }
-            if ($quiz->timeopen){
-                $quizinformationtable->data[] = array(get_string('quizopen', 'quiz'), userdate($quiz->timeopen));
-            }
-            if ($quiz->timeclose){
-                $quizinformationtable->data[] = array(get_string('quizclose', 'quiz'), userdate($quiz->timeclose));
-            }
-            if ($quiz->timeopen && $quiz->timeclose){
-                $quizinformationtable->data[] = array(get_string('duration', 'quiz_statistics'), format_time($quiz->timeclose - $quiz->timeopen));
-            }
-        }
 
-        $timemodified = time() - QUIZ_REPORT_TIME_TO_CACHE_STATS;
-        $params = array('quizid'=>$quiz->id, 'groupid'=>$currentgroup, 'allattempts'=>$useallattempts, 'timemodified'=>$timemodified);
-        if ($recalculate || !$quizstats = $DB->get_record_select('quiz_statistics', 'quizid = :quizid  AND groupid = :groupid AND allattempts = :allattempts AND timemodified > :timemodified', $params, '*', true)){
-            list($s, $usingattemptsstring, $quizstats, $qstats) = $this->quiz_stats($nostudentsingroup, $quiz->id, $currentgroup, $groupstudents, $questions, $useallattempts);
-            $toinsert = (object)((array)$quizstats + $params);
-            $toinsert->timemodified = time();
-            $quizstatisticsid = $DB->insert_record('quiz_statistics', $toinsert);
-            foreach ($qstats->questions as $question){
-                $question->_stats->quizstatisticsid = $quizstatisticsid;
-                $DB->insert_record('quiz_question_statistics', $question->_stats, false, true);
-            }
-            foreach ($qstats->subquestions as $subquestion){
-                $subquestion->_stats->quizstatisticsid = $quizstatisticsid;
-                $DB->insert_record('quiz_question_statistics', $subquestion->_stats, false, true);
-            }
-            if (isset($qstats)){
-                $questions = $qstats->questions;
-                $subquestions = $qstats->subquestions;
-            } else {
-                $questions = array();
-                $subquestions = array();
+        list($quizstats, $questions, $subquestions, $s, $usingattemptsstring) 
+            = $this->quiz_questions_stats($quiz, $currentgroup, $nostudentsingroup,
+                                        $useallattempts, $groupstudents, $questions);
+
+        if (!$this->table->is_downloading()){
+            if ($s==0){
+                print_heading(get_string('noattempts','quiz'));
             }
+        }
+        if ($s){
+            $this->table->setup($quiz, $cm->id, $reporturl, $s);
+        }
+        
+        if (!$qid){
+            $this->output_quiz_stats_table($course, $cm, $quiz, $quizstats, $usingattemptsstring, $currentgroup, $groupstudents, $useallattempts, $download, $reporturl);
+            $this->output_question_stats_table($s, $questions, $subquestions);
         } else {
-            if ($useallattempts){
-                $usingattemptsstring = get_string('allattempts', 'quiz_statistics');
-                $s = $quizstats->allattemptscount;
+            $thisquestion = false;
+            if (isset($questions[$qid])){
+                $thisquestion = $questions[$qid];
+            } else if (isset($subquestions[$qid])){
+                $thisquestion = $subquestions[$qid];
             } else {
-                $usingattemptsstring = get_string('firstattempts', 'quiz_statistics');
-                $s = $quizstats->firstattemptscount;
+                print_error('questiondoesnotexist', 'question');
             }
-            $questionstats = $DB->get_records('quiz_question_statistics', array('quizstatisticsid'=>$quizstats->id), 'subquestion ASC');
-            $questionstats = quiz_report_index_by_keys($questionstats, array('subquestion', 'questionid'));
-            if (1 < count($questionstats)){
-                list($mainquestionstats, $subquestionstats) = $questionstats;
-                $subqstofetch = array_keys($subquestionstats);
-                $subquestions = question_load_questions($subqstofetch);
-                foreach (array_keys($subquestions) as $subqid){
-                    $subquestions[$subqid]->_stats = $subquestionstats[$subqid];
+            $this->output_question_info_table($quiz, $thisquestion);
+        }
+        return true;
+    }
+    
+    function output_question_info_table($quiz, $question){
+        $datumfromtable = $this->table->format_row($question);
+
+        $questioninfotable = new object();
+        $questioninfotable->align = array('center', 'center');
+        $questioninfotable->width = '60%';
+        $questioninfotable->class = 'generaltable titlesleft';
+        
+        $questioninfotable->data = array();
+        $questioninfotable->data[] = array(get_string('modulename', 'quiz'), $quiz->name);
+        $questioninfotable->data[] = array(get_string('questionname', 'quiz_statistics'), $question->name.'&nbsp;'.$datumfromtable['actions']);
+        $questioninfotable->data[] = array(get_string('questiontype', 'quiz_statistics'), $question->qtype.'&nbsp;'.$datumfromtable['icon']);
+        $questioninfotable->data[] = array(get_string('positions', 'quiz_statistics'), $question->_stats->positions);
+
+        $questionstatstable = new object();
+        $questionstatstable->align = array('center', 'center');
+        $questionstatstable->width = '60%';
+        $questionstatstable->class = 'generaltable titlesleft';
+
+        unset($datumfromtable['number']);
+        unset($datumfromtable['icon']);
+        $actions = $datumfromtable['actions'];
+        unset($datumfromtable['actions']);
+        unset($datumfromtable['name']);
+        $labels = array('s' => get_string('attempts', 'quiz_statistics'),
+                        'facility' => get_string('facility', 'quiz_statistics'),
+                        'sd' => get_string('standarddeviationq', 'quiz_statistics'),
+                        'random_guess_score' => get_string('random_guess_score', 'quiz_statistics'),
+                        'intended_weight'=> get_string('intended_weight', 'quiz_statistics'),
+                        'effective_weight'=> get_string('effective_weight', 'quiz_statistics'),
+                        'discrimination_index'=> get_string('discrimination_index', 'quiz_statistics'),
+                        'discriminative_efficiency'=> get_string('discriminative_efficiency', 'quiz_statistics'));
+        foreach ($datumfromtable as $item => $value){
+            $questionstatstable->data[] = array($labels[$item], $value);
+        }
+
+        print_heading(get_string('questioninformation', 'quiz_statistics'));
+        print_table($questioninfotable);
+        
+        print_box(format_text($question->questiontext).$actions, 'boxaligncenter generalbox boxwidthnormal mdl-align');
+
+        print_heading(get_string('questionstatistics', 'quiz_statistics'));
+        print_table($questionstatstable);
+        
+        print_heading(get_string('analysisofresponses', 'quiz_statistics'));
+
+    }
+        
+    function output_question_stats_table($s, $questions, $subquestions){
+        if (!$this->table->is_downloading()){
+            print_heading(get_string('quizstructureanalysis', 'quiz_statistics'));
+        }
+        if ($s){
+            foreach ($questions as $question){
+                $this->table->add_data_keyed($this->table->format_row($question));
+                if (!empty($question->_stats->subquestions)){
+                    $subitemstodisplay = explode(',', $question->_stats->subquestions);
+                    foreach ($subitemstodisplay as $subitemid){
+                        $subquestions[$subitemid]->maxgrade = $question->maxgrade;
+                        $this->table->add_data_keyed($this->table->format_row($subquestions[$subitemid]));
+                    }
                 }
-            } else {
-                $mainquestionstats = $questionstats[0];
-                $subquestions = array();
-            }
-            foreach (array_keys($questions) as $qid){
-                $questions[$qid]->_stats = $mainquestionstats[$qid];
             }
+
+            $this->table->finish_output();
         }
-        if (!$table->is_downloading()){
-            if ($s==0){
-                print_heading(get_string('noattempts','quiz'));
+    }
+    
+    function output_quiz_stats_table($course, $cm, $quiz, $quizstats, $usingattemptsstring, $currentgroup, $groupstudents, $useallattempts, $download, $reporturl){
+        global $DB;
+        // Print information on the number of existing attempts
+        $quizinformationtablehtml = print_heading(get_string('quizinformation', 'quiz_statistics'), '', 2, 'main', true);
+        $quizinformationtable = new object();
+        $quizinformationtable->align = array('center', 'center');
+        $quizinformationtable->width = '60%';
+        $quizinformationtable->class = 'generaltable titlesleft';
+        $quizinformationtable->data = array();
+        $quizinformationtable->data[] = array(get_string('quizname', 'quiz_statistics'), $quiz->name);
+        $quizinformationtable->data[] = array(get_string('coursename', 'quiz_statistics'), $course->fullname);
+        if ($cm->idnumber){
+            $quizinformationtable->data[] = array(get_string('coursename', 'quiz_statistics'), $cm->idnumber);
+        }
+        if ($quiz->timeopen){
+            $quizinformationtable->data[] = array(get_string('quizopen', 'quiz'), userdate($quiz->timeopen));
+        }
+        if ($quiz->timeclose){
+            $quizinformationtable->data[] = array(get_string('quizclose', 'quiz'), userdate($quiz->timeclose));
+        }
+        if ($quiz->timeopen && $quiz->timeclose){
+            $quizinformationtable->data[] = array(get_string('duration', 'quiz_statistics'), format_time($quiz->timeclose - $quiz->timeopen));
+        }
+        $format = array('firstattemptscount' => '',
+                    'allattemptscount' => '',
+                    'firstattemptsavg' => 'sumgrades_as_percentage',
+                    'allattemptsavg' => 'sumgrades_as_percentage',
+                    'median' => 'sumgrades_as_percentage',
+                    'standarddeviation' => 'sumgrades_as_percentage',
+                    'skewness' => '',
+                    'kurtosis' => '',
+                    'cic' => 'number_format',
+                    'errorratio' => 'number_format',
+                    'standarderror' => 'sumgrades_as_percentage');
+        foreach ($quizstats as $property => $value){
+            if (!isset($format[$property])){
+                continue;
             }
-            $format = array('firstattemptscount' => '',
-                        'allattemptscount' => '',
-                        'firstattemptsavg' => 'sumgrades_as_percentage',
-                        'allattemptsavg' => 'sumgrades_as_percentage',
-                        'median' => 'sumgrades_as_percentage',
-                        'standarddeviation' => 'sumgrades_as_percentage',
-                        'skewness' => '',
-                        'kurtosis' => '',
-                        'cic' => 'number_format',
-                        'errorratio' => 'number_format',
-                        'standarderror' => 'sumgrades_as_percentage');
-            foreach ($quizstats as $property => $value){
-                if (!isset($format[$property])){
-                    continue;
-                }
+            if (!is_null($value)){
                 switch ($format[$property]){
                     case 'sumgrades_as_percentage' :
                         $formattedvalue = quiz_report_scale_sumgrades_as_percentage($value, $quiz);
                         break;
                     case 'number_format' :
-                        $formattedvalue = number_format($value, $quiz->decimalpoints).' %';
+                        $formattedvalue = number_format($value, $quiz->decimalpoints).'%';
                         break;
                     default :
                         $formattedvalue = $value;
                 }
                 $quizinformationtable->data[] = array(get_string($property, 'quiz_statistics', $usingattemptsstring), $formattedvalue);
             }
+        }
+        if (!$this->table->is_downloading()){
             if (isset($quizstats->timemodified)){
                 list($fromqa, $whereqa, $qaparams) = quiz_report_attempts_sql($quiz->id, $currentgroup, $groupstudents, $useallattempts);
                 $sql = 'SELECT COUNT(1) ' .
@@ -198,39 +270,36 @@ class quiz_statistics_report extends quiz_default_report {
                 $a->lastcalculated = format_time(time() - $quizstats->timemodified);
                 if (!$a->count = $DB->count_records_sql($sql, array('time'=>$quizstats->timemodified)+$qaparams)){
                     $a->count = 0;
-                } 
-                print_box_start('boxaligncenter generalbox boxwidthnormal mdl-align');
-                echo get_string('lastcalculated', 'quiz_statistics', $a);
-                print_single_button($reporturl->out(true), $reporturl->params()+array('recalculate'=>1),
-                                    get_string('recalculatenow', 'quiz_statistics'), 'post');
-                print_box_end();
+                }
+                $quizinformationtablehtml .= print_box_start('boxaligncenter generalbox boxwidthnormal mdl-align', '', true);
+                $quizinformationtablehtml .= get_string('lastcalculated', 'quiz_statistics', $a);
+                $quizinformationtablehtml .= print_single_button($reporturl->out(true), $reporturl->params()+array('recalculate'=>1),
+                                    get_string('recalculatenow', 'quiz_statistics'), 'post', '', true);
+                $quizinformationtablehtml .= print_box_end(true);
             }
-            print_table($quizinformationtable);
-            
         }
-        if (!$table->is_downloading()){
-            print_heading(get_string('quizstructureanalysis', 'quiz_statistics'));
-        }
-        if ($s){
-            $table->setup($quiz, $cm->id, $reporturl, $s);
-            
-            foreach ($questions as $question){
-                $table->add_data_keyed($table->format_row($question));
-                if (!empty($question->_stats->subquestions)){
-                    $subitemstodisplay = explode(',', $question->_stats->subquestions);
-                    foreach ($subitemstodisplay as $subitemid){
-                        $subquestions[$subitemid]->maxgrade = $question->maxgrade;
-                        $table->add_data_keyed($table->format_row($subquestions[$subitemid]));
-                    }
+        $quizinformationtablehtml .= print_table($quizinformationtable, true);
+        if (!$this->table->is_downloading()){
+            echo $quizinformationtablehtml;
+        } else {
+            $exportclass =& $this->table->export_class_instance();
+            if ($download == 'xhtml'){
+                echo $quizinformationtablehtml;
+            } else {
+                $exportclass->start_table(get_string('quizinformation', 'quiz_statistics'));
+                $headers = array();
+                $row = array();
+                foreach ($quizinformationtable->data as $data){
+                    $headers[]= $data[0];
+                    $row[] = $data[1];
                 }
+                $exportclass->output_headers($headers);
+                $exportclass->add_data($row);
+                $exportclass->finish_table();
             }
-
-            $table->finish_output();
         }
-        return true;
     }
-    
-    
+
     function quiz_stats($nostudentsingroup, $quizid, $currentgroup, $groupstudents, $questions, $useallattempts){
         global $CFG, $DB;
         if (!$nostudentsingroup){
@@ -337,15 +406,17 @@ class quiz_statistics_report extends quiz_default_report {
                     
                     $k2= $s*$m2/($s-1);
                     $k3= $s*$s*$m3/(($s-1)*($s-2));
-                    
-                    $quizstats->skewness = $k3 / (pow($k2, 2/3));
+                    if ($k2){
+                        $quizstats->skewness = $k3 / (pow($k2, 2/3));
+                    }
                 }
     
     
                 if ($s>3){
                     $k4= (($s*$s*$s)/(($s-1)*($s-2)*($s-3)))*((($s+1)*$m4)-(3*($s-1)*$m2*$m2));
-                    
-                    $quizstats->kurtosis = $k4 / ($k2*$k2);
+                    if ($k2){
+                        $quizstats->kurtosis = $k4 / ($k2*$k2);
+                    }
                 }
             }
         }
@@ -353,7 +424,6 @@ class quiz_statistics_report extends quiz_default_report {
             require_once("$CFG->dirroot/mod/quiz/report/statistics/qstats.php");
             $qstats = new qstats($questions, $s, $sumgradesavg);
             $qstats->get_records($quizid, $currentgroup, $groupstudents, $useallattempts);
-            set_time_limit(0);
             $qstats->process_states();
         } else {
             $qstats = false;
@@ -361,15 +431,68 @@ class quiz_statistics_report extends quiz_default_report {
         if ($s>1){
             $p = count($qstats->questions);//no of positions
             if ($p > 1){
-                $quizstats->cic = (100 * $p / ($p -1)) * (1 - ($qstats->sum_of_grade_variance())/$k2);
-                $quizstats->errorratio = 100 * sqrt(1-($quizstats->cic/100));
-                $quizstats->standarderror = ($quizstats->errorratio * $quizstats->standarddeviation / 100);
-                
+                if ($k2){
+                    $quizstats->cic = (100 * $p / ($p -1)) * (1 - ($qstats->sum_of_grade_variance())/$k2);
+                    $quizstats->errorratio = 100 * sqrt(1-($quizstats->cic/100));
+                    $quizstats->standarderror = ($quizstats->errorratio * $quizstats->standarddeviation / 100);
+                }
             }
         }
         return array($s, $usingattemptsstring, $quizstats, $qstats);
     }
-
+    
+    function quiz_questions_stats($quiz, $currentgroup, $nostudentsingroup, $useallattempts, $groupstudents, $questions){
+        global $DB;
+        $timemodified = time() - QUIZ_REPORT_TIME_TO_CACHE_STATS;
+        $params = array('quizid'=>$quiz->id, 'groupid'=>(int)$currentgroup, 'allattempts'=>$useallattempts, 'timemodified'=>$timemodified);
+        if (!$quizstats = $DB->get_record_select('quiz_statistics', 'quizid = :quizid  AND groupid = :groupid AND allattempts = :allattempts AND timemodified > :timemodified', $params, '*', true)){
+            list($s, $usingattemptsstring, $quizstats, $qstats) = $this->quiz_stats($nostudentsingroup, $quiz->id, $currentgroup, $groupstudents, $questions, $useallattempts);
+            $toinsert = (object)((array)$quizstats + $params);
+            $toinsert->timemodified = time();
+            $quizstatisticsid = $DB->insert_record('quiz_statistics', $toinsert);
+            foreach ($qstats->questions as $question){
+                $question->_stats->quizstatisticsid = $quizstatisticsid;
+                $DB->insert_record('quiz_question_statistics', $question->_stats, false, true);
+            }
+            foreach ($qstats->subquestions as $subquestion){
+                $subquestion->_stats->quizstatisticsid = $quizstatisticsid;
+                $DB->insert_record('quiz_question_statistics', $subquestion->_stats, false, true);
+            }
+            if (isset($qstats)){
+                $questions = $qstats->questions;
+                $subquestions = $qstats->subquestions;
+            } else {
+                $questions = array();
+                $subquestions = array();
+            }
+        } else {
+            //use cached results
+            if ($useallattempts){
+                $usingattemptsstring = get_string('allattempts', 'quiz_statistics');
+                $s = $quizstats->allattemptscount;
+            } else {
+                $usingattemptsstring = get_string('firstattempts', 'quiz_statistics');
+                $s = $quizstats->firstattemptscount;
+            }
+            $questionstats = $DB->get_records('quiz_question_statistics', array('quizstatisticsid'=>$quizstats->id), 'subquestion ASC');
+            $questionstats = quiz_report_index_by_keys($questionstats, array('subquestion', 'questionid'));
+            if (1 < count($questionstats)){
+                list($mainquestionstats, $subquestionstats) = $questionstats;
+                $subqstofetch = array_keys($subquestionstats);
+                $subquestions = question_load_questions($subqstofetch);
+                foreach (array_keys($subquestions) as $subqid){
+                    $subquestions[$subqid]->_stats = $subquestionstats[$subqid];
+                }
+            } else {
+                $mainquestionstats = $questionstats[0];
+                $subquestions = array();
+            }
+            foreach (array_keys($questions) as $qid){
+                $questions[$qid]->_stats = $mainquestionstats[$qid];
+            }
+        }
+        return array($quizstats, $questions, $subquestions, $s, $usingattemptsstring);
+    }
 }
 function quiz_report_attempts_sql($quizid, $currentgroup, $groupstudents, $allattempts = true){
     global $DB;
@@ -386,4 +509,10 @@ function quiz_report_attempts_sql($quizid, $currentgroup, $groupstudents, $allat
     }
     return array($fromqa, $whereqa, $qaparams);
 }
+function quiz_report_safe_divider($dividend, $divisor){
+    if ($divisor == 0){
+        return null;
+    }
+    return $dividend / $divisor;
+}
 ?>
diff --git a/mod/quiz/report/statistics/statistics_graph.php b/mod/quiz/report/statistics/statistics_graph.php
new file mode 100644 (file)
index 0000000..4d906b0
--- /dev/null
@@ -0,0 +1,88 @@
+<?php  // $Id$
+include '../../../../config.php';
+include $CFG->dirroot."/lib/graphlib.php";
+include $CFG->dirroot."/mod/quiz/locallib.php";
+include $CFG->dirroot."/mod/quiz/report/reportlib.php";
+function graph_get_new_colour(){
+    static $colourindex = 0;
+    $colours = array('red', 'green', 'yellow', 'orange', 'purple', 'black', 'maroon', 'blue', 'ltgreen', 'navy', 'ltred', 'ltltgreen', 'ltltorange', 'olive', 'gray', 'ltltred', 'ltorange', 'lime', 'ltblue', 'ltltblue');
+    $colour = $colours[$colourindex];
+    $colourindex++;
+    if ($colourindex > (count($colours)-1)){
+        $colourindex =0;
+    }
+    return $colour;
+}
+$quizstatisticsid = required_param('id', PARAM_INT);
+
+$quizstatistics = $DB->get_record('quiz_statistics', array('id' => $quizstatisticsid));
+$questionstatistics = $DB->get_records('quiz_question_statistics', array('quizstatisticsid' => $quizstatistics->id, 'subquestion' => 0));
+$quiz = $DB->get_record('quiz', array('id' => $quizstatistics->quizid));
+require_login($quiz->course);
+$questions = quiz_report_load_questions($quiz);
+$cm = get_coursemodule_from_instance('quiz', $quiz->id);
+if ($groupmode = groups_get_activity_groupmode($cm)) {   // Groups are being used
+    $groups = groups_get_activity_allowed_groups($cm);
+} else {
+    $groups = false;
+}
+if ($quizstatistics->groupid){
+    if (!in_array($quizstatistics->groupid, $groups)){
+        print_error('groupnotamember', 'group');
+    }
+}
+$modcontext = get_context_instance(CONTEXT_MODULE, $cm->id);
+require_capability('mod/quiz:viewreports', $modcontext);
+
+$line = new graph(800,600);
+$line->parameter['title']   = '';
+$line->parameter['y_label_left'] = '%';
+$line->parameter['x_label'] = get_string('position', 'quiz_statistics');
+$line->parameter['y_label_angle'] = 90;
+$line->parameter['x_label_angle'] = 0;
+$line->parameter['x_axis_angle'] = 60;
+
+$line->parameter['legend']        = 'outside-top';
+$line->parameter['legend_border'] = 'black';
+$line->parameter['legend_offset'] = 4;
+
+
+$line->parameter['bar_size']    = 1.2; 
+$line->parameter['bar_spacing'] = 10; 
+
+$fieldstoplot = array('facility' => get_string('facility', 'quiz_statistics'), 'discriminativeefficiency' => get_string('discriminative_efficiency', 'quiz_statistics'));
+$fieldstoplotfactor = array('facility' => 100, 'discriminativeefficiency' => 1);
+
+$line->x_data = array();
+foreach (array_keys($fieldstoplot) as $fieldtoplot){
+    $line->y_data[$fieldtoplot] = array();
+    $line->y_format[$fieldtoplot] =
+            array('colour' => graph_get_new_colour(), 'bar' => 'fill', 'shadow_offset' => 1, 'legend' => $fieldstoplot[$fieldtoplot]);
+}
+foreach ($questionstatistics as $questionstatistic){
+    $line->x_data[] = $questions[$questionstatistic->questionid]->number;
+    foreach (array_keys($fieldstoplot) as $fieldtoplot){
+        $value = !is_null($questionstatistic->$fieldtoplot)?$questionstatistic->$fieldtoplot:0;
+        $value = $value * $fieldstoplotfactor[$fieldtoplot];
+        $line->y_data[$fieldtoplot][$questions[$questionstatistic->questionid]->number] = $value;
+    }
+}
+ksort($line->x_data);
+$max = 0;
+$min = 0;
+foreach (array_keys($fieldstoplot) as $fieldtoplot){
+    ksort($line->y_data[$fieldtoplot]);
+    $line->y_data[$fieldtoplot] = array_values($line->y_data[$fieldtoplot]);
+    $max = (max($line->y_data[$fieldtoplot])> $max)? max($line->y_data[$fieldtoplot]): $max;
+    $min = (min($line->y_data[$fieldtoplot])> $min)? min($line->y_data[$fieldtoplot]): $min;
+}
+$line->y_order = array_keys($fieldstoplot);
+
+
+$line->parameter['y_min_left'] = $min;  // start at 0
+$line->parameter['y_max_left'] = $max;
+$line->parameter['y_decimal_left'] = 0; 
+
+
+$line->draw();
+?>
index a4cdb1c694a2c0f23b50f7ed8a993969526ea8ef..366aefc1eb298c7160071be189957b1fb290010c 100644 (file)
@@ -59,7 +59,7 @@ class quiz_report_statistics_table extends flexible_table {
         
         $columns[]= 'discriminative_efficiency';
         $headers[]= get_string('discriminative_efficiency', 'quiz_statistics');
-        
+
         $this->define_columns($columns);
         $this->define_headers($headers);
         $this->sortable(false);
@@ -99,7 +99,13 @@ class quiz_report_statistics_table extends flexible_table {
 
 
     function col_name($question){
-        return $question->name;
+        if (!$this->is_downloading() && $question->qtype!='random'){
+            $tooltip = get_string('detailedanalysis', 'quiz_statistics');
+            $url = $this->baseurl .'&amp;qid='.$question->id;
+            return "<a title=\"$tooltip\" href=\"$url\">".$question->name."</a>";
+        } else {
+            return $question->name;
+        }
 
     }
     
@@ -121,25 +127,25 @@ class quiz_report_statistics_table extends flexible_table {
         return $question->qtype;
     }
     function col_intended_weight($question){
-        return quiz_report_scale_sumgrades_as_percentage($question->maxgrade, $this->quiz);
+        return quiz_report_scale_sumgrades_as_percentage($question->_stats->maxgrade, $this->quiz);
     }
     function col_effective_weight($question){
         if (!$question->_stats->subquestion){
-            return number_format($question->_stats->effectiveweight, 2).' %';
+            return number_format($question->_stats->effectiveweight, 2).'%';
         } else {
             return '';
         }
     }
     function col_discrimination_index($question){
         if (is_numeric($question->_stats->discriminationindex)){
-            return number_format($question->_stats->discriminationindex, 2).' %';
+            return number_format($question->_stats->discriminationindex, 2).'%';
         } else {
             return $question->_stats->discriminationindex;
         }
     }
     function col_discriminative_efficiency($question){
         if (is_numeric($question->_stats->discriminativeefficiency)){
-            return number_format($question->_stats->discriminativeefficiency, 2).' %';
+            return number_format($question->_stats->discriminativeefficiency, 2).'%';
         } else {
             return '';
         }
@@ -147,14 +153,14 @@ class quiz_report_statistics_table extends flexible_table {
     function col_random_guess_score($question){
         $randomguessscore = question_get_random_guess_score($question);
         if (is_numeric($randomguessscore)){
-            return number_format($randomguessscore * 100, 2).' %';
+            return number_format($randomguessscore * 100, 2).'%';
         } else {
             return $randomguessscore; // empty string returned by random question.
         }
     }
     
     function col_sd($question){
-        return number_format($question->_stats->sd*100 / $question->maxgrade, 2).' %';
+        return number_format($question->_stats->sd*100 / $question->_stats->maxgrade, 2).'%';
     }
     function col_s($question){
         if (isset($question->_stats->s)){
@@ -164,7 +170,8 @@ class quiz_report_statistics_table extends flexible_table {
         }
     }
     function col_facility($question){
-        return number_format($question->_stats->facility*100, 2).' %';
+        return number_format($question->_stats->facility*100, 2).'%';
     }
+    
 }
 ?>
index b522d2d478ff84b7f5abe0b4a64f814ff491af03..406f977934a812f07c921222d81d6eb7afb68673 100644 (file)
@@ -1,4 +1,4 @@
 <?php
-$plugin->version  = 2008072500;   // The (date) version of this module
+$plugin->version  = 2008072801;   // The (date) version of this module
 
 ?>
\ No newline at end of file