Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/question/engine/questionusage.php
Go to the documentation of this file.
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 
00027 defined('MOODLE_INTERNAL') || die();
00028 
00029 
00052 class question_usage_by_activity {
00058     protected $id = null;
00059 
00064     protected $preferredbehaviour = null;
00065 
00067     protected $context;
00068 
00070     protected $owningcomponent;
00071 
00073     protected $questionattempts = array();
00074 
00076     protected $observer;
00077 
00087     public function __construct($component, $context) {
00088         $this->owningcomponent = $component;
00089         $this->context = $context;
00090         $this->observer = new question_usage_null_observer();
00091     }
00092 
00097     public function set_preferred_behaviour($behaviour) {
00098         $this->preferredbehaviour = $behaviour;
00099         $this->observer->notify_modified();
00100     }
00101 
00103     public function get_preferred_behaviour() {
00104         return $this->preferredbehaviour;
00105     }
00106 
00108     public function get_owning_context() {
00109         return $this->context;
00110     }
00111 
00113     public function get_owning_component() {
00114         return $this->owningcomponent;
00115     }
00116 
00120     public function get_id() {
00121         if (is_null($this->id)) {
00122             $this->id = random_string(10);
00123         }
00124         return $this->id;
00125     }
00126 
00132     public function set_id_from_database($id) {
00133         $this->id = $id;
00134         foreach ($this->questionattempts as $qa) {
00135             $qa->set_usage_id($id);
00136         }
00137     }
00138 
00140     public function get_observer() {
00141         return $this->observer;
00142     }
00143 
00149     public function set_observer($observer) {
00150         $this->observer = $observer;
00151         foreach ($this->questionattempts as $qa) {
00152             $qa->set_observer($observer);
00153         }
00154     }
00155 
00167     public function add_question(question_definition $question, $maxmark = null) {
00168         $qa = new question_attempt($question, $this->get_id(), $this->observer, $maxmark);
00169         if (count($this->questionattempts) == 0) {
00170             $this->questionattempts[1] = $qa;
00171         } else {
00172             $this->questionattempts[] = $qa;
00173         }
00174         $qa->set_slot(end(array_keys($this->questionattempts)));
00175         $this->observer->notify_attempt_added($qa);
00176         return $qa->get_slot();
00177     }
00178 
00184     public function get_question($slot) {
00185         return $this->get_question_attempt($slot)->get_question();
00186     }
00187 
00189     public function get_slots() {
00190         return array_keys($this->questionattempts);
00191     }
00192 
00194     public function get_first_question_number() {
00195         reset($this->questionattempts);
00196         return key($this->questionattempts);
00197     }
00198 
00200     public function question_count() {
00201         return count($this->questionattempts);
00202     }
00203 
00212     public function get_attempt_iterator() {
00213         return new question_attempt_iterator($this);
00214     }
00215 
00222     protected function check_slot($slot) {
00223         if (!array_key_exists($slot, $this->questionattempts)) {
00224             throw new coding_exception('There is no question_attempt number ' . $slot .
00225                     ' in this attempt.');
00226         }
00227     }
00228 
00237     public function get_question_attempt($slot) {
00238         $this->check_slot($slot);
00239         return $this->questionattempts[$slot];
00240     }
00241 
00247     public function get_question_state($slot) {
00248         return $this->get_question_attempt($slot)->get_state();
00249     }
00250 
00257     public function get_question_state_string($slot, $showcorrectness) {
00258         return $this->get_question_attempt($slot)->get_state_string($showcorrectness);
00259     }
00260 
00267     public function get_question_state_class($slot, $showcorrectness) {
00268         return $this->get_question_attempt($slot)->get_state_class($showcorrectness);
00269     }
00270 
00276     public function get_question_action_time($slot) {
00277         return $this->get_question_attempt($slot)->get_last_action_time();
00278     }
00279 
00286     public function get_question_fraction($slot) {
00287         return $this->get_question_attempt($slot)->get_fraction();
00288     }
00289 
00296     public function get_question_mark($slot) {
00297         return $this->get_question_attempt($slot)->get_mark();
00298     }
00299 
00305     public function get_question_max_mark($slot) {
00306         return $this->get_question_attempt($slot)->get_max_mark();
00307     }
00308 
00315     public function get_total_mark() {
00316         $mark = 0;
00317         foreach ($this->questionattempts as $qa) {
00318             if ($qa->get_max_mark() > 0 && $qa->get_state() == question_state::$needsgrading) {
00319                 return null;
00320             }
00321             $mark += $qa->get_mark();
00322         }
00323         return $mark;
00324     }
00325 
00329     public function get_question_summary($slot) {
00330         return $this->get_question_attempt($slot)->get_question_summary();
00331     }
00332 
00336     public function get_response_summary($slot) {
00337         return $this->get_question_attempt($slot)->get_response_summary();
00338     }
00339 
00343     public function get_right_answer_summary($slot) {
00344         return $this->get_question_attempt($slot)->get_right_answer_summary();
00345     }
00346 
00357     public function render_question($slot, $options, $number = null) {
00358         $options->context = $this->context;
00359         return $this->get_question_attempt($slot)->render($options, $number);
00360     }
00361 
00368     public function render_question_head_html($slot) {
00369         //$options->context = $this->context;
00370         return $this->get_question_attempt($slot)->render_head_html();
00371     }
00372 
00384     public function render_question_at_step($slot, $seq, $options, $number = null) {
00385         $options->context = $this->context;
00386         return $this->get_question_attempt($slot)->render_at_step(
00387                 $seq, $options, $number, $this->preferredbehaviour);
00388     }
00389 
00400     public function check_file_access($slot, $options, $component, $filearea,
00401             $args, $forcedownload) {
00402         return $this->get_question_attempt($slot)->check_file_access(
00403                 $options, $component, $filearea, $args, $forcedownload);
00404     }
00405 
00416     public function replace_loaded_question_attempt_info($slot, $qa) {
00417         $this->check_slot($slot);
00418         $this->questionattempts[$slot] = $qa;
00419     }
00420 
00428     public function get_field_prefix($slot) {
00429         return $this->get_question_attempt($slot)->get_field_prefix();
00430     }
00431 
00437     public function get_num_variants($slot) {
00438         return $this->get_question_attempt($slot)->get_question()->get_num_variants();
00439     }
00440 
00446     public function get_variant($slot) {
00447         return $this->get_question_attempt($slot)->get_variant();
00448     }
00449 
00457     public function start_question($slot, $variant = null) {
00458         if (is_null($variant)) {
00459             $variant = rand(1, $this->get_num_variants($slot));
00460         }
00461 
00462         $qa = $this->get_question_attempt($slot);
00463         $qa->start($this->preferredbehaviour, $variant);
00464         $this->observer->notify_attempt_modified($qa);
00465     }
00466 
00473     public function start_all_questions(question_variant_selection_strategy $variantstrategy = null,
00474             $timestamp = null, $userid = null) {
00475         if (is_null($variantstrategy)) {
00476             $variantstrategy = new question_variant_random_strategy();
00477         }
00478 
00479         foreach ($this->questionattempts as $qa) {
00480             $qa->start($this->preferredbehaviour, $qa->select_variant($variantstrategy));
00481             $this->observer->notify_attempt_modified($qa);
00482         }
00483     }
00484 
00493     public function start_question_based_on($slot, question_attempt $oldqa) {
00494         $qa = $this->get_question_attempt($slot);
00495         $qa->start_based_on($oldqa);
00496         $this->observer->notify_attempt_modified($qa);
00497     }
00498 
00512     public function process_all_actions($timestamp = null, $postdata = null) {
00513         $slots = question_attempt::get_submitted_var('slots', PARAM_SEQUENCE, $postdata);
00514         if (is_null($slots)) {
00515             $slots = $this->get_slots();
00516         } else if (!$slots) {
00517             $slots = array();
00518         } else {
00519             $slots = explode(',', $slots);
00520         }
00521         foreach ($slots as $slot) {
00522             if (!$this->validate_sequence_number($slot, $postdata)) {
00523                 continue;
00524             }
00525             $submitteddata = $this->extract_responses($slot, $postdata);
00526             $this->process_action($slot, $submitteddata, $timestamp);
00527         }
00528         $this->update_question_flags($postdata);
00529     }
00530 
00540     public function extract_responses($slot, $postdata = null) {
00541         return $this->get_question_attempt($slot)->get_submitted_data($postdata);
00542     }
00543 
00549     public function process_action($slot, $submitteddata, $timestamp = null) {
00550         $qa = $this->get_question_attempt($slot);
00551         $qa->process_action($submitteddata, $timestamp);
00552         $this->observer->notify_attempt_modified($qa);
00553     }
00554 
00565     public function validate_sequence_number($slot, $postdata = null) {
00566         $qa = $this->get_question_attempt($slot);
00567         $sequencecheck = $qa->get_submitted_var(
00568                 $qa->get_control_field_name('sequencecheck'), PARAM_INT, $postdata);
00569         if (is_null($sequencecheck)) {
00570             return false;
00571         } else if ($sequencecheck != $qa->get_num_steps()) {
00572             throw new question_out_of_sequence_exception($this->id, $slot, $postdata);
00573         } else {
00574             return true;
00575         }
00576     }
00584     public function update_question_flags($postdata = null) {
00585         foreach ($this->questionattempts as $qa) {
00586             $flagged = $qa->get_submitted_var(
00587                     $qa->get_flag_field_name(), PARAM_BOOL, $postdata);
00588             if (!is_null($flagged) && $flagged != $qa->is_flagged()) {
00589                 $qa->set_flagged($flagged);
00590             }
00591         }
00592     }
00593 
00601     public function get_correct_response($slot) {
00602         return $this->get_question_attempt($slot)->get_correct_response();
00603     }
00604 
00618     public function finish_question($slot, $timestamp = null) {
00619         $qa = $this->get_question_attempt($slot);
00620         $qa->finish($timestamp);
00621         $this->observer->notify_attempt_modified($qa);
00622     }
00623 
00628     public function finish_all_questions($timestamp = null) {
00629         foreach ($this->questionattempts as $qa) {
00630             $qa->finish($timestamp);
00631             $this->observer->notify_attempt_modified($qa);
00632         }
00633     }
00634 
00642     public function manual_grade($slot, $comment, $mark) {
00643         $qa = $this->get_question_attempt($slot);
00644         $qa->manual_grade($comment, $mark);
00645         $this->observer->notify_attempt_modified($qa);
00646     }
00647 
00656     public function regrade_question($slot, $finished = false, $newmaxmark = null) {
00657         $oldqa = $this->get_question_attempt($slot);
00658         if (is_null($newmaxmark)) {
00659             $newmaxmark = $oldqa->get_max_mark();
00660         }
00661 
00662         $newqa = new question_attempt($oldqa->get_question(), $oldqa->get_usage_id(),
00663                 $this->observer, $newmaxmark);
00664         $newqa->set_database_id($oldqa->get_database_id());
00665         $newqa->set_slot($oldqa->get_slot());
00666         $newqa->regrade($oldqa, $finished);
00667 
00668         $this->questionattempts[$slot] = $newqa;
00669         $this->observer->notify_attempt_modified($newqa);
00670     }
00671 
00677     public function regrade_all_questions($finished = false) {
00678         foreach ($this->questionattempts as $slot => $notused) {
00679             $this->regrade_question($slot, $finished);
00680         }
00681     }
00682 
00692     public static function load_from_records($records, $qubaid) {
00693         $record = $records->current();
00694         while ($record->qubaid != $qubaid) {
00695             $records->next();
00696             if (!$records->valid()) {
00697                 throw new coding_exception("Question usage $qubaid not found in the database.");
00698             }
00699             $record = $records->current();
00700         }
00701 
00702         $quba = new question_usage_by_activity($record->component,
00703             get_context_instance_by_id($record->contextid));
00704         $quba->set_id_from_database($record->qubaid);
00705         $quba->set_preferred_behaviour($record->preferredbehaviour);
00706 
00707         $quba->observer = new question_engine_unit_of_work($quba);
00708 
00709         while ($record && $record->qubaid == $qubaid && !is_null($record->slot)) {
00710             $quba->questionattempts[$record->slot] =
00711                     question_attempt::load_from_records($records,
00712                     $record->questionattemptid, $quba->observer,
00713                     $quba->get_preferred_behaviour());
00714             if ($records->valid()) {
00715                 $record = $records->current();
00716             } else {
00717                 $record = false;
00718             }
00719         }
00720 
00721         return $quba;
00722     }
00723 }
00724 
00725 
00740 class question_attempt_iterator implements Iterator, ArrayAccess {
00742     protected $quba;
00744     protected $slots;
00745 
00751     public function __construct(question_usage_by_activity $quba) {
00752         $this->quba = $quba;
00753         $this->slots = $quba->get_slots();
00754         $this->rewind();
00755     }
00756 
00758     public function current() {
00759         return $this->offsetGet(current($this->slots));
00760     }
00762     public function key() {
00763         return current($this->slots);
00764     }
00765     public function next() {
00766         next($this->slots);
00767     }
00768     public function rewind() {
00769         reset($this->slots);
00770     }
00772     public function valid() {
00773         return current($this->slots) !== false;
00774     }
00775 
00777     public function offsetExists($slot) {
00778         return in_array($slot, $this->slots);
00779     }
00781     public function offsetGet($slot) {
00782         return $this->quba->get_question_attempt($slot);
00783     }
00784     public function offsetSet($slot, $value) {
00785         throw new coding_exception('You are only allowed read-only access to ' .
00786                 'question_attempt::states through a question_attempt_step_iterator. Cannot set.');
00787     }
00788     public function offsetUnset($slot) {
00789         throw new coding_exception('You are only allowed read-only access to ' .
00790                 'question_attempt::states through a question_attempt_step_iterator. Cannot unset.');
00791     }
00792 }
00793 
00794 
00805 interface question_usage_observer {
00807     public function notify_modified();
00808 
00813     public function notify_attempt_modified(question_attempt $qa);
00814 
00819     public function notify_attempt_added(question_attempt $qa);
00820 
00827     public function notify_step_added(question_attempt_step $step, question_attempt $qa, $seq);
00828 
00835     public function notify_step_modified(question_attempt_step $step, question_attempt $qa, $seq);
00836 
00842     public function notify_step_deleted(question_attempt_step $step, question_attempt $qa);
00843 
00844 }
00845 
00846 
00854 class question_usage_null_observer implements question_usage_observer {
00855     public function notify_modified() {
00856     }
00857     public function notify_attempt_modified(question_attempt $qa) {
00858     }
00859     public function notify_attempt_added(question_attempt $qa) {
00860     }
00861     public function notify_step_added(question_attempt_step $step, question_attempt $qa, $seq) {
00862     }
00863     public function notify_step_modified(question_attempt_step $step, question_attempt $qa, $seq) {
00864     }
00865     public function notify_step_deleted(question_attempt_step $step, question_attempt $qa) {
00866     }
00867 }
 All Data Structures Namespaces Files Functions Variables Enumerations