ee9de9ee8a009ded68e3a5848918e5512bac5ddf
[atutor.git] / docs / mods / _standard / tests / take_test_q.php
1 <?php
2 /****************************************************************/
3 /* ATutor                                                                                                               */
4 /****************************************************************/
5 /* Copyright (c) 2002-2010                                      */
6 /* Inclusive Design Institute                                   */
7 /* http://atutor.ca                                                                                             */
8 /*                                                              */
9 /* This program is free software. You can redistribute it and/or*/
10 /* modify it under the terms of the GNU General Public License  */
11 /* as published by the Free Software Foundation.                                */
12 /****************************************************************/
13 // $Id$
14 define('AT_INCLUDE_PATH', '../../../include/');
15 require(AT_INCLUDE_PATH.'vitals.inc.php');
16 require(AT_INCLUDE_PATH.'../mods/_standard/tests/lib/test_result_functions.inc.php');
17 require(AT_INCLUDE_PATH.'../mods/_standard/tests/classes/testQuestions.class.php');
18
19 $tid = intval($_REQUEST['tid']);
20 if (isset($_REQUEST['cid']))
21 {
22         $cid = intval($_REQUEST['cid']);
23         $cid_url = SEP.'cid='.$cid;
24 }
25
26 if (isset($_REQUEST['gid']))
27         $mid = $addslashes($_REQUEST['gid']);
28 else
29         $mid = $_SESSION['member_id'];
30
31 //make sure max attempts not reached, and still on going
32 $sql            = "SELECT *, UNIX_TIMESTAMP(start_date) AS start_date, UNIX_TIMESTAMP(end_date) AS end_date FROM ".TABLE_PREFIX."tests WHERE test_id=".$tid." AND course_id=".$_SESSION['course_id'];
33 $result= mysql_query($sql, $db);
34 $test_row = mysql_fetch_assoc($result);
35 /* check to make sure we can access this test: */
36 if (!$test_row['guests'] && ($_SESSION['enroll'] == AT_ENROLL_NO || $_SESSION['enroll'] == AT_ENROLL_ALUMNUS)) {
37         require(AT_INCLUDE_PATH.'header.inc.php');
38         $msg->printInfos('NOT_ENROLLED');
39
40         require(AT_INCLUDE_PATH.'footer.inc.php');
41         exit;
42 }
43
44 if (!$test_row['guests'] && !authenticate_test($tid)) {
45         header('Location: '.url_rewrite('mods/_standard/tests/my_tests.php', AT_PRETTY_URL_IS_HEADER));
46         exit;
47 }
48
49 // checks one/all questions per page, and forward user to the correct one
50 if (!$test_row['display']) {
51         header('Location: '.url_rewrite('mods/_standard/tests/take_test.php?tid='.$tid.$cid_url, AT_PRETTY_URL_IS_HEADER));
52 }
53
54 $out_of = $test_row['out_of'];
55
56 $sql    = "SELECT COUNT(*) AS num_questions FROM ".TABLE_PREFIX."tests_questions_assoc WHERE test_id=$tid";
57 $result = mysql_query($sql, $db);
58 $row = mysql_fetch_assoc($result);
59 if (!$test_row['random'] || $test_row['num_questions'] > $row['num_questions']) {
60         $test_row['num_questions'] = $row['num_questions'];
61 }
62
63 $sql = "SELECT COUNT(*) AS cnt FROM ".TABLE_PREFIX."tests_results WHERE status=1 AND test_id=".$tid." AND member_id='".$mid."'";
64 $takes_result= mysql_query($sql, $db) or die(mysql_error());
65 $takes = mysql_fetch_assoc($takes_result);      
66
67 if ( (($test_row['start_date'] > time()) || ($test_row['end_date'] < time())) || 
68    ( ($test_row['num_takes'] != AT_TESTS_TAKE_UNLIMITED) && ($takes['cnt'] >= $test_row['num_takes']) )  ) {
69         require(AT_INCLUDE_PATH.'header.inc.php');
70         $msg->printInfos('MAX_ATTEMPTS');
71         require(AT_INCLUDE_PATH.'footer.inc.php');
72         exit;
73 }
74
75 if (!isset($_GET['pos'])) {
76         $pos = 0; // first question
77 } else {
78         $pos = abs($_GET['pos']);
79 }
80
81 $max_pos = 0;
82
83 // get and check for a valid result_id. if there is none then get all the questions and insert them as in progress.
84 // note: for guests without guest information, the result_id is stored in session, but no need to really know that here;
85 //       for guests with guest information, always start a new test.
86 if (isset($_REQUEST['gid']))
87         $result_id = 0;
88 else
89         $result_id = get_test_result_id($tid, $max_pos);
90         
91 // set position to allow users to return to a test they have partially completed, and continue from where they left of.
92 if (!isset($_GET['pos']) && $result_id > 0)
93 {
94         $sql = "SELECT COUNT(*) total_questions FROM ".TABLE_PREFIX."tests_answers WHERE result_id = ". $result_id;
95         $total_result = mysql_query($sql, $db) or die(mysql_error());
96         $total = mysql_fetch_assoc($total_result);
97         
98         $sql = "SELECT COUNT(*) pos FROM ".TABLE_PREFIX."tests_answers WHERE result_id = ". $result_id ." AND answer <> ''";
99         $answer_result = mysql_query($sql, $db) or die(mysql_error());
100         $answer = mysql_fetch_assoc($answer_result);
101
102         // if user answered all the questions without cliking last "next" button, resume test at the last question
103         $pos = ($total['total_questions'] == $answer['pos']) ? (--$answer['pos']) : $answer['pos'];
104 }
105
106 if ($result_id == 0) {
107         // there is no test in progress, yet.
108         // init this test.
109
110         // simple safety op to make sure nothing is being posted (as it shouldn't!)
111         // $_POST = array(); // don't need this because of the else-if
112         // basically, shouldn't be able to post to this page if there isn't a valid result_id first (how can someone post an answer
113         // to a question they haven't viewed? [unless they're trying to 'hack' something])
114
115         $result_id = init_test_result_questions($tid, (bool) $test_row['random'], $test_row['num_questions'], $mid);
116
117         if (!$_SESSION['member_id']) {
118                 // this is a guest, so we store the result_id in SESSION
119                 $_SESSION['test_result_id'] = $result_id;
120         }
121
122         $pos = 0; // force to always start at the first question
123                 
124 } else if (isset($_POST['next']) || isset($_POST['previous'])) {
125         // if the test isn't time limited, then what happens when only a few questions are answered? the test result
126         // will be inconsistant.
127         // need to keep track of the max(pos) answered, so that we know if a question is being re-answered.
128         // store 'max_pos' in session or db or form?
129
130         // assuming only one question is displayed      
131         $question_id = intval(key($_POST['answers']));
132
133         // get the old score (incase this question is being re-answered)
134         $sql = "SELECT score FROM ".TABLE_PREFIX."tests_answers WHERE result_id=$result_id AND question_id=$question_id";
135         $result = mysql_query($sql, $db);
136         $row = mysql_fetch_assoc($result);
137         $old_score = $row['score'];
138
139         $score = 0;
140
141         $sql = "SELECT TQA.weight, TQA.question_id, TQ.type, TQ.answer_0, TQ.answer_1, TQ.answer_2, TQ.answer_3, TQ.answer_4, TQ.answer_5, TQ.answer_6, TQ.answer_7, TQ.answer_8, TQ.answer_9 FROM ".TABLE_PREFIX."tests_questions_assoc TQA INNER JOIN ".TABLE_PREFIX."tests_questions TQ USING (question_id) WHERE TQA.test_id=$tid AND TQA.question_id=$question_id";
142         $result = mysql_query($sql, $db);
143
144         if ($row = mysql_fetch_assoc($result)) {
145                 if (isset($_POST['answers'][$row['question_id']])) {
146                         $obj = TestQuestions::getQuestion($row['type']);
147                         $score = $obj->mark($row);
148
149                         $sql    = "UPDATE ".TABLE_PREFIX."tests_answers SET answer='{$_POST[answers][$row[question_id]]}', score='$score' WHERE result_id=$result_id AND question_id=$row[question_id]";
150                         mysql_query($sql, $db);
151                         
152                         if (is_null($score) && $test_row['result_release']==AT_RELEASE_MARKED)
153                                 $_REQUEST['efs'] = 1; // set final score to empty if there's any unmarked question and release option is "once quiz submitted and all questions are marked"
154                 }
155         }
156
157         $pos++;
158
159         if ($_REQUEST['efs']) // set final score to empty if there's any unmarked question and release option is "once quiz submitted and all questions are marked"
160         {
161                 $sql    = "UPDATE ".TABLE_PREFIX."tests_results SET final_score=null, date_taken=date_taken, end_time=NOW(), max_pos=$pos WHERE result_id=$result_id";
162                 $result = mysql_query($sql, $db);
163         }
164         // update the final score
165         // update status to complate to fix refresh test issue.
166         else if ($pos > $max_pos) {
167                 $sql    = "UPDATE ".TABLE_PREFIX."tests_results SET final_score=final_score + $score, date_taken=date_taken, end_time=NOW(), max_pos=$pos WHERE result_id=$result_id";
168                 $result = mysql_query($sql, $db);
169         } else {
170                 // this question has already been answered, so we have to re-mark it, which means finding the OLD score for this question and adjusting
171                 // $score with the positive or negative difference.
172                 // no need to update max_pos b/c we're only updating a previously answered question.
173
174                 $score = $old_score - $score;
175
176                 $sql    = "UPDATE ".TABLE_PREFIX."tests_results SET final_score=final_score - $score, date_taken=date_taken, end_time=NOW() WHERE result_id=$result_id";
177                 $result = mysql_query($sql, $db);
178         }
179
180         if (isset($_POST['previous'])) {
181                 $pos-=2;
182                 if ($pos < 0) {
183                         $pos = 0;
184                 }
185         }
186
187         if ($pos >= $test_row['num_questions']) {
188                 // end of the test.
189                 $sql    = "UPDATE ".TABLE_PREFIX."tests_results SET status=1, date_taken=date_taken, end_time=NOW() WHERE result_id=$result_id";
190                 $result = mysql_query($sql, $db);
191
192                 $msg->addFeedback('ACTION_COMPLETED_SUCCESSFULLY');
193                 if ((!$_SESSION['enroll'] && !isset($cid)) || $test_row['result_release']==AT_RELEASE_IMMEDIATE) {
194                         header('Location: '.url_rewrite('mods/_standard/tests/view_results.php?tid='.$tid.SEP.'rid='.$result_id.$cid_url, AT_PRETTY_URL_IS_HEADER));
195                         exit;
196                 }
197                 
198                 if (isset($cid)) header('Location: '.url_rewrite('content.php?cid='.$cid, AT_PRETTY_URL_IS_HEADER));
199                 else header('Location: '.url_rewrite('mods/_standard/tests/my_tests.php', AT_PRETTY_URL_IS_HEADER));
200                 exit;
201         } // else:
202         
203         header('Location: '.url_rewrite('mods/_standard/tests/take_test_q.php?tid='.$tid.SEP.'pos='.$pos.SEP.'efs='.$_REQUEST['efs'].$cid_url, AT_PRETTY_URL_IS_HEADER));
204         exit;
205 }
206
207 if (defined('AT_FORCE_GET_FILE') && AT_FORCE_GET_FILE) {
208         $content_base_href = 'get.php/';
209 } else {
210         $course_base_href = 'content/' . $_SESSION['course_id'] . '/';
211 }
212
213 require(AT_INCLUDE_PATH.'header.inc.php');
214
215 /* Retrieve the content_id of this test */
216 $num_questions = $test_row['num_questions'];
217 $content_id = $test_row['content_id'];
218 $anonymous = $test_row['anonymous'];
219 $instructions = $test_row['instructions'];
220 $title = $test_row['title'];
221
222 $_letters = array(_AT('A'), _AT('B'), _AT('C'), _AT('D'), _AT('E'), _AT('F'), _AT('G'), _AT('H'), _AT('I'), _AT('J'));
223
224 // this is a kludge to get the question number incremented.
225 // a diff approach could be to pass the position to the display() method.
226 for ($i = 0; $i < $pos; $i++) {
227         TestQuestionCounter(true);
228 }
229
230 // retrieve the test questions that were saved to `tests_answers`
231 if ($test_row['random']) {
232         $sql    = "SELECT R.*, A.*, Q.* FROM ".TABLE_PREFIX."tests_answers R INNER JOIN ".TABLE_PREFIX."tests_questions_assoc A USING (question_id) INNER JOIN ".TABLE_PREFIX."tests_questions Q USING (question_id) WHERE R.result_id=$result_id AND A.test_id=$tid ORDER BY Q.question_id LIMIT $pos, 1";
233 } else {
234         $sql    = "SELECT R.*, A.*, Q.* FROM ".TABLE_PREFIX."tests_answers R INNER JOIN ".TABLE_PREFIX."tests_questions_assoc A USING (question_id) INNER JOIN ".TABLE_PREFIX."tests_questions Q USING (question_id) WHERE R.result_id=$result_id AND A.test_id=$tid ORDER BY A.ordering, Q.question_id LIMIT $pos, 1";
235 }
236 $result = mysql_query($sql, $db);
237 $question_row = mysql_fetch_assoc($result);
238
239 if (!$result || !$question_row) {
240         echo '<p>'._AT('no_questions').'</p>';
241         require(AT_INCLUDE_PATH.'footer.inc.php');
242         exit;
243 }
244
245 ?>
246 <form method="post" action="<?php echo $_SERVER['PHP_SELF']; ?>?pos=<?php echo $pos; ?>">
247 <input type="hidden" name="tid" value="<?php echo $tid; ?>" />
248 <?php if (isset($_REQUEST['cid'])) {?> <input type="hidden" name="cid" value="<?php echo $cid; ?>" /> <?php }?>
249
250 <div class="input-form" style="width:95%">
251
252         <fieldset class="group_form"><legend class="group_form"><?php echo $title ?> (<?php echo _AT('question').' '. ($pos+1).'/'.$test_row['num_questions']; ?>)</legend>
253         <?php if ($_REQUEST['efs']){?>
254         <input type="hidden" name="efs" value=<?php echo $_REQUEST['efs']; ?> />
255         <?php }?>
256         
257         <?php
258         // retrieve the answer to re-populate the form (so we can edit our answer)
259         $sql = "SELECT answer FROM ".TABLE_PREFIX."tests_answers WHERE result_id=$result_id AND question_id=$question_row[question_id]";
260         $result = mysql_query($sql, $db);
261         $row = mysql_fetch_assoc($result);
262         
263         $obj = TestQuestions::getQuestion($question_row['type']);
264         $obj->display($question_row, $row['answer']);
265
266         ?>
267         <div class="row buttons">
268                  <div style="display:none"><input type="submit" value="<?php echo _AT('next'); ?>" name="next"/></div>
269                 <?php if ($pos > 0): ?>
270                         <input type="submit" name="previous" value="<?php echo _AT('previous'); ?>" />
271                 <?php endif; ?>
272                 <?php if (($pos+1) == $test_row['num_questions']): ?>
273                         <input type="submit" name="next" value="<?php echo _AT('submit'); ?>" accesskey="s" onclick="confirmSubmit(this, '<?php echo $addslashes(_AT("test_confirm_submit")); ?>'); return false;"/>
274                 <?php else: ?>
275                         <input type="submit" name="next" value="<?php echo _AT('next'); ?>" accesskey="s" />
276                 <?php endif; ?>
277         </div>
278         </fieldset>
279 </div>
280 </form>
281 <script type="text/javascript" src="<?php echo $_base_href;?>/mods/_standard/tests/lib/take_test.js">
282 <?php require(AT_INCLUDE_PATH.'footer.inc.php'); ?>