Show All link below question list.
[moodle.git] / question / questiontypes / shortanswer / questiontype.php
CommitLineData
516cf3eb 1<?php // $Id$
2
3///////////////////
4/// SHORTANSWER ///
5///////////////////
6
7/// QUESTION TYPE CLASS //////////////////
8
9///
10/// This class contains some special features in order to make the
11/// question type embeddable within a multianswer (cloze) question
12///
13
32a189d6 14class question_shortanswer_qtype extends quiz_default_questiontype {
516cf3eb 15
16 function name() {
17 return 'shortanswer';
18 }
19
20 function get_question_options(&$question) {
21 // Get additional information from database
22 // and attach it to the question object
32a189d6 23 if (!$question->options = get_record('question_shortanswer', 'question', $question->id)) {
516cf3eb 24 notify('Error: Missing question options!');
25 return false;
26 }
27
dc1f00de 28 if (!$question->options->answers = get_records('question_answers', 'question',
516cf3eb 29 $question->id, 'id ASC')) {
30 notify('Error: Missing question answers!');
31 return false;
32 }
33 return true;
34 }
35
36 function save_question_options($question) {
dc1f00de 37 if (!$oldanswers = get_records("question_answers", "question", $question->id, "id ASC")) {
516cf3eb 38 $oldanswers = array();
39 }
40
41 $answers = array();
42 $maxfraction = -1;
43
44 // Insert all the new answers
45 foreach ($question->answer as $key => $dataanswer) {
46 if ($dataanswer != "") {
47 if ($oldanswer = array_shift($oldanswers)) { // Existing answer, so reuse it
48 $answer = $oldanswer;
49 $answer->answer = trim($dataanswer);
50 $answer->fraction = $question->fraction[$key];
51 $answer->feedback = $question->feedback[$key];
dc1f00de 52 if (!update_record("question_answers", $answer)) {
516cf3eb 53 $result->error = "Could not update quiz answer! (id=$answer->id)";
54 return $result;
55 }
56 } else { // This is a completely new answer
57 unset($answer);
58 $answer->answer = trim($dataanswer);
59 $answer->question = $question->id;
60 $answer->fraction = $question->fraction[$key];
61 $answer->feedback = $question->feedback[$key];
dc1f00de 62 if (!$answer->id = insert_record("question_answers", $answer)) {
516cf3eb 63 $result->error = "Could not insert quiz answer!";
64 return $result;
65 }
66 }
67 $answers[] = $answer->id;
68 if ($question->fraction[$key] > $maxfraction) {
69 $maxfraction = $question->fraction[$key];
70 }
71 }
72 }
73
32a189d6 74 if ($options = get_record("question_shortanswer", "question", $question->id)) {
516cf3eb 75 $options->answers = implode(",",$answers);
76 $options->usecase = $question->usecase;
32a189d6 77 if (!update_record("question_shortanswer", $options)) {
516cf3eb 78 $result->error = "Could not update quiz shortanswer options! (id=$options->id)";
79 return $result;
80 }
81 } else {
82 unset($options);
83 $options->question = $question->id;
84 $options->answers = implode(",",$answers);
85 $options->usecase = $question->usecase;
32a189d6 86 if (!insert_record("question_shortanswer", $options)) {
516cf3eb 87 $result->error = "Could not insert quiz shortanswer options!";
88 return $result;
89 }
90 }
91
92 // delete old answer records
93 if (!empty($oldanswers)) {
94 foreach($oldanswers as $oa) {
dc1f00de 95 delete_records('question_answers', 'id', $oa->id);
516cf3eb 96 }
97 }
98
99 /// Perform sanity checks on fractional grades
100 if ($maxfraction != 1) {
101 $maxfraction = $maxfraction * 100;
102 $result->noticeyesno = get_string("fractionsnomax", "quiz", $maxfraction);
103 return $result;
104 } else {
105 return true;
106 }
107 }
108
109 /**
110 * Deletes question from the question-type specific tables
111 *
112 * @return boolean Success/Failure
113 * @param object $question The question being deleted
114 */
115 function delete_question($question) {
32a189d6 116 delete_records("question_shortanswer", "question", $question->id);
117 //TODO: delete also the states from question_rqp_states
516cf3eb 118 return true;
119 }
120
121 function print_question_formulation_and_controls(&$question, &$state, $cmoptions, $options) {
122 global $CFG;
123 /// This implementation is also used by question type NUMERICAL
124 $answers = &$question->options->answers;
125 $correctanswers = $this->get_correct_responses($question, $state);
126 $readonly = empty($options->readonly) ? '' : 'readonly="readonly"';
127 $nameprefix = $question->name_prefix;
128
129 /// Print question text and media
130
131 $questiontext = format_text($question->questiontext,
132 $question->questiontextformat,
133 NULL, $cmoptions->course);
4f48fb42 134 $image = get_question_image($question, $cmoptions->course);
516cf3eb 135
136 /// Print input controls
137
138 if (isset($state->responses[''])) {
139 $value = ' value="'.s($state->responses['']).'" ';
140 } else {
141 $value = ' value="" ';
142 }
143 $inputname = ' name="'.$nameprefix.'" ';
144
145 $feedback = '';
146 if ($options->feedback) {
516cf3eb 147 foreach($answers as $answer) {
37a12367 148 if($answer->feedback and $this->test_response($question, $state, $answer)) {
516cf3eb 149 $feedback = format_text($answer->feedback, true, false);
150 break;
151 }
152 }
153 }
154
155 $correctanswer = '';
156 if ($options->readonly && $options->correct_responses) {
157 $delimiter = '';
158 if ($correctanswers) {
159 foreach ($correctanswers as $ca) {
160 $correctanswer .= $delimiter.$ca;
161 $delimiter = ', ';
162 }
163 }
164 }
e586cfb4 165 include("$CFG->dirroot/question/questiontypes/shortanswer/display.html");
516cf3eb 166 }
167
168 // ULPGC ecastro
169 function check_response(&$question, &$state) {
170 $answers = &$question->options->answers;
171 $testedstate = clone($state);
172 $teststate = clone($state);
173 foreach($answers as $aid => $answer) {
174 $teststate->responses[''] = trim($answer->answer);
175 if($this->compare_responses($question, $testedstate, $teststate)) {
176 return $aid;
177 }
178 }
179 return false;
180 }
181
182 function grade_responses(&$question, &$state, $cmoptions) {
183 $answers = &$question->options->answers;
184 $testedstate = clone($state);
185 $teststate = clone($state);
186 $state->raw_grade = 0;
187
188 foreach($answers as $answer) {
189 $teststate->responses[''] = trim($answer->answer);
190 if($this->compare_responses($question, $testedstate, $teststate)) {
191 $state->raw_grade = $answer->fraction;
192 break;
193 }
194 }
195
196 // Make sure we don't assign negative or too high marks
197 $state->raw_grade = min(max((float) $state->raw_grade,
198 0.0), 1.0) * $question->maxgrade;
199 $state->penalty = $question->penalty * $question->maxgrade;
200
f30bbcaf 201 // mark the state as graded
202 $state->event = ($state->event == QUESTION_EVENTCLOSE) ? QUESTION_EVENTCLOSEANDGRADE : QUESTION_EVENTGRADE;
203
516cf3eb 204 return true;
205 }
206
207 function compare_responses(&$question, &$state, &$teststate) {
208 if (isset($state->responses[''])) {
209 $response0 = trim(stripslashes($state->responses['']));
210 } else {
211 $response0 = '';
212 }
213
214 if (isset($teststate->responses[''])) {
215 $response1 = trim(stripslashes($teststate->responses['']));
216 } else {
217 $response1 = '';
218 }
219
220 if (!$question->options->usecase) { // Don't compare case
221 $response0 = strtolower($response0);
222 $response1 = strtolower($response1);
223 }
224
225 /// These are things to protect in the strings when wildcards are used
226 $search = array('\\', '+', '(', ')', '[', ']', '-');
227 $replace = array('\\\\', '\+', '\(', '\)', '\[', '\]', '\-');
228
229 if (strpos(' '.$response1, '*')) {
230 $response1 = str_replace('\*','@@@@@@',$response1);
231 $response1 = str_replace('*','.*',$response1);
232 $response1 = str_replace($search, $replace, $response1);
233 $response1 = str_replace('@@@@@@', '\*',$response1);
234
235 if (ereg('^'.$response1.'$', $response0)) {
236 return true;
237 }
238
239 } else if ($response1 == $response0) {
240 return true;
241 }
242
243 return false;
244 }
245
246}
247//// END OF CLASS ////
248
249//////////////////////////////////////////////////////////////////////////
250//// INITIATION - Without this line the question type is not in use... ///
251//////////////////////////////////////////////////////////////////////////
ccccf04f 252// define("SHORTANSWER", "1"); // already defined in questionlib.php
32a189d6 253$QTYPES[SHORTANSWER]= new question_shortanswer_qtype();
ccccf04f 254// The following adds the questiontype to the menu of types shown to teachers
255$QTYPE_MENU[SHORTANSWER] = get_string("shortanswer", "quiz");
516cf3eb 256
257?>