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