Moodle  2.2.1
http://www.collinsharper.com
C:/xampp/htdocs/moodle/question/type/randomsamatch/questiontype.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 
00039 class qtype_randomsamatch extends question_type {
00040     const MAX_SUBQUESTIONS = 10;
00041 
00042     public function requires_qtypes() {
00043         return array('shortanswer', 'match');
00044     }
00045 
00046     public function is_usable_by_random() {
00047         return false;
00048     }
00049 
00050     public function get_question_options($question) {
00051         global $DB;
00052         $question->options = $DB->get_record('question_randomsamatch',
00053                 array('question' => $question->id), '*', MUST_EXIST);
00054 
00055         // This could be included as a flag in the database. It's already
00056         // supported by the code.
00057         // Recurse subcategories: 0 = no recursion, 1 = recursion
00058         $question->options->subcats = 1;
00059         return true;
00060 
00061     }
00062 
00063     public function save_question_options($question) {
00064         global $DB;
00065         $options = new stdClass();
00066         $options->question = $question->id;
00067         $options->choose = $question->choose;
00068 
00069         if (2 > $question->choose) {
00070             $result = new stdClass();
00071             $result->error = "At least two shortanswer questions need to be chosen!";
00072             return $result;
00073         }
00074 
00075         if ($existing = $DB->get_record('question_randomsamatch',
00076                 array('question' => $options->question))) {
00077             $options->id = $existing->id;
00078             $DB->update_record('question_randomsamatch', $options);
00079         } else {
00080             $DB->insert_record('question_randomsamatch', $options);
00081         }
00082         return true;
00083     }
00084 
00085     public function delete_question($questionid, $contextid) {
00086         global $DB;
00087         $DB->delete_records('question_randomsamatch', array('question' => $questionid));
00088 
00089         parent::delete_question($questionid, $contextid);
00090     }
00091 
00092     public function create_session_and_responses(&$question, &$state, $cmoptions, $attempt) {
00093         // Choose a random shortanswer question from the category:
00094         // We need to make sure that no question is used more than once in the
00095         // quiz. Therfore the following need to be excluded:
00096         // 1. All questions that are explicitly assigned to the quiz
00097         // 2. All random questions
00098         // 3. All questions that are already chosen by an other random question
00099         global $QTYPES, $OUTPUT, $USER;
00100         if (!isset($cmoptions->questionsinuse)) {
00101             $cmoptions->questionsinuse = $cmoptions->questions;
00102         }
00103 
00104         if ($question->options->subcats) {
00105             // recurse into subcategories
00106             $categorylist = question_categorylist($question->category);
00107         } else {
00108             $categorylist = array($question->category);
00109         }
00110 
00111         $saquestions = $this->get_sa_candidates($categorylist, $cmoptions->questionsinuse);
00112 
00113         $count  = count($saquestions);
00114         $wanted = $question->options->choose;
00115 
00116         if ($count < $wanted) {
00117             $question->questiontext = "Insufficient selection options are
00118                 available for this question, therefore it is not available in  this
00119                 quiz. Please inform your teacher.";
00120             // Treat this as a description from this point on
00121             $question->qtype = 'description';
00122             return true;
00123         }
00124 
00125         $saquestions =
00126          draw_rand_array($saquestions, $question->options->choose); // from bug 1889
00127 
00128         foreach ($saquestions as $key => $wrappedquestion) {
00129             if (!$QTYPES[$wrappedquestion->qtype]
00130              ->get_question_options($wrappedquestion)) {
00131                 return false;
00132             }
00133 
00134             // Now we overwrite the $question->options->answers field to only
00135             // *one* (the first) correct answer. This loop can be deleted to
00136             // take all answers into account (i.e. put them all into the
00137             // drop-down menu.
00138             $foundcorrect = false;
00139             foreach ($wrappedquestion->options->answers as $answer) {
00140                 if ($foundcorrect || $answer->fraction != 1.0) {
00141                     unset($wrappedquestion->options->answers[$answer->id]);
00142                 } else if (!$foundcorrect) {
00143                     $foundcorrect = true;
00144                 }
00145             }
00146 
00147             if (!$QTYPES[$wrappedquestion->qtype]
00148              ->create_session_and_responses($wrappedquestion, $state, $cmoptions,
00149              $attempt)) {
00150                 return false;
00151             }
00152             $wrappedquestion->name_prefix = $question->name_prefix;
00153             $wrappedquestion->maxgrade    = $question->maxgrade;
00154             $cmoptions->questionsinuse .= ",$wrappedquestion->id";
00155             $state->options->subquestions[$key] = clone($wrappedquestion);
00156         }
00157 
00158         // Shuffle the answers (Do this always because this is a random question type)
00159         $subquestionids = array_values(array_map(create_function('$val',
00160          'return $val->id;'), $state->options->subquestions));
00161         $subquestionids = swapshuffle($subquestionids);
00162 
00163         // Create empty responses
00164         foreach ($subquestionids as $val) {
00165             $state->responses[$val] = '';
00166         }
00167         return true;
00168     }
00169 
00170     function restore_session_and_responses(&$question, &$state) {
00171         global $DB;
00172         global $QTYPES, $OUTPUT;
00173         static $wrappedquestions = array();
00174         if (empty($state->responses[''])) {
00175             $question->questiontext = "Insufficient selection options are
00176              available for this question, therefore it is not available in  this
00177              quiz. Please inform your teacher.";
00178             // Treat this as a description from this point on
00179             $question->qtype = 'description';
00180         } else {
00181             $responses = explode(',', $state->responses['']);
00182             $responses = array_map(create_function('$val',
00183              'return explode("-", $val);'), $responses);
00184 
00185             // Restore the previous responses
00186             $state->responses = array();
00187             foreach ($responses as $response) {
00188                 $wqid = $response[0];
00189                 $state->responses[$wqid] = $response[1];
00190                 if (!isset($wrappedquestions[$wqid])) {
00191                     if (!$wrappedquestions[$wqid] = $DB->get_record('question', array('id' => $wqid))) {
00192                         echo $OUTPUT->notification("Couldn't get question (id=$wqid)!");
00193                         return false;
00194                     }
00195                     if (!$QTYPES[$wrappedquestions[$wqid]->qtype]
00196                      ->get_question_options($wrappedquestions[$wqid])) {
00197                         echo $OUTPUT->notification("Couldn't get question options (id=$response[0])!");
00198                         return false;
00199                     }
00200 
00201                     // Now we overwrite the $question->options->answers field to only
00202                     // *one* (the first) correct answer. This loop can be deleted to
00203                     // take all answers into account (i.e. put them all into the
00204                     // drop-down menu.
00205                     $foundcorrect = false;
00206                     foreach ($wrappedquestions[$wqid]->options->answers as $answer) {
00207                         if ($foundcorrect || $answer->fraction != 1.0) {
00208                             unset($wrappedquestions[$wqid]->options->answers[$answer->id]);
00209                         } else if (!$foundcorrect) {
00210                             $foundcorrect = true;
00211                         }
00212                     }
00213                 }
00214                 $wrappedquestion = clone($wrappedquestions[$wqid]);
00215 
00216                 if (!$QTYPES[$wrappedquestion->qtype]
00217                  ->restore_session_and_responses($wrappedquestion, $state)) {
00218                     echo $OUTPUT->notification("Couldn't restore session of question (id=$response[0])!");
00219                     return false;
00220                 }
00221                 $wrappedquestion->name_prefix = $question->name_prefix;
00222                 $wrappedquestion->maxgrade    = $question->maxgrade;
00223 
00224                 $state->options->subquestions[$wrappedquestion->id] =
00225                  clone($wrappedquestion);
00226             }
00227         }
00228         return true;
00229     }
00230 
00231     public function get_sa_candidates($categorylist, $questionsinuse = 0) {
00232         global $DB;
00233         list ($usql, $params) = $DB->get_in_or_equal($categorylist);
00234         list ($ques_usql, $ques_params) = $DB->get_in_or_equal(explode(',', $questionsinuse),
00235                 SQL_PARAMS_QM, null, false);
00236         $params = array_merge($params, $ques_params);
00237         return $DB->get_records_select('question',
00238          "qtype = 'shortanswer' " .
00239          "AND category $usql " .
00240          "AND parent = '0' " .
00241          "AND hidden = '0'" .
00242          "AND id $ques_usql", $params);
00243     }
00244 
00251     public function get_random_guess_score($question) {
00252         return 1/$question->options->choose;
00253     }
00254 }
 All Data Structures Namespaces Files Functions Variables Enumerations