|
Moodle
2.2.1
http://www.collinsharper.com
|
00001 <?php 00002 // This file is part of Moodle - http://moodle.org/ 00003 // 00004 // Moodle is free software: you can redistribute it and/or modify 00005 // it under the terms of the GNU General Public License as published by 00006 // the Free Software Foundation, either version 3 of the License, or 00007 // (at your option) any later version. 00008 // 00009 // Moodle is distributed in the hope that it will be useful, 00010 // but WITHOUT ANY WARRANTY; without even the implied warranty of 00011 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 00012 // GNU General Public License for more details. 00013 // 00014 // You should have received a copy of the GNU General Public License 00015 // along with Moodle. If not, see <http://www.gnu.org/licenses/>. 00016 00028 defined('MOODLE_INTERNAL') || die(); 00029 00030 00042 class qbehaviour_interactive extends question_behaviour_with_save { 00043 const IS_ARCHETYPAL = true; 00044 00053 const READONLY_EXCEPT_TRY_AGAIN = 23485299; 00054 00055 public function is_compatible_question(question_definition $question) { 00056 return $question instanceof question_automatically_gradable; 00057 } 00058 00059 public function get_right_answer_summary() { 00060 return $this->question->get_right_answer_summary(); 00061 } 00062 00066 protected function is_try_again_state() { 00067 $laststep = $this->qa->get_last_step(); 00068 return $this->qa->get_state()->is_active() && $laststep->has_behaviour_var('submit') && 00069 $laststep->has_behaviour_var('_triesleft'); 00070 } 00071 00072 public function adjust_display_options(question_display_options $options) { 00073 // We only need different behaviour in try again states. 00074 if (!$this->is_try_again_state()) { 00075 parent::adjust_display_options($options); 00076 return; 00077 } 00078 00079 // Let the hint adjust the options. 00080 $hint = $this->get_applicable_hint(); 00081 if (!is_null($hint)) { 00082 $hint->adjust_display_options($options); 00083 } 00084 00085 // Now call the base class method, but protect some fields from being overwritten. 00086 $save = clone($options); 00087 parent::adjust_display_options($options); 00088 $options->feedback = $save->feedback; 00089 $options->numpartscorrect = $save->numpartscorrect; 00090 00091 // In a try-again state, everything except the try again button 00092 // Should be read-only. This is a mild hack to achieve this. 00093 if (!$options->readonly) { 00094 $options->readonly = self::READONLY_EXCEPT_TRY_AGAIN; 00095 } 00096 } 00097 00098 public function get_applicable_hint() { 00099 if (!$this->is_try_again_state()) { 00100 return null; 00101 } 00102 return $this->question->get_hint(count($this->question->hints) - 00103 $this->qa->get_last_behaviour_var('_triesleft'), $this->qa); 00104 } 00105 00106 public function get_expected_data() { 00107 if ($this->is_try_again_state()) { 00108 return array( 00109 'tryagain' => PARAM_BOOL, 00110 ); 00111 } else if ($this->qa->get_state()->is_active()) { 00112 return array( 00113 'submit' => PARAM_BOOL, 00114 ); 00115 } 00116 return parent::get_expected_data(); 00117 } 00118 00119 public function get_expected_qt_data() { 00120 $hint = $this->get_applicable_hint(); 00121 if (!empty($hint->clearwrong)) { 00122 return $this->question->get_expected_data(); 00123 } 00124 return parent::get_expected_qt_data(); 00125 } 00126 00127 public function get_state_string($showcorrectness) { 00128 $state = $this->qa->get_state(); 00129 if (!$state->is_active() || $state == question_state::$invalid) { 00130 return parent::get_state_string($showcorrectness); 00131 } 00132 00133 if ($this->is_try_again_state()) { 00134 return get_string('notcomplete', 'qbehaviour_interactive'); 00135 } else { 00136 return get_string('triesremaining', 'qbehaviour_interactive', 00137 $this->qa->get_last_behaviour_var('_triesleft')); 00138 } 00139 } 00140 00141 public function init_first_step(question_attempt_step $step, $variant) { 00142 parent::init_first_step($step, $variant); 00143 $step->set_behaviour_var('_triesleft', count($this->question->hints) + 1); 00144 } 00145 00146 public function process_action(question_attempt_pending_step $pendingstep) { 00147 if ($pendingstep->has_behaviour_var('finish')) { 00148 return $this->process_finish($pendingstep); 00149 } 00150 if ($this->is_try_again_state()) { 00151 if ($pendingstep->has_behaviour_var('tryagain')) { 00152 return $this->process_try_again($pendingstep); 00153 } else { 00154 return question_attempt::DISCARD; 00155 } 00156 } else { 00157 if ($pendingstep->has_behaviour_var('comment')) { 00158 return $this->process_comment($pendingstep); 00159 } else if ($pendingstep->has_behaviour_var('submit')) { 00160 return $this->process_submit($pendingstep); 00161 } else { 00162 return $this->process_save($pendingstep); 00163 } 00164 } 00165 } 00166 00167 public function summarise_action(question_attempt_step $step) { 00168 if ($step->has_behaviour_var('comment')) { 00169 return $this->summarise_manual_comment($step); 00170 } else if ($step->has_behaviour_var('finish')) { 00171 return $this->summarise_finish($step); 00172 } else if ($step->has_behaviour_var('tryagain')) { 00173 return get_string('tryagain', 'qbehaviour_interactive'); 00174 } else if ($step->has_behaviour_var('submit')) { 00175 return $this->summarise_submit($step); 00176 } else { 00177 return $this->summarise_save($step); 00178 } 00179 } 00180 00181 public function process_try_again(question_attempt_pending_step $pendingstep) { 00182 $pendingstep->set_state(question_state::$todo); 00183 return question_attempt::KEEP; 00184 } 00185 00186 public function process_submit(question_attempt_pending_step $pendingstep) { 00187 if ($this->qa->get_state()->is_finished()) { 00188 return question_attempt::DISCARD; 00189 } 00190 00191 if (!$this->is_complete_response($pendingstep)) { 00192 $pendingstep->set_state(question_state::$invalid); 00193 00194 } else { 00195 $triesleft = $this->qa->get_last_behaviour_var('_triesleft'); 00196 $response = $pendingstep->get_qt_data(); 00197 list($fraction, $state) = $this->question->grade_response($response); 00198 if ($state == question_state::$gradedright || $triesleft == 1) { 00199 $pendingstep->set_state($state); 00200 $pendingstep->set_fraction($this->adjust_fraction($fraction, $pendingstep)); 00201 00202 } else { 00203 $pendingstep->set_behaviour_var('_triesleft', $triesleft - 1); 00204 $pendingstep->set_state(question_state::$todo); 00205 } 00206 $pendingstep->set_new_response_summary($this->question->summarise_response($response)); 00207 } 00208 return question_attempt::KEEP; 00209 } 00210 00211 protected function adjust_fraction($fraction, question_attempt_pending_step $pendingstep) { 00212 $totaltries = $this->qa->get_step(0)->get_behaviour_var('_triesleft'); 00213 $triesleft = $this->qa->get_last_behaviour_var('_triesleft'); 00214 00215 $fraction -= ($totaltries - $triesleft) * $this->question->penalty; 00216 $fraction = max($fraction, 0); 00217 return $fraction; 00218 } 00219 00220 public function process_finish(question_attempt_pending_step $pendingstep) { 00221 if ($this->qa->get_state()->is_finished()) { 00222 return question_attempt::DISCARD; 00223 } 00224 00225 $response = $this->qa->get_last_qt_data(); 00226 if (!$this->question->is_gradable_response($response)) { 00227 $pendingstep->set_state(question_state::$gaveup); 00228 00229 } else { 00230 list($fraction, $state) = $this->question->grade_response($response); 00231 $pendingstep->set_fraction($this->adjust_fraction($fraction, $pendingstep)); 00232 $pendingstep->set_state($state); 00233 } 00234 $pendingstep->set_new_response_summary($this->question->summarise_response($response)); 00235 return question_attempt::KEEP; 00236 } 00237 00238 public function process_save(question_attempt_pending_step $pendingstep) { 00239 $status = parent::process_save($pendingstep); 00240 if ($status == question_attempt::KEEP && 00241 $pendingstep->get_state() == question_state::$complete) { 00242 $pendingstep->set_state(question_state::$todo); 00243 } 00244 return $status; 00245 } 00246 }