MDL-20636 Add missing defined('MOODLE_INTERNAL') || die();
[moodle.git] / question / engine / states.php
CommitLineData
d1b7e03d
TH
1<?php
2
3// This file is part of Moodle - http://moodle.org/
4//
5// Moodle is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// Moodle is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
17
d1b7e03d
TH
18/**
19 * This defines the states a question can be in.
20 *
017bc1d9 21 * @package moodlecore
d1b7e03d 22 * @subpackage questionengine
017bc1d9
TH
23 * @copyright 2010 The Open University
24 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
d1b7e03d
TH
25 */
26
27
a17b297d
TH
28defined('MOODLE_INTERNAL') || die();
29
30
d1b7e03d
TH
31/**
32 * An enumeration representing the states a question can be in after a
33 * {@link question_attempt_step}.
34 *
35 * There are also some useful methods for testing and manipulating states.
36 *
017bc1d9
TH
37 * @copyright 2009 The Open University
38 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
d1b7e03d
TH
39 */
40
41abstract class question_state {
42 /**#@+
43 * Specific question_state instances.
44 */
45 public static $notstarted;
46 public static $unprocessed;
47 public static $todo;
48 public static $invalid;
49 public static $complete;
50 public static $needsgrading;
51 public static $finished;
52 public static $gaveup;
53 public static $gradedwrong;
54 public static $gradedpartial;
55 public static $gradedright;
56 public static $manfinished;
57 public static $mangaveup;
58 public static $mangrwrong;
59 public static $mangrpartial;
60 public static $mangrright;
61 /**#@+-*/
62
63 protected function __construct() {
64 }
65
66 public static function init() {
67 $us = new ReflectionClass('question_state');
68 foreach ($us->getStaticProperties() as $name => $notused) {
69 $class = 'question_state_' . $name;
70 $states[$name] = new $class();
71 self::$$name = $states[$name];
72 }
73 }
74
75 /**
76 * Get all the states in an array.
77 * @return of question_state objects.
78 */
79 public static function get_all() {
80 $states = array();
81 $us = new ReflectionClass('question_state');
82 foreach ($us->getStaticProperties() as $name => $notused) {
83 $states[] = self::$$name;
84 }
85 return $states;
86 }
87
88 /**
89 * Get all the states in an array.
90 * @param string $summarystate one of the four summary states
91 * inprogress, needsgrading, manuallygraded or autograded.
92 * @return arrau of the corresponding states.
93 */
94 public static function get_all_for_summary_state($summarystate) {
95 $states = array();
96 foreach (self::get_all() as $state) {
97 if ($state->get_summary_state() == $summarystate) {
98 $states[] = $state;
99 }
100 }
101 if (empty($states)) {
102 throw new Exception('unknown summary state ' . $summarystate);
103 }
104 return $states;
105 }
106
107 /**
108 * @return string convert this state to a string.
109 */
110 public function __toString() {
111 return substr(get_class($this), 15);
112 }
113
114 /**
115 * @param string $name a state name.
116 * @return question_state the state with that name.
117 */
118 public static function get($name) {
119 return self::$$name;
120 }
121
122 /**
123 * Is this state one of the ones that mean the question attempt is in progress?
124 * That is, started, but no finished.
125 * @return boolean
126 */
127 public function is_active() {
128 return false;
129 }
130
131 /**
132 * Is this state one of the ones that mean the question attempt is finished?
133 * That is, no further interaction possible, apart from manual grading.
134 * @return boolean
135 */
136 public function is_finished() {
137 return true;
138 }
139
140 /**
141 * Is this state one of the ones that mean the question attempt has been graded?
142 * @return boolean
143 */
144 public function is_graded() {
145 return false;
146 }
147
148 /**
149 * Is this state one of the ones that mean the question attempt has been graded?
150 * @return boolean
151 */
152 public function is_correct() {
153 return false;
154 }
155
156 /**
157 * Is this state one of the ones that mean the question attempt has been graded?
158 * @return boolean
159 */
160 public function is_partially_correct() {
161 return false;
162 }
163
164 /**
165 * Is this state one of the ones that mean the question attempt has been graded?
166 * @return boolean
167 */
168 public function is_incorrect() {
169 return false;
170 }
171
172 /**
173 * Is this state one of the ones that mean the question attempt has been graded?
174 * @return boolean
175 */
176 public function is_gave_up() {
177 return false;
178 }
179
180 /**
181 * Is this state one of the ones that mean the question attempt has had a manual comment added?
182 * @return boolean
183 */
184 public function is_commented() {
185 return false;
186 }
187
188 /**
189 * Each state can be categorised into one of four categories:
190 * inprogress, needsgrading, manuallygraded or autograded.
191 * @return string which category this state falls into.
192 */
193 public function get_summary_state() {
194 if (!$this->is_finished()) {
195 return 'inprogress';
196 } else if ($this == self::$needsgrading) {
197 return 'needsgrading';
198 } else if ($this->is_commented()) {
199 return 'manuallygraded';
200 } else {
201 return 'autograded';
202 }
203 }
204
205 /**
206 * Return the appropriate graded state based on a fraction. That is 0 or less
207 * is $graded_incorrect, 1 is $graded_correct, otherwise it is $graded_partcorrect.
208 * Appropriate allowance is made for rounding float values.
209 *
210 * @param number $fraction the grade, on the fraction scale.
211 * @return integer one of the state constants.
212 */
213 public static function graded_state_for_fraction($fraction) {
214 if ($fraction < 0.000001) {
215 return self::$gradedwrong;
216 } else if ($fraction > 0.999999) {
217 return self::$gradedright;
218 } else {
219 return self::$gradedpartial;
220 }
221 }
222
223 /**
224 * Return the appropriate manually graded state based on a fraction. That is 0 or less
225 * is $manually_graded_incorrect, 1 is $manually_graded_correct, otherwise it is
226 * $manually_graded_partcorrect. Appropriate allowance is made for rounding float values.
227 *
228 * @param number $fraction the grade, on the fraction scale.
229 * @return integer one of the state constants.
230 */
231 public static function manually_graded_state_for_fraction($fraction) {
232 if (is_null($fraction)) {
233 return self::$needsgrading;
234 } else if ($fraction < 0.000001) {
235 return self::$mangrwrong;
236 } else if ($fraction > 0.999999) {
237 return self::$mangrright;
238 } else {
239 return self::$mangrpartial;
240 }
241 }
242
243 /**
244 * Compute an appropriate state to move to after a manual comment has been
245 * added to this state.
246 * @param number $fraction the manual grade (if any) on the fraction scale.
247 * @return integer the new state.
248 */
249 public function corresponding_commented_state($fraction) {
250 throw new Exception('Unexpected question state.');
251 }
252
253 /**
254 * Return an appropriate CSS class name ''/'correct'/'partiallycorrect'/'incorrect',
255 * for a state.
256 * @return string
257 */
258 public function get_feedback_class() {
259 return '';
260 }
261
262 /**
263 * Return the name of an appropriate string to look up in the question
264 * language pack for a state. This is used, for example, by
265 * {@link question_behaviour::get_state_string()}. However, behaviours
266 * sometimes change this default string for soemthing more specific.
267 *
268 * @param boolean $showcorrectness Whether right/partial/wrong states should
269 * be distinguised, or just treated as 'complete'.
270 * @return string the name of a string that can be looked up in the 'question'
271 * lang pack, or used as a CSS class name, etc.
272 */
273 public abstract function get_state_class($showcorrectness);
274
275 /**
276 * The result of doing get_string on the result of {@link get_state_class()}.
277 *
278 * @param boolean $showcorrectness Whether right/partial/wrong states should
279 * be distinguised.
280 * @return string a string from the lang pack that can be used in the UI.
281 */
282 public function default_string($showcorrectness) {
283 return get_string($this->get_state_class($showcorrectness), 'question');
284 }
285}
286
287
288/**#@+
289 * Specific question_state subclasses.
290 *
017bc1d9
TH
291 * @copyright 2009 The Open University
292 * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
d1b7e03d
TH
293 */
294class question_state_notstarted extends question_state {
295 public function is_finished() {
296 return false;
297 }
298 public function get_state_class($showcorrectness) {
299 throw new Exception('Unexpected question state.');
300 }
301}
302class question_state_unprocessed extends question_state {
303 public function is_finished() {
304 return false;
305 }
306 public function get_state_class($showcorrectness) {
307 throw new Exception('Unexpected question state.');
308 }
309}
310class question_state_todo extends question_state {
311 public function is_active() {
312 return true;
313 }
314 public function is_finished() {
315 return false;
316 }
317 public function get_state_class($showcorrectness) {
318 return 'notyetanswered';
319 }
320}
321class question_state_invalid extends question_state {
322 public function is_active() {
323 return true;
324 }
325 public function is_finished() {
326 return false;
327 }
328 public function get_state_class($showcorrectness) {
329 return 'invalidanswer';
330 }
331}
332class question_state_complete extends question_state {
333 public function is_active() {
334 return true;
335 }
336 public function is_finished() {
337 return false;
338 }
339 public function get_state_class($showcorrectness) {
340 return 'answersaved';
341 }
342}
343class question_state_needsgrading extends question_state {
344 public function get_state_class($showcorrectness) {
f86390dc
TH
345 if ($showcorrectness) {
346 return 'requiresgrading';
347 } else {
348 return 'complete';
349 }
d1b7e03d
TH
350 }
351 public function corresponding_commented_state($fraction) {
352 return self::manually_graded_state_for_fraction($fraction);
353 }
354}
355class question_state_finished extends question_state {
356 public function get_state_class($showcorrectness) {
357 return 'complete';
358 }
359 public function corresponding_commented_state($fraction) {
360 return self::$manfinished;
361 }
362}
363class question_state_gaveup extends question_state {
364 public function is_gave_up() {
365 return true;
366 }
367 public function get_feedback_class() {
368 return 'incorrect';
369 }
370 public function get_state_class($showcorrectness) {
371 return 'notanswered';
372 }
373 public function corresponding_commented_state($fraction) {
374 if (is_null($fraction)) {
375 return self::$mangaveup;
376 } else {
377 return self::manually_graded_state_for_fraction($fraction);
378 }
379 }
380}
381abstract class question_state_graded extends question_state {
382 public function is_graded() {
383 return true;
384 }
385 public function get_state_class($showcorrectness) {
386 if ($showcorrectness) {
387 return $this->get_feedback_class();
388 } else {
389 return 'complete';
390 }
391 }
392 public function corresponding_commented_state($fraction) {
393 return self::manually_graded_state_for_fraction($fraction);
394 }
395}
396class question_state_gradedwrong extends question_state_graded {
397 public function is_incorrect() {
398 return true;
399 }
400 public function get_feedback_class() {
401 return 'incorrect';
402 }
403}
404class question_state_gradedpartial extends question_state_graded {
405 public function is_graded() {
406 return true;
407 }
408 public function is_partially_correct() {
409 return true;
410 }
411 public function get_feedback_class() {
412 return 'partiallycorrect';
413 }
414}
415class question_state_gradedright extends question_state_graded {
416 public function is_graded() {
417 return true;
418 }
419 public function is_correct() {
420 return true;
421 }
422 public function get_feedback_class() {
423 return 'correct';
424 }
425}
426class question_state_manfinished extends question_state_finished {
427 public function is_commented() {
428 return true;
429 }
430}
431class question_state_mangaveup extends question_state_gaveup {
432 public function is_commented() {
433 return true;
434 }
435}
436abstract class question_state_manuallygraded extends question_state_graded {
437 public function is_commented() {
438 return true;
439 }
440}
441class question_state_mangrwrong extends question_state_manuallygraded {
442 public function is_incorrect() {
443 return false;
444 }
445 public function get_feedback_class() {
446 return 'incorrect';
447 }
448}
449class question_state_mangrpartial extends question_state_manuallygraded {
450 public function is_partially_correct() {
451 return true;
452 }
453 public function get_feedback_class() {
454 return 'partiallycorrect';
455 }
456}
457class question_state_mangrright extends question_state_manuallygraded {
458 public function is_correct() {
459 return true;
460 }
461 public function get_feedback_class() {
462 return 'correct';
463 }
464}
465/**#@-*/
466question_state::init();