Index: multichoice.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/quiz/multichoice.module,v
retrieving revision 1.30
diff -u -r1.30 multichoice.module
--- multichoice.module	20 Sep 2006 14:03:40 -0000	1.30
+++ multichoice.module	5 Oct 2006 22:41:54 -0000
@@ -281,8 +281,7 @@
 function multichoice_view(&$node, $teaser = FALSE, $page = FALSE) {
   if (user_access('create multichoice')) {
     if (!$teaser) {
-      $question = multichoice_render_question($node);
-      $node->body .= $question;
+      $node->body = multichoice_render_question($node);
     }
   }
   else if ($teaser) {
@@ -342,7 +341,7 @@
   $form['tries'] = array(
     '#type' => $type,
     '#options' => $options,
-    '#default_value' => NULL,
+    '#default_value' => -1,
   );
   $form['submit'] = array(
     '#type' => 'submit',
@@ -385,10 +384,14 @@
       $results['tried'][] = $question->answers[$_POST['edit']['tries']]['aid'];
     }
   }
-
+  //Unset $_POST, otherwise it tries to use the previous answers on the next page...
+  unset($_POST['edit']['tries']);
+  
+  //Return the result
   return $results;
 }
 
+//Old claculate result function
 function multichoice_calculate_result($answers, $tried) {
   while(list($key, $answer) = each($answers)) {
     if ($answer['points'] == 1) {
@@ -411,6 +414,35 @@
   return 1;
 }
 
+//New singing and dancing one
+function multichoice_calculate_results($answers, $tried, $showPoints = FALSE, $showFeedback = FALSE) {
+  //Create results table
+  $rows = array();
+  $correctAnswers = array();
+  
+  while(list($key, $answer) = each($answers)) {
+    $cols = array();
+    
+    $cols[] = $answer['answer'];
+    if($showPoints) $cols[] = (($answer['points'] == 0)?'<img src=\''.base_path().'files/cross.png\' />':'<img src=\''.base_path().'files/tick.png\'/>');
+    $isSelected = (array_search($answer['aid'], $tried) !== FALSE);
+    $cols[] = ($isSelected?'<img src=\''.base_path().'files/tick.png\'/>':'<img src=\''.base_path().'files/cross.png\' />');
+    if($showFeedback) $cols[] = ($isSelected?$answer['feedback']:'');
+  
+    $rows[] = $cols;
+  
+  	
+  	if($answer['points'] > 0) {
+  	  $correctAnswers[] = $answer['aid'];
+  	}
+  }
+
+  
+  if($correctAnswers === $tried) { $score = 1; } else { $score = 0; }
+  
+  return array('score' => $score, 'resultstable' => $rows);
+}
+
 
 
 /**
Index: quiz.install
===================================================================
RCS file: /cvs/drupal/contributions/modules/quiz/quiz.install,v
retrieving revision 1.1
diff -u -r1.1 quiz.install
--- quiz.install	20 Sep 2006 14:08:57 -0000	1.1
+++ quiz.install	5 Oct 2006 22:41:54 -0000
@@ -13,6 +13,7 @@
       db_query("CREATE TABLE quiz (
                   nid INTEGER UNSIGNED NOT NULL,
                   number_of_questions TINYINT UNSIGNED NOT NULL,
+                  pass_rate TINYINT UNSIGNED NOT NULL,
                   shuffle TINYINT UNSIGNED NOT NULL,
                   backwards_navigation TINYINT UNSIGNED NOT NULL,
                   feedback_time TINYINT UNSIGNED NOT NULL,
Index: quiz.module
===================================================================
RCS file: /cvs/drupal/contributions/modules/quiz/quiz.module,v
retrieving revision 1.66
diff -u -r1.66 quiz.module
--- quiz.module	20 Sep 2006 14:03:40 -0000	1.66
+++ quiz.module	5 Oct 2006 22:41:54 -0000
@@ -24,7 +24,7 @@
  * Implementation of hook_perm().
  */
 function quiz_perm() {
-  return array('create quizzes', 'access quizzes');
+  return array('create quizzes', 'access quizzes', 'user results');
 }
 
 /**
@@ -83,16 +83,28 @@
         // Menu item for quiz taking interface
         $items[] = array(
           'path' => 'node/'. arg(1) .'/quiz/start',
-          'title' => t('take quiz'),
+          'title' => t('take test'),
           'callback' => 'quiz_take_quiz',
           'access' => user_access('access quizzes'),
           'type' => MENU_LOCAL_TASK,);
       }
     }
     else {
-    
+      $items[] = array(
+        'path' => 'user/'.arg(1).'/myresults',
+        'title' => t('my results'),
+        'callback' => 'quiz_get_user_results',
+        'access' => user_access('user results'),
+        'type' => MENU_LOCAL_TASK);
 
       $items[] = array(
+        'path' => 'user/quiz/' . arg(2) . "/userresults",
+        'title' => t('user results'),
+        'callback' => 'quiz_user_results',
+        'access' => user_access('user results'),
+        'type' => MENU_CALLBACK);
+        
+      $items[] = array(
         'path' => 'admin/quiz/' . arg(2) . "/view",
         'title' => t('view quiz'),
         'callback' => 'quiz_admin_results',
@@ -147,11 +159,19 @@
     '#description' => t('The number of questions to include in this quiz from the question bank'),
     '#required' => TRUE,
   );
+  
+  $form['pass_rate'] = array(
+    '#type' => 'textfield',
+    '#title' => t('Pass rate for quiz %'),
+    '#default_value' => ($node->pass_rate ? $node->pass_rate : 100),
+    '#description' => t('Pass rate for the quiz as a percentage score'),
+    '#required' => TRUE,
+  );
 
   $form['shuffle'] = array(
     '#type' => 'checkbox',
     '#title' => t('Shuffle questions'),
-    '#default_value' => $node->shuffle ? $node->shuffle : 1,
+    '#default_value' => (isset($node->shuffle) ? $node->shuffle : 1),
     '#description' => t('Whether to shuffle/randomize the questions on the quiz'),
   );
 
@@ -210,6 +230,32 @@
 }
 
 /**
+ * @param $nid
+ *   Quiz ID
+ * Finds out the number of questions for the quiz.
+ * Good example of usage could be to calculate the % of score
+ * @return integer
+ *   Returns the number of quiz questions.
+ */
+function quiz_get_number_of_questions($nid) {
+  $result = db_fetch_object(db_query('SELECT number_of_questions FROM quiz WHERE nid = %d', $nid));  
+  $numberOfQuestions = $result->number_of_questions;
+  return $numberOfQuestions;
+}
+
+/**
+ * @param $nid
+ *   Quiz ID
+ * Finds out the pass rate for the quiz.
+ * @return integer
+ *   Returns the pass rate of quiz.
+ */
+function quiz_get_pass_rate($nid) {
+    $passrate = db_fetch_object(db_query('SELECT pass_rate FROM quiz WHERE nid = %d', $nid));
+    return $passrate->pass_rate;   
+}
+
+/**
  * Implementation of hook_validate().
  */
 function quiz_validate(&$node) {
@@ -225,6 +271,12 @@
     form_set_error('close', t('Close date before open date'));
     form_set_error('open', t('Open date after close date'));
   }
+  if ($node->pass_rate > 100) {
+    form_set_error('pass_rate', t('The pass rate value must not be more than 100%.'));
+  }
+  if ($node->pass_rate < 1) {
+    form_set_error('pass_rate', t('The pass rate value must not be less than 0%.'));
+  }
 }
 
 /**
@@ -234,7 +286,7 @@
   $open = $node->open['year'].'-'.$node->open['month'].'-'.$node->open['day'];
   $close = $node->close['year'].'-'.$node->close['month'].'-'.$node->close['day'];
     
-  db_query("INSERT INTO {quiz} (nid, number_of_questions, shuffle, backwards_navigation, feedback_time, open, close, takes) VALUES(%d, %d, %d, %d, %d, '%s', '%s', %d)", $node->nid, $node->number_of_questions, $node->shuffle, $node->backwards_navigation, $node->feedback_time, $open, $close, $node->takes);
+  db_query("INSERT INTO {quiz} (nid, number_of_questions, pass_rate, shuffle, backwards_navigation, feedback_time, open, close, takes) VALUES(%d, %d, %d, %d, %d, %d, '%s', '%s', %d)", $node->nid, $node->number_of_questions, $node->pass_rate, $node->shuffle, $node->backwards_navigation, $node->feedback_time, $open, $close, $node->takes);
 }
 
 /**
@@ -244,7 +296,7 @@
   $open = $node->open['year'].'-'.$node->open['month'].'-'.$node->open['day'];
   $close = $node->close['year'].'-'.$node->close['month'].'-'.$node->close['day'];
     
-  db_query("UPDATE {quiz} SET number_of_questions = %d, shuffle = %d, backwards_navigation = %d, feedback_time = %d, open = '%s', close = '%s', takes = %d WHERE nid = %d", $node->number_of_questions, $node->shuffle, $node->backwards_navigation, $node->feedback_time, $open, $close, $node->takes, $node->nid);
+  db_query("UPDATE {quiz} SET number_of_questions = %d, pass_rate = %d, shuffle = %d, backwards_navigation = %d, feedback_time = %d, open = '%s', close = '%s', takes = %d WHERE nid = %d", $node->number_of_questions, $node->pass_rate, $node->shuffle, $node->backwards_navigation, $node->feedback_time, $open, $close, $node->takes, $node->nid);
 }
 
 /**
@@ -271,6 +323,7 @@
  * Implementation of hook_view().
  */
 function quiz_view(&$node, $teaser = FALSE, $page = FALSE) {
+
   if (!$teaser && user_access('create quizzes')) {
 
     // Format quiz options
@@ -301,7 +354,7 @@
     
     // Format Quiz Dates
     $node->body .= '<h3>'. t('Quiz start/end') .'</h3>';
-    $node->body .= "<p>" . date("l, jS F Y", strtotime($node->open)) . " &mdash " . date("l, jS F Y", strtotime($node->close)) ."</p>";
+    $node->body .= "<p>" . date("l, jS F Y", strtotime($node->open)) . " &mdash; " . date("l, jS F Y", strtotime($node->close)) ."</p>";
     $node->body .= "<p><strong>Days quiz live for:</strong> " . floor((strtotime($node->close) - strtotime($node->open)) / 60 / 60 / 24) . "</p>";
     
     $remaining = floor((strtotime($node->close) - time()) / 60 / 60 / 24);
@@ -339,6 +392,69 @@
 }
 
 /**
+ * Displays all the quizs the user has taken part in
+ *
+ * @return
+ *  HTML output for page
+ */ 
+function quiz_get_user_results() {
+  global $user;
+  $output = '';
+  $rows = array();
+
+  $results = array();
+  $dbresult = db_query("SELECT
+                          n.nid nid,
+                          n.title title,
+                          u.name name,
+                          qr.rid rid,
+                          qr.time_start time_start,
+                          if (qr.time_end, qr.time_end, 'In Progress&hellip;') finished
+                        FROM {node} n, {quiz} q, {quiz_result} qr, {users} u
+                        WHERE
+                          n.type = 'quiz'
+                            AND
+                          n.nid = q.nid
+                            AND
+                          qr.quiz_nid = q.nid
+                            AND
+                          u.uid = qr.uid
+                            AND
+                          u.uid = ".$user->uid."
+                        ORDER BY qr.rid ASC");
+  //Create results array
+  while($line = db_fetch_array($dbresult)) {
+    $results[$line['rid']] = $line;
+  }
+  
+  while(list($key, $result) = each($results)) {
+    $rows[] = array(
+      l("view", "user/quiz/".$result['rid']."/userresults"),
+      $result['title'],
+      $result['name'],
+      $result['rid'],
+      $result['time_start'],
+      $result['finished'],);
+  }
+  
+  $header = array(
+    t('Action'),
+    t('Quiz Title'),
+    t('Username'),
+    t('Result<br/>ID'),
+    t('Time Started'),
+    t('Finished?'));
+  
+  if (isset($rows)) {
+    $output .= theme('table', $header, $rows);
+  }
+  else {
+    $output .= 'No questions found.';
+  }
+  return $output;    
+}
+
+/**
  * Handles quiz taking
  *
  * @return
@@ -349,18 +465,16 @@
 
   if (arg(0) == 'node' && is_numeric(arg(1)) && user_access('access quizzes')) {
     if ($quiz = node_load(arg(1))) {
-
       if (!isset($_SESSION['quiz_questions'])) {
 
         // First time running through quiz
         if ($rid = quiz_start_actions($user->uid, $quiz->nid)) {
-
           // Create question list
           $questions = quiz_build_question_list($quiz->nid);
-
           // Initialize session variables
           $_SESSION['quiz_questions'] = $questions;
           $_SESSION['rid'] = $rid;
+          $_SESSION['question_number'] = 0;
 
         }
         else {
@@ -370,18 +484,29 @@
 
       // Check for answer submission
       if ($_POST['op'] == t('Submit')) {
-        $former_question = node_load(array('nid' => array_shift($_SESSION['quiz_questions'])));
-        $result = module_invoke($former_question->type, 'evaluate_question', $former_question->nid);
-        db_query("REPLACE {quiz_question_results} VALUES(%d, %d, '%s')", $_SESSION['rid'], $former_question->nid, serialize($result));
+      	if(!isset($_POST['edit']['tries'])) {
+      	  drupal_set_message('You must select an answer before you can progress to the next question!', 'error');
+      	}
+      	else {
+    		  $former_question = node_load(array('nid' => array_shift($_SESSION['quiz_questions'])));
+    		  $result = module_invoke($former_question->type, 'evaluate_question', $former_question->nid);
+    		  db_query("REPLACE {quiz_question_results} VALUES(%d, %d, '%s')", $_SESSION['rid'], $former_question->nid, serialize($result));   		  
+    		}
       }
 
       // Check if at the end of quiz
       if (!empty($_SESSION['quiz_questions'])) {
-
+        //Calculation for quiz progress bar 		  
+  		  $numOfQuestion = quiz_get_number_of_questions(arg(1));
+  		  $questionNumber = $numOfQuestion - count($_SESSION['quiz_questions']);
+  		  $progress = ($questionNumber*100)/$numOfQuestion;
+          
         // Load the next question
         $question_node = node_load(array('nid' => $_SESSION['quiz_questions'][0]));
 
-        $node->body = module_invoke($question_node->type, 'render_question', $question_node);
+        $node->body = '<br /><br />'.theme_progress_bar(round($progress), 'Quiz Progress Indicator').'<br /><br />';         
+        drupal_set_title($question_node->title);       
+        $node->body .= module_invoke($question_node->type, 'render_question', $question_node);
 
       }
       else {
@@ -391,10 +516,21 @@
         //First - update the result to show we have finished.
         db_query("UPDATE {quiz_result} SET time_end = NOW() WHERE rid = %d", $_SESSION['rid']);
         
-        //display results and remove session variables
-        $node->body = "Your score: " .quiz_calculate_score($_SESSION['rid']);
+        //Set the title!
+        drupal_set_title($node->title);
+        
+        //display overall result
+        #$node->body = "Your score: " .quiz_calculate_score($_SESSION['rid']);
+        
+
+        //Get results of this quiz from the Database...
+        $questions = _quiz_get_answers($_SESSION['rid']);
+        $node->body .= _quiz_get_results_table($questions, arg(1), FALSE, TRUE, $passrate);
+        
+        //remove session variables
         unset($_SESSION['quiz_questions']);
         unset($_SESSION['rid']);
+        unset($_SESSION['question_number']);
 
       }
     }
@@ -717,7 +853,8 @@
 function quiz_questions() {
   if ($_POST) {
     if ($_POST['op'] == 'Filter question list') {
-      $_SESSION['quiz_filter'] = $_POST['edit'][1];
+      //BE CAREFUL OF THE $_POST['edit'] ARRAY AS THIS VALUE IS LIABLE TO CHANGE WHEN VOCABS ARE ADDED AND DELETED...IF YOU HAVE MORE THAN ONE VOCAB, MAY GOD HELP YOU!!
+      $_SESSION['quiz_filter'] = $_POST['edit'][4];
     }
     else if ($_POST['op'] =='Submit questions') {
       if (quiz_update_questions($_POST['edit']['question_status'])) {
@@ -728,14 +865,14 @@
       }
     }
   }
-  
+    
   if (isset($_SESSION['quiz_filter'])) {
     $terms = $_SESSION['quiz_filter'];
   }
   else {
     $terms = array();
   }
-  
+
   $quiz = node_load(arg(1));
 
   // Set page title
@@ -892,9 +1029,9 @@
   }
   $sql .= "WHERE n.nid = r.nid AND n.type IN ('". implode("','", _quiz_get_question_types()) ."') ";
   if (count($terms) > 0) {
-    $sql .= 'AND t.tid IN ('. implode(',', $terms) .') AND t.nid = n.nid';
+    $sql .= 'AND t.tid IN ('.$terms.') AND t.nid = n.nid';
   }
-  
+
   $result = db_query($sql);
   
   // Create questions array
@@ -1057,31 +1194,100 @@
   return $results;
 }
 
-
+/*
+ * Quiz Results User
+ */
+function quiz_user_results() {
+  $questions = _quiz_get_answers(arg(2));
+  $result = db_fetch_object(db_query('SELECT quiz.nid FROM quiz, quiz_result WHERE quiz_result.quiz_nid = quiz.nid AND quiz_result.rid = %d', arg(2)));
+  return _quiz_get_results_table($questions, $result->nid, FALSE);
+}
 
 /*
  * Quiz Results Admin
  */
 function quiz_admin_results() {
   $questions = _quiz_get_answers(arg(2));
-  
+  $result = db_fetch_object(db_query('SELECT quiz.nid FROM quiz, quiz_result WHERE quiz_result.quiz_nid = quiz.nid AND quiz_result.rid = %d', arg(2)));
+  return _quiz_get_results_table($questions, $result->nid);
+}
+
+
+
+function _quiz_get_results_table($questions, $arg, $showPoints = TRUE, $showFeedback = FALSE) {
+  theme_add_style('themes/bluemarine/print_style.css', 'print');
   $output = '';
   $rows = array();
+  $totalScore = 0;
+
+  //get the quiz question using the node nid
+  $quizresult = db_fetch_object(db_query('SELECT title FROM node WHERE nid = %d', $arg));
 
   while(list($key, $question) = each($questions)) {
     $question['qanswer'] = unserialize($question['qanswer']);
-    $score = module_invoke($question['type'], 'calculate_result', $question['qanswer']['answers'], $question['qanswer']['tried']);
+    $result = module_invoke($question['type'], 'calculate_results', $question['qanswer']['answers'], $question['qanswer']['tried'], $showPoints, $showFeedback);
     
-    #dprint_r($question);
-    $rows[] = array(
-      $question['question'],
-      $score,
-      );
+    $totalScore += $result['score'];
+    
+    
+    //
+    $cols = array();
+    
+    //Question Answers Table
+    $innerHeader = array('&nbsp;');
+    if($showPoints) $innerHeader[] = 'Answer';
+    $innerHeader[] = 'User Answer';
+    if($showFeedback) $innerHeader[] = 'Feedback';
+        
+    $cols[] = '<b><i>'.$question['question'].'</i></b>'.theme('table', $innerHeader, $result['resultstable']);
+    
+    if($result['score'] == 1) {
+      $cols[] = '<img src=\''.base_path().'files/tick.png\' />';
+    }
+    else {
+      $cols[] = '<img src=\''.base_path().'files/cross.png\' />';      
+    }
+    
+    $rows[] = $cols;
+  }
+
+  //work out the score as a percentage
+  $numOfQuestions = quiz_get_number_of_questions($arg);
+  $percentageScore = round(($totalScore*100)/$numOfQuestions);
+
+  //work out if the student passed or failed
+  $passrate = quiz_get_pass_rate($arg);  
+  if ($percentageScore >= $passrate) {
+    if (arg(3) == 'view') {
+      $rows2[] = array(array('data' => '<b>The student passed this quiz.</b>', 'colspan' => '2'));      
+    }
+    else {
+      $rows2[] = array(array('data' => '<b>Congratulations, you have scored '.$passrate.'%.</b><br /><br /> Please print off this results page (using the File then Print function on your top toolbar) and take it to your Course Mentor, together with your completed Tasks for the module.', 'colspan' => '2'));      
+    }
+  } 
+  else if ($percentageScore < $passrate) {
+    if (arg(3) == 'view') {
+      $rows2[] = array(array('data' => '<b>The student failed this quiz.</b>', 'colspan' => '2'));
+    }
+    else {
+      $rows2[] = array(array('data' => '<b>That was a good effort, but you will need to take the test again. Because of the importance of Child Protection training, we need to make sure you score 100% in all of the tests.</b><br /><br />You may want to print this results page (using the File then Print function on your top toolbar), so you can refer to it when you take the test again. This will help you move quickly through the questions you already got right, and give you extra time to concentrate on the ones you got wrong.<br />', 'colspan' => '2'));
+      $rows2[] = array(array('data' => t('If you would like to retake the test now, please ').l('click here.', 'node/'.$arg.'/quiz/start'), 'colspan' => '2'));      
+      $rows2[] = array(array('data' => t('If you would prefer to retake the test later, ').l('click here', 'http://www.optimusprolearning.com').t(' to return to the main course log-in page.'), 'colspan' => '2'));            
+    }
   }
   
-  $header = array(t('Question'), t('Right or Wrong?'),);
+  $rows2[] = array(array('data' => '<b>Total Score</b> &mdash; '.$totalScore.'/'.$numOfQuestions, 'colspan'=> '2'));
+
+  $rows2[] = array(array('data' => '<b>Total Percentage</b> &mdash; '.$percentageScore.'%', 'colspan' => '2'));
+  $header = array(t('Question(s)'), t('Score'));
+  $header2 = array(array('data'=> t('Results and Options'), 'colspan' => '2'));
   
   if (isset($rows)) {
+    $output .= '<br /><br />';   
+    $output .= 'Test title &mdash; <b>'.$quizresult->title.'</b>';  
+    $output .= '<br /><br />';   
+    $output .= theme('table', $header2, $rows2); 
+    $output .= '<br /><br />'; 
     $output .= theme('table', $header, $rows);
   }
   else {
@@ -1090,6 +1296,11 @@
   return $output;
 }
 
+
+
+
+
+
 /*
  * Delete Result
  */
