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