From ccccf04f709c8a834a17a1e85447e4b699e7e40a Mon Sep 17 00:00:00 2001 From: gustav_delius Date: Sat, 18 Mar 2006 14:14:55 +0000 Subject: [PATCH] Multianswer restore now works. Timelimit is rounded to integer before saving in the database Some more diagnostic error messages Towards more plugable question types: $QUIZ_MENU is now populated by the question types themselves. --- lib/questionlib.php | 22 ++- mod/quiz/lib.php | 6 +- question/editlib.php | 25 ---- .../questiontypes/calculated/questiontype.php | 3 + .../description/questiontype.php | 3 + question/questiontypes/essay/questiontype.php | 3 + question/questiontypes/match/questiontype.php | 3 + .../multianswer/questiontype.php | 12 +- .../multichoice/questiontype.php | 3 + .../questiontypes/numerical/questiontype.php | 3 + .../questiontypes/random/questiontype.php | 1 + .../randomsamatch/questiontype.php | 3 + question/questiontypes/rqp/questiontype.php | 7 + .../shortanswer/questiontype.php | 3 + .../questiontypes/truefalse/questiontype.php | 3 + question/restorelib.php | 137 +++++++++--------- 16 files changed, 132 insertions(+), 105 deletions(-) diff --git a/lib/questionlib.php b/lib/questionlib.php index d133da568fa..7532245cf74 100644 --- a/lib/questionlib.php +++ b/lib/questionlib.php @@ -33,9 +33,7 @@ define('QUESTION_EVENTSUBMIT', '7'); /**#@-*/ /**#@+ -* The defined question types -* -* @todo It would be nicer to have a fully automatic plug-in system +* The core question types */ define("SHORTANSWER", "1"); define("TRUEFALSE", "2"); @@ -59,14 +57,24 @@ define("QUESTION_NUMANS", "10"); /** * Array holding question type objects */ -$QTYPES= array(); +global $QTYPES; +$QTYPES = array(); // This array will be populated when the questiontype.php files are loaded + +/** +* Array of question types names translated to the user's language +* +* The $QTYPE_MENU array holds the names of all the question types that the user should +* be able to create directly. Some internal question types like random questions are excluded. +* The complete list of question types can be found in {@link $QTYPES}. +*/ +$QTYPE_MENU = array(); // This array will be populated when the questiontype.php files are loaded require_once("$CFG->dirroot/question/questiontypes/questiontype.php"); /* * Load the questiontype.php file for each question type * These files in turn instantiate the corresponding question type class -* and adds it to the $QTYPES array +* and add them to the $QTYPES array */ $qtypenames= get_list_of_plugins('question/questiontypes'); foreach($qtypenames as $qtypename) { @@ -156,7 +164,9 @@ class cmoptions { */ function delete_question($question) { global $QTYPES; - $QTYPES[$question->qtype]->delete_question($question); + if (isset($QTYPES[$question->qtype])) { + $QTYPES[$question->qtype]->delete_question($question); + } else {echo 'qtype: '.$question->qtype.'
';} delete_records("question_answers", "question", $question->id); delete_records("question_states", "question", $question->id); delete_records("question_sessions", "questionid", $question->id); diff --git a/mod/quiz/lib.php b/mod/quiz/lib.php index b4a0c491f6d..f133d8ac55a 100644 --- a/mod/quiz/lib.php +++ b/mod/quiz/lib.php @@ -43,6 +43,8 @@ function quiz_add_instance($quiz) { $quiz->availableminute); } + $quiz->timelimit = round($quiz->timelimit); + if (empty($quiz->name)) { if (empty($quiz->intro)) { $quiz->name = get_string('modulename', 'quiz'); @@ -134,6 +136,8 @@ function quiz_update_instance($quiz) { $quiz->availableminute); } + $quiz->timelimit = round($quiz->timelimit); + $quiz->id = $quiz->instance; if (!update_record("quiz", $quiz)) { @@ -268,7 +272,7 @@ function quiz_delete_instance($id) { */ function quiz_delete_course($course, $feedback=true) { - global $CFG; + global $CFG, $QTYPES; //To detect if we have created the "container category" $concatid = 0; diff --git a/question/editlib.php b/question/editlib.php index c6f7acf4f32..5bf392bc921 100644 --- a/question/editlib.php +++ b/question/editlib.php @@ -15,31 +15,6 @@ require_once($CFG->libdir.'/questionlib.php'); -/** -* Array of question types names translated to the user's language -* -* The $QTYPE_MENU array holds the names of all the question types that the user should -* be able to create directly. Some internal question types like random questions are excluded. -* The complete list of question types can be found in {@link $QTYPES}. -*/ - -$QTYPE_MENU = array ( MULTICHOICE => get_string("multichoice", "quiz"), - TRUEFALSE => get_string("truefalse", "quiz"), - SHORTANSWER => get_string("shortanswer", "quiz"), - NUMERICAL => get_string("numerical", "quiz"), - CALCULATED => get_string("calculated", "quiz"), - MATCH => get_string("match", "quiz"), - DESCRIPTION => get_string("description", "quiz"), - RANDOMSAMATCH => get_string("randomsamatch", "quiz"), - MULTIANSWER => get_string("multianswer", "quiz"), - ESSAY => get_string("essay", "quiz") - ); -// add remote question types -if ($rqp_types = get_records('question_rqp_types')) { - foreach($rqp_types as $type) { - $QTYPE_MENU[100+$type->id] = $type->name; - } -} function question_category_form($course, $current, $recurse=1, $showhidden=false) { global $CFG; diff --git a/question/questiontypes/calculated/questiontype.php b/question/questiontypes/calculated/questiontype.php index a2279cd6320..ca9374c00bb 100644 --- a/question/questiontypes/calculated/questiontype.php +++ b/question/questiontypes/calculated/questiontype.php @@ -598,7 +598,10 @@ class question_calculated_qtype extends question_dataset_dependent_questiontype ////////////////////////////////////////////////////////////////////////// //// INITIATION - Without this line the question type is not in use... /// ////////////////////////////////////////////////////////////////////////// +// define("CALCULATED", "10"); // already defined in questionlib.php $QTYPES[CALCULATED]= new question_calculated_qtype(); +// The following adds the questiontype to the menu of types shown to teachers +$QTYPE_MENU[CALCULATED] = get_string("calculated", "quiz"); function quiz_qtype_calculated_calculate_answer($formula, $individualdata, $tolerance, $tolerancetype, $answerlength, $answerformat='1', $unit='') { diff --git a/question/questiontypes/description/questiontype.php b/question/questiontypes/description/questiontype.php index 5e2bba129ee..049ea3a5848 100644 --- a/question/questiontypes/description/questiontype.php +++ b/question/questiontypes/description/questiontype.php @@ -66,6 +66,9 @@ class quiz_description_qtype extends quiz_default_questiontype { ////////////////////////////////////////////////////////////////////////// //// INITIATION - Without this line the question type is not in use... /// ////////////////////////////////////////////////////////////////////////// +// define("DESCRIPTION", "7"); // already defined in questionlib.php $QTYPES[DESCRIPTION]= new quiz_description_qtype(); +// The following adds the questiontype to the menu of types shown to teachers +$QTYPE_MENU[DESCRIPTION] = get_string("description", "quiz"); ?> diff --git a/question/questiontypes/essay/questiontype.php b/question/questiontypes/essay/questiontype.php index 9e02af48a3e..c772ea1c7e0 100644 --- a/question/questiontypes/essay/questiontype.php +++ b/question/questiontypes/essay/questiontype.php @@ -311,6 +311,9 @@ class question_essay_qtype extends quiz_default_questiontype { ////////////////////////////////////////////////////////////////////////// //// INITIATION - Without this line the question type is not in use... /// ////////////////////////////////////////////////////////////////////////// +// define("ESSAY", "12"); // already defined in questionlib.php $QTYPES[ESSAY] = new question_essay_qtype(); +// The following adds the questiontype to the menu of types shown to teachers +$QTYPE_MENU[ESSAY] = get_string("essay", "quiz"); ?> diff --git a/question/questiontypes/match/questiontype.php b/question/questiontypes/match/questiontype.php index 57a5285c071..91fdd12c036 100644 --- a/question/questiontypes/match/questiontype.php +++ b/question/questiontypes/match/questiontype.php @@ -337,6 +337,9 @@ class question_match_qtype extends quiz_default_questiontype { ////////////////////////////////////////////////////////////////////////// //// INITIATION - Without this line the question type is not in use... /// ////////////////////////////////////////////////////////////////////////// +// define("MATCH", "5"); // already defined in questionlib.php $QTYPES[MATCH]= new question_match_qtype(); +// The following adds the questiontype to the menu of types shown to teachers +$QTYPE_MENU[MATCH] = get_string("match", "quiz"); ?> diff --git a/question/questiontypes/multianswer/questiontype.php b/question/questiontypes/multianswer/questiontype.php index 8afc0249eb5..e8c8460323a 100644 --- a/question/questiontypes/multianswer/questiontype.php +++ b/question/questiontypes/multianswer/questiontype.php @@ -24,7 +24,7 @@ class quiz_embedded_cloze_qtype extends quiz_default_questiontype { // Get relevant data indexed by positionkey from the multianswers table if (!$sequence = get_field('question_multianswer', 'sequence', 'question', $question->id)) { - notify('Error: Missing question options!'); + notify('Error: Cloze question '.$question->id.' is missing question options!'); return false; } @@ -92,13 +92,13 @@ class quiz_embedded_cloze_qtype extends quiz_default_questiontype { get_field('question_multianswer', 'id', 'question', $question->id)) { $multianswer->id = $oldid; if (!update_record("question_multianswer", $multianswer)) { - $result->error = "Could not update quiz multianswer! " . + $result->error = "Could not update cloze question options! " . "(id=$multianswer->id)"; return $result; } } else { if (!insert_record("question_multianswer", $multianswer)) { - $result->error = "Could not insert quiz multianswer!"; + $result->error = "Could not insert cloze question options!"; return $result; } } @@ -322,7 +322,7 @@ class quiz_embedded_cloze_qtype extends quiz_default_questiontype { echo ''; break; default: - error("Unable to recognized questiontype ($wrapped->qtype) of + error("Unable to recognize questiontype ($wrapped->qtype) of question part $positionkey."); break; } @@ -380,8 +380,10 @@ class quiz_embedded_cloze_qtype extends quiz_default_questiontype { ////////////////////////////////////////////////////////////////////////// //// INITIATION - Without this line the question type is not in use... /// ////////////////////////////////////////////////////////////////////////// +// define("MULTIANSWER", "9"); // already defined in questionlib.php $QTYPES[MULTIANSWER]= new quiz_embedded_cloze_qtype(); - +// The following adds the questiontype to the menu of types shown to teachers +$QTYPE_MENU[MULTIANSWER] = get_string("multianswer", "quiz"); ///////////////////////////////////////////////////////////// //// ADDITIONAL FUNCTIONS diff --git a/question/questiontypes/multichoice/questiontype.php b/question/questiontypes/multichoice/questiontype.php index fa4ad22d8ed..5f14db95a1e 100644 --- a/question/questiontypes/multichoice/questiontype.php +++ b/question/questiontypes/multichoice/questiontype.php @@ -372,6 +372,9 @@ class question_multichoice_qtype extends quiz_default_questiontype { ////////////////////////////////////////////////////////////////////////// //// INITIATION - Without this line the question type is not in use... /// ////////////////////////////////////////////////////////////////////////// +// define("MULTICHOICE", "3"); // already defined in questionlib.php $QTYPES[MULTICHOICE]= new question_multichoice_qtype(); +// The following adds the questiontype to the menu of types shown to teachers +$QTYPE_MENU[MULTICHOICE] = get_string("multichoice", "quiz"); ?> diff --git a/question/questiontypes/numerical/questiontype.php b/question/questiontypes/numerical/questiontype.php index a5a53c16009..a3919f7739f 100644 --- a/question/questiontypes/numerical/questiontype.php +++ b/question/questiontypes/numerical/questiontype.php @@ -409,6 +409,9 @@ class question_numerical_qtype extends question_shortanswer_qtype { ////////////////////////////////////////////////////////////////////////// //// INITIATION - Without this line the question type is not in use... /// ////////////////////////////////////////////////////////////////////////// +// define("NUMERICAL", "8"); // already defined in questionlib.php $QTYPES[NUMERICAL]= new question_numerical_qtype(); +// The following adds the questiontype to the menu of types shown to teachers +$QTYPE_MENU[NUMERICAL] = get_string("numerical", "quiz"); ?> diff --git a/question/questiontypes/random/questiontype.php b/question/questiontypes/random/questiontype.php index d413aa2cecd..697a6c43155 100644 --- a/question/questiontypes/random/questiontype.php +++ b/question/questiontypes/random/questiontype.php @@ -237,6 +237,7 @@ class quiz_random_qtype extends quiz_default_questiontype { ////////////////////////////////////////////////////////////////////////// //// INITIATION - Without this line the question type is not in use... /// ////////////////////////////////////////////////////////////////////////// +// define("RANDOM", "4"); // already defined in questionlib.php $QTYPES[RANDOM]= new quiz_random_qtype(); ?> diff --git a/question/questiontypes/randomsamatch/questiontype.php b/question/questiontypes/randomsamatch/questiontype.php index af53bf07f7e..f73e729817d 100644 --- a/question/questiontypes/randomsamatch/questiontype.php +++ b/question/questiontypes/randomsamatch/questiontype.php @@ -248,6 +248,9 @@ class question_randomsamatch_qtype extends question_match_qtype { ////////////////////////////////////////////////////////////////////////// //// INITIATION - Without this line the question type is not in use... /// ////////////////////////////////////////////////////////////////////////// +// define("RANDOMSAMATCH", "6"); // already defined in questionlib.php $QTYPES[RANDOMSAMATCH]= new question_randomsamatch_qtype(); +// The following adds the questiontype to the menu of types shown to teachers +$QTYPE_MENU[RANDOMSAMATCH] = get_string("randomsamatch", "quiz"); ?> diff --git a/question/questiontypes/rqp/questiontype.php b/question/questiontypes/rqp/questiontype.php index 95e42e3c95d..119e58c552e 100644 --- a/question/questiontypes/rqp/questiontype.php +++ b/question/questiontypes/rqp/questiontype.php @@ -437,6 +437,13 @@ class question_rqp_qtype extends quiz_default_questiontype { ////////////////////////////////////////////////////////////////////////// //// INITIATION - Without this line the question type is not in use... /// ////////////////////////////////////////////////////////////////////////// +// define("RQP", "11"); // already defined in questionlib.php $QTYPES[RQP]= new question_rqp_qtype(); +// The following adds the questiontype to the menu of types shown to teachers +if ($rqp_types = get_records('question_rqp_types')) { + foreach($rqp_types as $type) { + $QTYPE_MENU[100+$type->id] = $type->name; + } +} ?> diff --git a/question/questiontypes/shortanswer/questiontype.php b/question/questiontypes/shortanswer/questiontype.php index aa488cf9fba..f96300559e1 100644 --- a/question/questiontypes/shortanswer/questiontype.php +++ b/question/questiontypes/shortanswer/questiontype.php @@ -246,6 +246,9 @@ class question_shortanswer_qtype extends quiz_default_questiontype { ////////////////////////////////////////////////////////////////////////// //// INITIATION - Without this line the question type is not in use... /// ////////////////////////////////////////////////////////////////////////// +// define("SHORTANSWER", "1"); // already defined in questionlib.php $QTYPES[SHORTANSWER]= new question_shortanswer_qtype(); +// The following adds the questiontype to the menu of types shown to teachers +$QTYPE_MENU[SHORTANSWER] = get_string("shortanswer", "quiz"); ?> diff --git a/question/questiontypes/truefalse/questiontype.php b/question/questiontypes/truefalse/questiontype.php index 461136e7472..5b9076cb9db 100644 --- a/question/questiontypes/truefalse/questiontype.php +++ b/question/questiontypes/truefalse/questiontype.php @@ -211,6 +211,9 @@ class question_truefalse_qtype extends quiz_default_questiontype { ////////////////////////////////////////////////////////////////////////// //// INITIATION - Without this line the question type is not in use... /// ////////////////////////////////////////////////////////////////////////// +// define("TRUEFALSE", "2"); // already defined in questionlib.php $QTYPES[TRUEFALSE]= new question_truefalse_qtype(); +// The following adds the questiontype to the menu of types shown to teachers +$QTYPE_MENU[TRUEFALSE] = get_string("truefalse", "quiz"); ?> diff --git a/question/restorelib.php b/question/restorelib.php index 1c453f89b4f..6862779a1fb 100644 --- a/question/restorelib.php +++ b/question/restorelib.php @@ -82,37 +82,6 @@ //----------------------------------------------------------- include_once($CFG->libdir.'/questionlib.php'); - // load questiontype-specific functions - unset($restorefns); - unset($restoremapfns); - unset($restorestatefns); - unset($recodeansfns); - //if ($qtypes = get_records('question_types')) { - if ($qtypes = get_list_of_plugins('question/questiontypes')) { - foreach ($qtypes as $name) { - $qtype->name = $name; - $restorelib = $CFG->dirroot.'/question/questiontypes/'.$qtype->name.'/restorelib.php'; - if (file_exists($restorelib)) { - include_once($restorelib); - $restorefn = 'question_'.$qtype->name.'_restore'; - if (function_exists($restorefn)) { - $restorefns[$qtype->name] = $restorefn; - } - $restoremapfn = 'question_'.$qtype->name.'_restore_map'; - if (function_exists($restoremapfn)) { - $restoremapfns[$qtype->name] = $restoremapfn; - } - $restorestatefn = 'question_'.$qtype->name.'_states_restore'; - if (function_exists($restorestatefn)) { - $restorestatefns[$qtype->name] = $restorestatefn; - } - $recodeansfn = 'question_'.$qtype->name.'_recode_answer'; - if (function_exists($recodeansfn)) { - $recodeansfns[$qtype->name] = $recodeansfn; - } - } - } - } function restore_question_categories($category,$restore) { @@ -188,8 +157,30 @@ function restore_questions ($old_category_id,$new_category_id,$info,$restore) { - global $CFG; + global $CFG, $QTYPES; + // load questiontype-specific functions + unset($restorefns); + unset($restoremapfns); + unset($recodeansfns); + foreach ($QTYPES as $key => $qtype) { + $restorelib = $CFG->dirroot.'/question/questiontypes/'.$qtype->name().'/restorelib.php'; + if (file_exists($restorelib)) { + include_once($restorelib); + $restorefn = 'question_'.$qtype->name().'_restore'; + if (function_exists($restorefn)) { + $restorefns[$key] = $restorefn; + }else {echo $restorefn;} + $restoremapfn = 'question_'.$qtype->name().'_restore_map'; + if (function_exists($restoremapfn)) { + $restoremapfns[$key] = $restoremapfn; + } + $recodeansfn = 'question_'.$qtype->name().'_recode_answer'; + if (function_exists($recodeansfn)) { + $recodeansfns[$key] = $recodeansfn; + } + } + } $status = true; $restored_questions = array(); @@ -273,8 +264,8 @@ //Now, restore every question_answers in this question $status = question_restore_answers($oldid,$newid,$que_info,$restore); //Now, depending of the type of questions, invoke different functions - if (isset($restorefns[$question->type])) { - $status = $restorefns[$question->type]->restore($oldid,$newid,$que_info,$restore); + if (isset($restorefns[$question->qtype])) { + $status = $restorefns[$question->qtype]($oldid,$newid,$que_info,$restore); } } else { //We are NOT creating the question, but we need to know every question_answers @@ -284,8 +275,8 @@ //Now, depending of the type of questions, invoke different functions //to create the necessary mappings in backup_ids, because we are not //creating the question, but need some records in backup table - if (isset($restoremapfns[$question->type])) { - $status = $restoremapfns[$question->type]->restore($oldid,$newid,$que_info,$restore); + if (isset($restoremapfns[$question->qtype])) { + $status = $restoremapfns[$question->qtype]($oldid,$newid,$que_info,$restore); } } @@ -615,10 +606,12 @@ //We have to recode the answer field //It depends of the question type !! //We get the question first - $question = get_record("question","id",$state->question); + if (!$question = get_record("question","id",$state->question)) { + error("Can't find the record for question $state->question for which I am trying to restore a state"); + } //It exists if ($question) { - //Depending of the qtype, we make different recodes + //Depending on the qtype, we make different recodes switch ($question->qtype) { case 1: //SHORTANSWER QTYPE //Nothing to do. The response is a text. @@ -728,41 +721,38 @@ //Nothing to do. The response is a text. break; case 9: //MULTIANSWER QTYPE - //The answer is a comma separated list of hypen separated multianswer_id and answers. We must recode them. + //The answer is a comma separated list of hypen separated sequence number and answers. We may have to recode the answers $answer_field = ""; $in_first = true; $tok = strtok($state->answer,","); while ($tok) { //Extract the multianswer_id and the answer $exploded = explode("-",$tok); - $multianswer_id = $exploded[0]; + $seqnum = $exploded[0]; $answer = $exploded[1]; - //Get the multianswer from backup_ids - $mul = backup_getid($restore->backup_unique_code,"question_multianswer",$multianswer_id); - if ($mul) { - //Now, depending of the answertype field in question_multianswer - //we do diferent things - $mul_db = get_record ("question_multianswer","id",$mul->new_id); - if ($mul_db->answertype == "1") { - //Shortanswer - //The answer is text, do nothing - } else if ($mul_db->answertype == "3") { - //Multichoice - //The answer is an answer_id, look for it in backup_ids - $ans = backup_getid($restore->backup_unique_code,"question_answers",$answer); - $answer = $ans->new_id; - } else if ($mul_db->answertype == "8") { - //Numeric - //The answer is text, do nothing - } - - //Finaly, build the new answer field for each pair - if ($in_first) { - $answer_field .= $mul->new_id."-".$answer; - $in_first = false; - } else { - $answer_field .= ",".$mul->new_id."-".$answer; - } + // $sequence is an ordered array of the question ids. + if (!$sequence = get_field('question_multianswer', 'sequence', 'question', $question->id)) { + error("The cloze question $question->id is missing its options"); + } + $sequence = explode(',', $sequence); + // The id of the current question. + $wrappedquestionid = $sequence[$seqnum-1]; + // now we can find the question + if (!$wrappedquestion = get_record('question', 'id', $wrappedquestionid)) { + error("Can't find the subquestion $wrappedquestionid that is used as part $seqnum in cloze question $question->id"); + } + // For multichoice question we need to recode the answer + if ($wrappedquestion->qtype == MULTICHOICE) { + //The answer is an answer_id, look for it in backup_ids + $ans = backup_getid($restore->backup_unique_code,"question_answers",$answer); + $answer = $ans->new_id; + } + //build the new answer field for each pair + if ($in_first) { + $answer_field .= $seqnum."-".$answer; + $in_first = false; + } else { + $answer_field .= ",".$seqnum."-".$answer; } //check for next $tok = strtok(","); @@ -800,8 +790,19 @@ backup_putid($restore->backup_unique_code,"question_states",$oldid, $newid); //Now process question type specific state information - foreach ($restorestatefns as $restorestatefn) { - $restorestatefn($newid,$res_info,$restore); + + if ($qtypes = get_list_of_plugins('question/questiontypes')) { + foreach ($qtypes as $name) { + $qtype->name = $name; + $restorelib = $CFG->dirroot.'/question/questiontypes/'.$qtype->name.'/restorelib.php'; + if (file_exists($restorelib)) { + include_once($restorelib); + $restorestatefn = 'question_'.$qtype->name.'_states_restore'; + if (function_exists($restorestatefn)) { + $restorestatefn($newid,$res_info,$restore); + } + } + } } } else { $status = false;