2008-06-10 16:18:10 +00:00
< ? php
/**
2008-07-14 06:40:36 +00:00
* This script calculates various statistics about student attempts
2008-06-10 16:18:10 +00:00
*
* @ version $Id $
2008-07-14 06:40:36 +00:00
* @ author Martin Dougiamas , Jamie Pratt , Tim Hunt and others .
2008-06-10 16:18:10 +00:00
* @ license http :// www . gnu . org / copyleft / gpl . html GNU Public License
* @ package quiz
2008-07-24 13:25:07 +00:00
**/
2008-06-10 16:18:10 +00:00
2008-07-24 13:25:07 +00:00
define ( 'QUIZ_REPORT_TIME_TO_CACHE_STATS' , MINSECS * 15 );
2008-06-10 16:18:10 +00:00
require_once ( $CFG -> dirroot . '/mod/quiz/report/statistics/statistics_form.php' );
require_once ( $CFG -> dirroot . '/mod/quiz/report/statistics/statistics_table.php' );
2008-06-24 08:59:29 +00:00
class quiz_statistics_report extends quiz_default_report {
2009-08-10 05:00:41 +00:00
2008-07-30 09:02:44 +00:00
/**
* @ var object instance of table class used for main questions stats table .
*/
var $table ;
2008-06-10 16:18:10 +00:00
/**
* Display the report .
*/
function display ( $quiz , $cm , $course ) {
2009-08-06 08:23:24 +00:00
global $CFG , $DB , $QTYPES , $OUTPUT ;
2008-06-10 16:18:10 +00:00
$context = get_context_instance ( CONTEXT_MODULE , $cm -> id );
$download = optional_param ( 'download' , '' , PARAM_ALPHA );
2008-09-11 12:48:08 +00:00
$everything = optional_param ( 'everything' , 0 , PARAM_BOOL );
2008-07-24 17:42:06 +00:00
$recalculate = optional_param ( 'recalculate' , 0 , PARAM_BOOL );
2008-07-30 09:02:44 +00:00
//pass the question id for detailed analysis question
$qid = optional_param ( 'qid' , 0 , PARAM_INT );
2008-06-10 16:18:10 +00:00
$pageoptions = array ();
$pageoptions [ 'id' ] = $cm -> id ;
$pageoptions [ 'q' ] = $quiz -> id ;
$pageoptions [ 'mode' ] = 'statistics' ;
2009-08-10 05:00:41 +00:00
2008-07-24 13:25:07 +00:00
$questions = quiz_report_load_questions ( $quiz );
// Load the question type specific information
if ( ! get_question_options ( $questions )) {
print_error ( 'cannotloadquestion' , 'question' );
}
2008-06-10 16:18:10 +00:00
2009-08-10 05:00:41 +00:00
2008-06-10 16:18:10 +00:00
$reporturl = new moodle_url ( $CFG -> wwwroot . '/mod/quiz/report.php' , $pageoptions );
$mform = new mod_quiz_report_statistics ( $reporturl );
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' );
}
} else {
$useallattempts = get_user_preferences ( 'quiz_report_statistics_useallattempts' , 0 );
}
/// find out current groups mode
$currentgroup = groups_get_activity_group ( $cm , true );
2009-08-10 05:00:41 +00:00
2008-06-10 16:18:10 +00:00
2008-06-18 11:38:17 +00:00
$nostudentsingroup = false ; //true if a group is selected and their is noeone in it.
2008-06-10 16:18:10 +00:00
if ( ! empty ( $currentgroup )) {
// all users who can attempt quizzes and who are in the currently selected group
2009-01-14 07:08:02 +00:00
$groupstudents = get_users_by_capability ( $context , array ( 'mod/quiz:reviewmyattempts' , 'mod/quiz:attempt' ), '' , '' , '' , '' , $currentgroup , '' , false );
2008-06-18 11:38:17 +00:00
if ( ! $groupstudents ){
$nostudentsingroup = true ;
2008-06-10 16:18:10 +00:00
}
2008-07-24 13:25:07 +00:00
} else {
$groupstudents = array ();
2008-06-10 16:18:10 +00:00
}
2009-08-10 05:00:41 +00:00
2008-07-30 09:02:44 +00:00
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 )){
2008-09-11 12:48:08 +00:00
print_error ( 'errordeleting' , 'quiz_statistics' , '' , 'quiz_statistics' );
2008-07-30 09:02:44 +00:00
}
if ( ! $DB -> delete_records_select ( 'quiz_question_statistics' , " quizstatisticsid $todeletesql " , $todeleteparams )){
2008-09-11 12:48:08 +00:00
print_error ( 'errordeleting' , 'quiz_statistics' , '' , 'quiz_question_statistics' );
}
if ( ! $DB -> delete_records_select ( 'quiz_question_response_stats' , " quizstatisticsid $todeletesql " , $todeleteparams )){
print_error ( 'errordeleting' , 'quiz_statistics' , '' , 'quiz_question_response_stats' );
2008-07-30 09:02:44 +00:00
}
}
2009-07-31 09:30:11 +00:00
redirect ( $reporturl );
2008-07-30 09:02:44 +00:00
}
2009-08-10 05:00:41 +00:00
2008-07-30 09:02:44 +00:00
$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 ()) {
2008-06-10 16:18:10 +00:00
// 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
2008-07-30 09:02:44 +00:00
if ( ! $this -> table -> is_downloading ()) {
2008-06-10 16:18:10 +00:00
groups_print_activity_menu ( $cm , $reporturl -> out ());
2008-07-30 09:02:44 +00:00
echo '<br />' ;
2008-06-18 11:38:17 +00:00
if ( $currentgroup && ! $groupstudents ){
2009-08-18 05:16:08 +00:00
echo $OUTPUT -> notification ( get_string ( 'nostudentsingroup' , 'quiz_statistics' ));
2008-06-18 11:38:17 +00:00
}
2008-06-10 16:18:10 +00:00
}
}
2008-07-30 09:02:44 +00:00
if ( ! $this -> table -> is_downloading ()) {
2008-07-24 13:25:07 +00:00
// Print display options
$mform -> set_data ( array ( 'useallattempts' => $useallattempts ));
$mform -> display ();
}
2009-08-10 05:00:41 +00:00
list ( $quizstats , $questions , $subquestions , $s , $usingattemptsstring )
2008-07-30 09:02:44 +00:00
= $this -> quiz_questions_stats ( $quiz , $currentgroup , $nostudentsingroup ,
$useallattempts , $groupstudents , $questions );
if ( ! $this -> table -> is_downloading ()){
if ( $s == 0 ){
2009-08-06 08:23:24 +00:00
echo $OUTPUT -> heading ( get_string ( 'noattempts' , 'quiz' ));
2008-07-24 13:25:07 +00:00
}
2008-07-30 09:02:44 +00:00
}
if ( $s ){
$this -> table -> setup ( $quiz , $cm -> id , $reporturl , $s );
}
2009-08-10 05:00:41 +00:00
2008-07-30 10:28:39 +00:00
if ( ! $qid ){ //main page
2008-09-11 12:48:08 +00:00
$this -> output_quiz_info_table ( $course , $cm , $quiz , $quizstats , $usingattemptsstring , $currentgroup , $groupstudents , $useallattempts , $download , $reporturl , $everything );
$this -> output_quiz_structure_analysis_table ( $s , $questions , $subquestions );
if ( ! $this -> table -> is_downloading () || ( $everything && $this -> table -> is_downloading () == 'xhtml' )){
if ( $s > 1 ){
$imageurl = $CFG -> wwwroot . '/mod/quiz/report/statistics/statistics_graph.php?id=' . $quizstats -> id ;
2009-08-06 08:23:24 +00:00
echo $OUTPUT -> heading ( get_string ( 'statisticsreportgraph' , 'quiz_statistics' ));
2008-09-11 12:48:08 +00:00
echo '<div class="mdl-align"><img src="' . $imageurl . '" alt="' . get_string ( 'statisticsreportgraph' , 'quiz_statistics' ) . '" /></div>' ;
}
}
if ( $this -> table -> is_downloading ()){
if ( $everything ){
foreach ( $questions as $question ){
if ( $question -> qtype != 'random' && $QTYPES [ $question -> qtype ] -> show_analysis_of_responses ()){
$this -> output_individual_question_data ( $quiz , $question , $reporturl , $quizstats );
} elseif ( ! empty ( $question -> _stats -> subquestions )) {
$subitemstodisplay = explode ( ',' , $question -> _stats -> subquestions );
foreach ( $subitemstodisplay as $subitemid ){
$this -> output_individual_question_data ( $quiz , $subquestions [ $subitemid ], $reporturl , $quizstats );
}
}
}
$exportclassinstance =& $this -> table -> export_class_instance ();
} else {
$this -> table -> finish_output ();
}
}
if ( $this -> table -> is_downloading () && $everything ){
$exportclassinstance -> finish_document ();
}
2008-07-30 10:28:39 +00:00
} else { //individual question page
2008-07-30 09:02:44 +00:00
$thisquestion = false ;
if ( isset ( $questions [ $qid ])){
$thisquestion = $questions [ $qid ];
} else if ( isset ( $subquestions [ $qid ])){
$thisquestion = $subquestions [ $qid ];
2008-07-24 13:25:07 +00:00
} else {
2008-07-30 09:02:44 +00:00
print_error ( 'questiondoesnotexist' , 'question' );
2008-07-24 13:25:07 +00:00
}
2008-09-11 12:48:08 +00:00
$this -> output_individual_question_data ( $quiz , $thisquestion , $reporturl , $quizstats );
2008-07-30 09:02:44 +00:00
}
return true ;
}
2009-08-10 05:00:41 +00:00
2008-09-11 12:48:08 +00:00
function sort_response_details ( $detail1 , $detail2 ){
if ( $detail1 -> credit == $detail2 -> credit ){
return strcmp ( $detail1 -> answer , $detail2 -> answer );
2008-07-30 09:02:44 +00:00
}
2008-09-11 12:48:08 +00:00
return ( $detail1 -> credit > $detail2 -> credit ) ? - 1 : 1 ;
2008-07-30 09:02:44 +00:00
}
2008-09-11 12:48:08 +00:00
function sort_answers ( $answer1 , $answer2 ){
if ( $answer1 -> rcount == $answer2 -> rcount ){
return strcmp ( $answer1 -> response , $answer2 -> response );
} else {
return ( $answer1 -> rcount > $answer2 -> rcount ) ? - 1 : 1 ;
}
}
2009-08-10 05:00:41 +00:00
2008-09-11 12:48:08 +00:00
function output_individual_question_data ( $quiz , $question , $reporturl , $quizstats ){
2009-08-10 05:00:41 +00:00
global $CFG , $DB , $QTYPES , $OUTPUT ;
2008-09-11 12:48:08 +00:00
require_once ( $CFG -> dirroot . '/mod/quiz/report/statistics/statistics_question_table.php' );
$this -> qtable = new quiz_report_statistics_question_table ( $question -> id );
$downloadtype = $this -> table -> is_downloading ();
if ( ! $this -> table -> is_downloading ()){
$datumfromtable = $this -> table -> format_row ( $question );
2009-08-10 05:00:41 +00:00
2009-08-20 08:45:47 +00:00
$questioninfotable = new html_table ();
2008-09-11 12:48:08 +00:00
$questioninfotable -> align = array ( 'center' , 'center' );
$questioninfotable -> width = '60%' ;
2009-08-20 08:45:47 +00:00
$questioninfotable -> add_class ( 'generaltable titlesleft' );
2009-08-10 05:00:41 +00:00
2008-09-11 12:48:08 +00:00
$questioninfotable -> data = array ();
$questioninfotable -> data [] = array ( get_string ( 'modulename' , 'quiz' ), $quiz -> name );
$questioninfotable -> data [] = array ( get_string ( 'questionname' , 'quiz_statistics' ), $question -> name . ' ' . $datumfromtable [ 'actions' ]);
$questioninfotable -> data [] = array ( get_string ( 'questiontype' , 'quiz_statistics' ), $datumfromtable [ 'icon' ] . ' ' . get_string ( $question -> qtype , 'quiz' ) . ' ' . $datumfromtable [ 'icon' ]);
$questioninfotable -> data [] = array ( get_string ( 'positions' , 'quiz_statistics' ), $question -> _stats -> positions );
2009-08-10 05:00:41 +00:00
2009-08-20 08:45:47 +00:00
$questionstatstable = new html_table ();
2008-09-11 12:48:08 +00:00
$questionstatstable -> align = array ( 'center' , 'center' );
$questionstatstable -> width = '60%' ;
2009-08-20 08:45:47 +00:00
$questionstatstable -> add_class ( 'generaltable titlesleft' );
2009-08-10 05:00:41 +00:00
2008-09-11 12:48:08 +00:00
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 );
}
2009-08-06 08:23:24 +00:00
echo $OUTPUT -> heading ( get_string ( 'questioninformation' , 'quiz_statistics' ));
2009-08-20 08:45:47 +00:00
echo $OUTPUT -> table ( $questioninfotable );
2009-08-10 05:00:41 +00:00
echo $OUTPUT -> box ( format_text ( $question -> questiontext , $question -> questiontextformat ) . $actions , 'boxaligncenter generalbox boxwidthnormal mdl-align' );
2009-08-06 08:23:24 +00:00
echo $OUTPUT -> heading ( get_string ( 'questionstatistics' , 'quiz_statistics' ));
2009-08-20 08:45:47 +00:00
echo $OUTPUT -> table ( $questionstatstable );
2009-08-10 05:00:41 +00:00
2008-09-11 12:48:08 +00:00
} else {
$this -> qtable -> export_class_instance ( $this -> table -> export_class_instance ());
$questiontabletitle = ! empty ( $question -> number ) ? '(' . $question -> number . ') ' : '' ;
$questiontabletitle .= " \" { $question -> name } \" " ;
$questiontabletitle = " <em> $questiontabletitle </em> " ;
if ( $downloadtype == 'xhtml' ){
$questiontabletitle = get_string ( 'analysisofresponsesfor' , 'quiz_statistics' , $questiontabletitle );
}
$exportclass =& $this -> table -> export_class_instance ();
$exportclass -> start_table ( $questiontabletitle );
}
if ( $QTYPES [ $question -> qtype ] -> show_analysis_of_responses ()){
if ( ! $this -> table -> is_downloading ()){
2009-08-06 08:23:24 +00:00
echo $OUTPUT -> heading ( get_string ( 'analysisofresponses' , 'quiz_statistics' ));
2008-09-11 12:48:08 +00:00
}
$teacherresponses = $QTYPES [ $question -> qtype ] -> get_possible_responses ( $question );
$this -> qtable -> setup ( $reporturl , $question , count ( $teacherresponses ) > 1 );
if ( $this -> table -> is_downloading ()){
$exportclass -> output_headers ( $this -> qtable -> headers );
}
2009-08-10 05:00:41 +00:00
2008-09-11 12:48:08 +00:00
$responses = $DB -> get_records ( 'quiz_question_response_stats' , array ( 'quizstatisticsid' => $quizstats -> id , 'questionid' => $question -> id ), 'credit DESC, subqid ASC, aid ASC, rcount DESC' );
$responses = quiz_report_index_by_keys ( $responses , array ( 'subqid' , 'aid' ), false );
foreach ( $responses as $subqid => $response ){
foreach ( array_keys ( $responses [ $subqid ]) as $aid ){
uasort ( $responses [ $subqid ][ $aid ], array ( 'quiz_statistics_report' , 'sort_answers' ));
}
if ( isset ( $responses [ $subqid ][ '0' ])){
$wildcardresponse = new object ();
$wildcardresponse -> answer = '*' ;
$wildcardresponse -> credit = 0 ;
$teacherresponses [ $subqid ][ 0 ] = $wildcardresponse ;
}
}
$first = true ;
$subq = 0 ;
foreach ( $teacherresponses as $subqid => $tresponsesforsubq ){
$subq ++ ;
$qhaswildcards = $QTYPES [ $question -> qtype ] -> has_wildcards_in_responses ( $question , $subqid );
if ( ! $first ){
$this -> qtable -> add_separator ();
}
uasort ( $tresponsesforsubq , array ( 'quiz_statistics_report' , 'sort_response_details' ));
foreach ( $tresponsesforsubq as $aid => $teacherresponse ){
$teacherresponserow = new object ();
2009-08-10 05:00:41 +00:00
$teacherresponserow -> response = $teacherresponse -> answer ;
2008-09-11 12:48:08 +00:00
$teacherresponserow -> rcount = 0 ;
$teacherresponserow -> subq = $subq ;
$teacherresponserow -> credit = $teacherresponse -> credit ;
if ( isset ( $responses [ $subqid ][ $aid ])){
2009-08-10 05:00:41 +00:00
$singleanswer = count ( $responses [ $subqid ][ $aid ]) == 1 &&
2008-09-11 12:48:08 +00:00
( $responses [ $subqid ][ $aid ][ 0 ] -> response == $teacherresponserow -> response );
if ( ! $singleanswer && $qhaswildcards ){
$this -> qtable -> add_separator ();
}
foreach ( $responses [ $subqid ][ $aid ] as $response ){
$teacherresponserow -> rcount += $response -> rcount ;
}
if ( $aid != 0 || $qhaswildcards ){
$this -> qtable -> add_data_keyed ( $this -> qtable -> format_row ( $teacherresponserow ));
}
if ( ! $singleanswer ){
foreach ( $responses [ $subqid ][ $aid ] as $response ){
if ( ! $downloadtype || $downloadtype == 'xhtml' ){
$indent = ' ' ;
} else {
$indent = ' ' ;
}
$response -> response = ( $qhaswildcards ? $indent : '' ) . $response -> response ;
$response -> subq = $subq ;
if (( count ( $responses [ $subqid ][ $aid ]) < 2 ) || ( $response -> rcount > ( $teacherresponserow -> rcount / 10 ))){
$this -> qtable -> add_data_keyed ( $this -> qtable -> format_row ( $response ));
}
}
}
} else {
$this -> qtable -> add_data_keyed ( $this -> qtable -> format_row ( $teacherresponserow ));
}
}
$first = false ;
}
$this -> qtable -> finish_output ( ! $this -> table -> is_downloading ());
}
2008-07-30 09:02:44 +00:00
if ( ! $this -> table -> is_downloading ()){
2008-09-11 12:48:08 +00:00
$url = $reporturl -> out ();
$text = get_string ( 'backtoquizreport' , 'quiz_statistics' );
2009-08-10 05:00:41 +00:00
echo $OUTPUT -> box ( " <a href= \" $url\ " > $text </ a > " , 'boxaligncenter generalbox boxwidthnormal mdl-align');
2008-07-30 09:02:44 +00:00
}
2008-09-11 12:48:08 +00:00
}
2009-08-10 05:00:41 +00:00
2008-09-11 12:48:08 +00:00
function output_quiz_structure_analysis_table ( $s , $questions , $subquestions ){
2009-08-06 08:23:24 +00:00
global $OUTPUT ;
2008-07-30 09:02:44 +00:00
if ( $s ){
2008-09-11 12:48:08 +00:00
if ( ! $this -> table -> is_downloading ()){
2009-08-06 08:23:24 +00:00
echo $OUTPUT -> heading ( get_string ( 'quizstructureanalysis' , 'quiz_statistics' ));
2008-09-11 12:48:08 +00:00
}
2008-07-30 09:02:44 +00:00
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 ]));
}
2008-07-24 13:25:07 +00:00
}
}
2008-07-30 09:02:44 +00:00
2008-09-11 12:48:08 +00:00
$this -> table -> finish_output ( ! $this -> table -> is_downloading ());
2008-06-10 16:18:10 +00:00
}
2008-07-30 09:02:44 +00:00
}
2009-08-10 05:00:41 +00:00
2008-09-11 12:48:08 +00:00
function output_quiz_info_table ( $course , $cm , $quiz , $quizstats , $usingattemptsstring ,
$currentgroup , $groupstudents , $useallattempts , $download , $reporturl , $everything ){
2009-08-06 08:23:24 +00:00
global $DB , $OUTPUT ;
2008-07-30 09:02:44 +00:00
// Print information on the number of existing attempts
2009-08-06 08:23:24 +00:00
$quizinformationtablehtml = $OUTPUT -> heading ( get_string ( 'quizinformation' , 'quiz_statistics' ), 2 , 'main' );
2009-08-20 08:45:47 +00:00
$quizinformationtable = new html_table ();
2008-07-30 09:02:44 +00:00
$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 ){
2008-10-14 15:19:25 +00:00
$quizinformationtable -> data [] = array ( get_string ( 'idnumbermod' ), $cm -> idnumber );
2008-07-30 09:02:44 +00:00
}
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 ;
2008-07-24 13:25:07 +00:00
}
2008-07-30 09:02:44 +00:00
if ( ! is_null ( $value )){
2008-07-24 13:25:07 +00:00
switch ( $format [ $property ]){
case 'sumgrades_as_percentage' :
$formattedvalue = quiz_report_scale_sumgrades_as_percentage ( $value , $quiz );
break ;
case 'number_format' :
2008-08-15 06:42:38 +00:00
$formattedvalue = quiz_format_grade ( $quiz , $value ) . '%' ;
2008-07-24 13:25:07 +00:00
break ;
default :
$formattedvalue = $value ;
}
$quizinformationtable -> data [] = array ( get_string ( $property , 'quiz_statistics' , $usingattemptsstring ), $formattedvalue );
}
2008-07-30 09:02:44 +00:00
}
if ( ! $this -> table -> is_downloading ()){
2008-07-24 17:42:06 +00:00
if ( isset ( $quizstats -> timemodified )){
list ( $fromqa , $whereqa , $qaparams ) = quiz_report_attempts_sql ( $quiz -> id , $currentgroup , $groupstudents , $useallattempts );
$sql = 'SELECT COUNT(1) ' .
'FROM ' . $fromqa . ' ' .
'WHERE ' . $whereqa . ' AND qa.timefinish > :time' ;
$a = new object ();
$a -> lastcalculated = format_time ( time () - $quizstats -> timemodified );
if ( ! $a -> count = $DB -> count_records_sql ( $sql , array ( 'time' => $quizstats -> timemodified ) + $qaparams )){
$a -> count = 0 ;
2008-07-30 09:02:44 +00:00
}
2009-08-10 05:00:41 +00:00
$quizinformationtablehtml .= $OUTPUT -> box_start ( 'boxaligncenter generalbox boxwidthnormal mdl-align' );
2008-07-30 09:02:44 +00:00
$quizinformationtablehtml .= get_string ( 'lastcalculated' , 'quiz_statistics' , $a );
2009-08-20 08:45:47 +00:00
$quizinformationtablehtml .= $OUTPUT -> button ( html_form :: make_button ( $reporturl -> out ( true ), $reporturl -> params () + array ( 'recalculate' => 1 ),
get_string ( 'recalculatenow' , 'quiz_statistics' )));
2009-08-10 05:00:41 +00:00
$quizinformationtablehtml .= $OUTPUT -> box_end ();
2008-07-24 17:42:06 +00:00
}
2008-09-11 12:48:08 +00:00
$downloadoptions = $this -> table -> get_download_menu ();
$quizinformationtablehtml .= '<form action="' . $this -> table -> baseurl . '" method="post">' ;
$quizinformationtablehtml .= '<div class="mdl-align">' ;
$quizinformationtablehtml .= '<input type="hidden" name="everything" value="1"/>' ;
$quizinformationtablehtml .= '<input type="submit" value="' . get_string ( 'downloadeverything' , 'quiz_statistics' ) . '"/>' ;
2009-08-18 00:13:03 +00:00
$select = html_select :: make ( $downloadoptions , 'download' , $this -> table -> defaultdownloadformat , false );
$select -> nothingvalue = '' ;
$quizinformationtablehtml .= $OUTPUT -> select ( $select );;
2009-08-18 05:16:08 +00:00
$quizinformationtablehtml .= $OUTPUT -> help_icon ( moodle_help_icon :: make ( 'tableexportformats' , get_string ( 'tableexportformats' , 'table' )));
2008-09-11 12:48:08 +00:00
$quizinformationtablehtml .= '</div></form>' ;
2008-06-18 11:38:17 +00:00
}
2009-08-20 08:45:47 +00:00
$quizinformationtablehtml .= $OUTPUT -> table ( $quizinformationtable );
2008-07-30 09:02:44 +00:00
if ( ! $this -> table -> is_downloading ()){
echo $quizinformationtablehtml ;
2008-09-11 12:48:08 +00:00
} elseif ( $everything ) {
2008-07-30 09:02:44 +00:00
$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 ];
2008-07-24 13:25:07 +00:00
}
2008-07-30 09:02:44 +00:00
$exportclass -> output_headers ( $headers );
$exportclass -> add_data ( $row );
$exportclass -> finish_table ();
2008-07-24 13:25:07 +00:00
}
}
}
2008-07-30 09:02:44 +00:00
2008-07-24 13:25:07 +00:00
function quiz_stats ( $nostudentsingroup , $quizid , $currentgroup , $groupstudents , $questions , $useallattempts ){
global $CFG , $DB ;
2008-06-18 11:38:17 +00:00
if ( ! $nostudentsingroup ){
//Calculating_MEAN_of_grades_for_all_attempts_by_students
//http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#Calculating_MEAN_of_grades_for_all_attempts_by_students
2009-08-10 05:00:41 +00:00
2008-07-24 13:25:07 +00:00
list ( $fromqa , $whereqa , $qaparams ) = quiz_report_attempts_sql ( $quizid , $currentgroup , $groupstudents );
2009-08-10 05:00:41 +00:00
2008-07-24 13:25:07 +00:00
$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 (attempt=1)' ;
2009-08-10 05:00:41 +00:00
2008-06-18 11:38:17 +00:00
if ( ! $attempttotals = $DB -> get_records_sql ( $sql , $qaparams )){
$s = 0 ;
2008-09-11 12:48:08 +00:00
$usingattemptsstring = '' ;
2008-06-18 11:38:17 +00:00
} else {
$firstattempt = $attempttotals [ 1 ];
$allattempts = new object ();
2009-08-10 05:00:41 +00:00
$allattempts -> countrecs = $firstattempt -> countrecs +
2008-06-18 11:38:17 +00:00
( isset ( $attempttotals [ 0 ]) ? $attempttotals [ 0 ] -> countrecs : 0 );
2009-08-10 05:00:41 +00:00
$allattempts -> total = $firstattempt -> total +
2008-06-18 11:38:17 +00:00
( isset ( $attempttotals [ 0 ]) ? $attempttotals [ 0 ] -> total : 0 );
if ( $useallattempts ){
$usingattempts = $allattempts ;
$usingattempts -> attempts = get_string ( 'allattempts' , 'quiz_statistics' );
$usingattempts -> sql = '' ;
} else {
$usingattempts = $firstattempt ;
$usingattempts -> attempts = get_string ( 'firstattempts' , 'quiz_statistics' );
$usingattempts -> sql = 'AND qa.attempt=1 ' ;
}
2008-07-24 13:25:07 +00:00
$usingattemptsstring = $usingattempts -> attempts ;
2008-06-18 11:38:17 +00:00
$s = $usingattempts -> countrecs ;
2008-07-16 11:55:38 +00:00
$sumgradesavg = $usingattempts -> total / $usingattempts -> countrecs ;
2008-06-18 11:38:17 +00:00
}
2008-06-10 16:18:10 +00:00
} else {
2008-06-18 11:38:17 +00:00
$s = 0 ;
2008-06-10 16:18:10 +00:00
}
2008-07-24 13:25:07 +00:00
$quizstats = new object ();
if ( $s == 0 ){
$quizstats -> firstattemptscount = 0 ;
$quizstats -> allattemptscount = 0 ;
} else {
$quizstats -> firstattemptscount = $firstattempt -> countrecs ;
$quizstats -> allattemptscount = $allattempts -> countrecs ;
$quizstats -> firstattemptsavg = $firstattempt -> total / $firstattempt -> countrecs ;
$quizstats -> allattemptsavg = $allattempts -> total / $allattempts -> countrecs ;
2008-06-10 16:18:10 +00:00
}
2008-07-24 13:25:07 +00:00
//recalculate sql again this time possibly including test for first attempt.
list ( $fromqa , $whereqa , $qaparams ) = quiz_report_attempts_sql ( $quizid , $currentgroup , $groupstudents , $useallattempts );
2009-08-10 05:00:41 +00:00
2008-06-10 16:18:10 +00:00
//get the median
2008-07-24 13:25:07 +00:00
if ( $s ) {
2008-06-18 11:38:17 +00:00
if (( $s % 2 ) == 0 ){
2008-06-10 16:18:10 +00:00
//even number of attempts
2008-06-18 11:38:17 +00:00
$limitoffset = ( $s / 2 ) - 1 ;
2008-06-10 16:18:10 +00:00
$limit = 2 ;
} else {
2008-07-29 13:56:37 +00:00
$limitoffset = ( floor ( $s / 2 ));
2008-06-10 16:18:10 +00:00
$limit = 1 ;
}
$sql = 'SELECT id, sumgrades ' .
2008-06-10 18:15:19 +00:00
'FROM ' . $fromqa .
'WHERE ' . $whereqa .
2008-06-10 16:18:10 +00:00
'ORDER BY sumgrades' ;
2008-06-18 11:38:17 +00:00
if ( ! $mediangrades = $DB -> get_records_sql_menu ( $sql , $qaparams , $limitoffset , $limit )){
2008-06-10 16:18:10 +00:00
print_error ( 'errormedian' , 'quiz_statistics' );
}
2008-09-11 12:48:08 +00:00
$quizstats -> median = array_sum ( $mediangrades ) / count ( $mediangrades );
2008-06-16 13:29:00 +00:00
if ( $s > 1 ){
2009-08-10 05:00:41 +00:00
//fetch sum of squared, cubed and power 4d
2008-06-16 13:29:00 +00:00
//differences between grades and mean grade
2008-06-18 11:38:17 +00:00
$mean = $usingattempts -> total / $s ;
2008-06-16 13:29:00 +00:00
$sql = " SELECT " .
2008-06-18 11:38:17 +00:00
" SUM(POWER((qa.sumgrades - :mean1),2)) AS power2, " .
" SUM(POWER((qa.sumgrades - :mean2),3)) AS power3, " .
" SUM(POWER((qa.sumgrades - :mean3),4)) AS power4 " .
2008-06-16 13:29:00 +00:00
'FROM ' . $fromqa .
2008-07-24 13:25:07 +00:00
'WHERE ' . $whereqa ;
2008-06-18 11:38:17 +00:00
$params = array ( 'mean1' => $mean , 'mean2' => $mean , 'mean3' => $mean ) + $qaparams ;
2008-06-16 13:29:00 +00:00
if ( ! $powers = $DB -> get_record_sql ( $sql , $params )){
print_error ( 'errorpowers' , 'quiz_statistics' );
}
2009-08-10 05:00:41 +00:00
2008-06-16 13:29:00 +00:00
//Standard_Deviation
//see http://docs.moodle.org/en/Development:Quiz_item_analysis_calculations_in_practise#Standard_Deviation
2009-08-10 05:00:41 +00:00
2008-07-24 13:25:07 +00:00
$quizstats -> standarddeviation = sqrt ( $powers -> power2 / ( $s - 1 ));
2008-06-16 13:29:00 +00:00
2009-08-10 05:00:41 +00:00
2008-06-16 13:29:00 +00:00
//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 ;
2009-08-10 05:00:41 +00:00
2008-06-16 13:29:00 +00:00
$k2 = $s * $m2 / ( $s - 1 );
$k3 = $s * $s * $m3 / (( $s - 1 ) * ( $s - 2 ));
2008-07-30 09:02:44 +00:00
if ( $k2 ){
2008-10-21 09:25:02 +00:00
$quizstats -> skewness = $k3 / ( pow ( $k2 , 3 / 2 ));
2008-07-30 09:02:44 +00:00
}
2008-06-16 13:29:00 +00:00
}
2009-08-10 05:00:41 +00:00
2008-06-16 13:29:00 +00:00
if ( $s > 3 ){
2008-10-21 09:25:02 +00:00
$k4 = $s * $s * ((( $s + 1 ) * $m4 ) - ( 3 * ( $s - 1 ) * $m2 * $m2 )) / (( $s - 1 ) * ( $s - 2 ) * ( $s - 3 ));
2008-07-30 09:02:44 +00:00
if ( $k2 ){
$quizstats -> kurtosis = $k4 / ( $k2 * $k2 );
}
2008-06-16 13:29:00 +00:00
}
2008-06-18 11:38:17 +00:00
}
}
if ( $s ){
2008-07-18 14:36:24 +00:00
require_once ( " $CFG->dirroot /mod/quiz/report/statistics/qstats.php " );
$qstats = new qstats ( $questions , $s , $sumgradesavg );
2008-07-24 13:25:07 +00:00
$qstats -> get_records ( $quizid , $currentgroup , $groupstudents , $useallattempts );
2008-07-18 14:36:24 +00:00
$qstats -> process_states ();
2008-10-29 10:00:44 +00:00
$qstats -> process_responses ();
2008-07-24 13:25:07 +00:00
} else {
$qstats = false ;
2008-06-18 11:38:17 +00:00
}
2008-07-24 13:25:07 +00:00
if ( $s > 1 ){
$p = count ( $qstats -> questions ); //no of positions
if ( $p > 1 ){
2008-09-11 12:48:08 +00:00
if ( isset ( $k2 )){
2008-07-30 09:02:44 +00:00
$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 );
}
2008-07-18 14:36:24 +00:00
}
2008-06-10 18:15:19 +00:00
}
2008-07-24 13:25:07 +00:00
return array ( $s , $usingattemptsstring , $quizstats , $qstats );
2008-06-10 16:18:10 +00:00
}
2009-08-10 05:00:41 +00:00
2008-07-30 09:02:44 +00:00
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 );
2008-09-11 12:48:08 +00:00
if ( $s ){
$toinsert = ( object )(( array ) $quizstats + $params );
$toinsert -> timemodified = time ();
$quizstats -> id = $DB -> insert_record ( 'quiz_statistics' , $toinsert );
foreach ( $qstats -> questions as $question ){
$question -> _stats -> quizstatisticsid = $quizstats -> id ;
$DB -> insert_record ( 'quiz_question_statistics' , $question -> _stats , false , true );
}
foreach ( $qstats -> subquestions as $subquestion ){
$subquestion -> _stats -> quizstatisticsid = $quizstats -> id ;
$DB -> insert_record ( 'quiz_question_statistics' , $subquestion -> _stats , false , true );
}
foreach ( $qstats -> responses as $response ){
$response -> quizstatisticsid = $quizstats -> id ;
$DB -> insert_record ( 'quiz_question_response_stats' , $response , false );
}
2008-07-30 09:02:44 +00:00
}
2008-09-11 12:48:08 +00:00
if ( $qstats ){
2008-07-30 09:02:44 +00:00
$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 ;
}
2008-09-11 12:48:08 +00:00
$subquestions = array ();
2008-07-30 09:02:44 +00:00
$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 ];
}
2008-09-11 12:48:08 +00:00
} elseif ( count ( $questionstats )) {
2008-07-30 09:02:44 +00:00
$mainquestionstats = $questionstats [ 0 ];
}
2008-09-11 12:48:08 +00:00
if ( count ( $questionstats )) {
foreach ( array_keys ( $questions ) as $qid ){
$questions [ $qid ] -> _stats = $mainquestionstats [ $qid ];
}
2008-07-30 09:02:44 +00:00
}
}
return array ( $quizstats , $questions , $subquestions , $s , $usingattemptsstring );
}
2008-06-10 16:18:10 +00:00
}
2008-07-24 13:25:07 +00:00
function quiz_report_attempts_sql ( $quizid , $currentgroup , $groupstudents , $allattempts = true ){
2008-07-29 13:56:37 +00:00
global $DB ;
2008-07-24 13:25:07 +00:00
$fromqa = '{quiz_attempts} qa ' ;
2008-07-24 17:42:06 +00:00
$whereqa = 'qa.quiz = :quizid AND qa.preview=0 AND qa.timefinish !=0 ' ;
2008-07-24 13:25:07 +00:00
$qaparams = array ( 'quizid' => $quizid );
if ( ! empty ( $currentgroup ) && $groupstudents ) {
list ( $grpsql , $grpparams ) = $DB -> get_in_or_equal ( array_keys ( $groupstudents ), SQL_PARAMS_NAMED , 'u0000' );
$whereqa .= 'AND qa.userid ' . $grpsql . ' ' ;
$qaparams += $grpparams ;
}
if ( ! $allattempts ){
$whereqa .= 'AND qa.attempt=1 ' ;
}
return array ( $fromqa , $whereqa , $qaparams );
}
2008-09-11 12:48:08 +00:00
2008-06-10 16:18:10 +00:00
?>