From b9bd6da4f8c6b85eff0e28839ebefe15b1fbbfea Mon Sep 17 00:00:00 2001 From: nicolasconnault Date: Thu, 12 Jun 2008 09:15:16 +0000 Subject: [PATCH] CONTRIB-490 Added support for scripted generation of questions. Calculated type not yet supported (datasets too complicated ;) --- lib/questionlib.php | 29 ++++++++++------------ question/type/essay/questiontype.php | 20 +++++++++++++++ question/type/match/questiontype.php | 19 ++++++++++++++ question/type/multianswer/questiontype.php | 28 +++++++++++++++++++++ question/type/multichoice/questiontype.php | 29 ++++++++++++++++++++++ question/type/numerical/questiontype.php | 28 +++++++++++++++++++++ question/type/questiontype.php | 24 ++++++++++++++++++ question/type/shortanswer/questiontype.php | 29 ++++++++++++++++++++++ question/type/truefalse/questiontype.php | 22 ++++++++++++++++ 9 files changed, 212 insertions(+), 16 deletions(-) diff --git a/lib/questionlib.php b/lib/questionlib.php index bba7790722..8914e68067 100644 --- a/lib/questionlib.php +++ b/lib/questionlib.php @@ -169,7 +169,7 @@ foreach($qtypenames as $qtypename) { * Long-time Moodle programmers will realise that this replaces the old $QTYPE_MENU array. * The array returned will only hold 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. - * + * * @return array an array of question type names translated to the user's language. */ function question_type_menu() { @@ -571,7 +571,7 @@ function question_delete_course_category($category, $newcategory, $feedback=true // Loop over question categories. if ($categories = $DB->get_records('question_categories', array('contextid'=>$context->id), 'parent', 'id, parent, name')) { foreach ($categories as $category) { - + // Deal with any questions in the category. if ($questions = $DB->get_records('question', array('category'=>$category->id))) { @@ -635,9 +635,9 @@ function question_delete_course_category($category, $newcategory, $feedback=true * * @param string $questionids list of questionids * @param object $newcontext the context to create the saved category in. - * @param string $oldplace a textual description of the think being deleted, e.g. from get_context_name + * @param string $oldplace a textual description of the think being deleted, e.g. from get_context_name * @param object $newcategory - * @return mixed false on + * @return mixed false on */ function question_save_from_deletion($questionids, $newcontextid, $oldplace, $newcategory = null) { global $DB; @@ -757,8 +757,8 @@ function questionbank_navigation_tabs(&$row, $contexts, $querystring) { /** * Load a set of questions, given a list of ids. The $join and $extrafields arguments can be used - * together to pull in extra data. See, for example, the usage in mod/quiz/attempt.php, and - * read the code below to see how the SQL is assembled. + * together to pull in extra data. See, for example, the usage in mod/quiz/attempt.php, and + * read the code below to see how the SQL is assembled. * * @param string $questionlist list of comma-separated question ids. * @param string $extrafields @@ -769,7 +769,7 @@ function questionbank_navigation_tabs(&$row, $contexts, $querystring) { function question_load_questions($questionlist, $extrafields = '', $join = '') { global $CFG, $DB; if ($join) { - $join = ' JOIN {'.$join.'}'; + $join = ' JOIN '.$join.''; } if ($extrafields) { $extrafields = ', ' . $extrafields; @@ -1027,8 +1027,7 @@ function save_question_session(&$question, &$state) { } // create or update the session - if (!$session = $DB->get_record('question_sessions', 'attemptid', - $state->attempt, 'questionid', $question->id)) { + if (!$session = $DB->get_record('question_sessions', array('attemptid' => $state->attempt, 'questionid' => $question->id))) { $session->attemptid = $state->attempt; $session->questionid = $question->id; $session->newest = $state->id; @@ -1264,7 +1263,7 @@ function regrade_question_in_attempt($question, $attempt, $cmoptions, $verbose=f } if ($action->event == QUESTION_EVENTMANUALGRADE) { - // Ensure that the grade is in range - in the past this was not checked, + // Ensure that the grade is in range - in the past this was not checked, // but now it is (MDL-14835) - so we need to ensure the data is valid before // proceeding. if ($states[$j]->grade < 0) { @@ -1934,12 +1933,7 @@ function question_make_default_categories($contexts) { $toreturn = null; // If it already exists, just return it. foreach ($contexts as $key => $context) { - $exists = $DB->record_exists("question_categories", array('contextid'=>$context->id)); - - if ($exists === false) { - print_error('cannotgetcats'); - } - if (!$exists){ + if (!$exists = $DB->record_exists("question_categories", array('contextid'=>$context->id))){ // Otherwise, we need to make one $category = new stdClass; $contextname = print_context_name($context, false, true); @@ -1952,7 +1946,10 @@ function question_make_default_categories($contexts) { if (!$category->id = $DB->insert_record('question_categories', $category)) { print_error('cannotcreatedefaultcat', '', '', print_context_name($context)); } + } else { + $category = $DB->get_record('question_categories', array('contextid' => $context->id)); } + if ($context->contextlevel == CONTEXT_COURSE){ $toreturn = clone($category); } diff --git a/question/type/essay/questiontype.php b/question/type/essay/questiontype.php index f3ea4d5d12..3b10ab2685 100644 --- a/question/type/essay/questiontype.php +++ b/question/type/essay/questiontype.php @@ -134,6 +134,26 @@ class question_essay_qtype extends default_questiontype { return question_backup_answers($bf, $preferences, $questionid, $level); } + /** + * Runs all the code required to set up and save an essay question for testing purposes. + * Alternate DB table prefix may be used to facilitate data deletion. + */ + function generate_test($name, $courseid = null) { + global $DB; + list($form, $question) = parent::generate_test($name, $courseid); + $form->questiontext = "What is the purpose of life?"; + $form->feedback = "feedback"; + $form->generalfeedback = "General feedback"; + $form->fraction = 0; + $form->penalty = 0; + + if ($courseid) { + $course = $DB->get_record('course', array('id' => $courseid)); + } + + return $this->save_question($question, $form, $course); + } + // Restore method not needed. } //// END OF CLASS //// diff --git a/question/type/match/questiontype.php b/question/type/match/questiontype.php index 76757e9ca6..9eb5893696 100644 --- a/question/type/match/questiontype.php +++ b/question/type/match/questiontype.php @@ -704,6 +704,25 @@ class question_match_qtype extends default_questiontype { } } } + + /** + * Runs all the code required to set up and save an essay question for testing purposes. + * Alternate DB table prefix may be used to facilitate data deletion. + */ + function generate_test($name, $courseid = null) { + global $DB; + list($form, $question) = parent::generate_test($name, $courseid); + $form->shuffleanswers = 1; + $form->noanswers = 3; + $form->subquestions = array('cat', 'dog', 'cow'); + $form->subanswers = array('feline', 'canine', 'bovine'); + + if ($courseid) { + $course = $DB->get_record('course', array('id' => $courseid)); + } + + return $this->save_question($question, $form, $course); + } } //// END OF CLASS //// diff --git a/question/type/multianswer/questiontype.php b/question/type/multianswer/questiontype.php index c2befa6054..a860943566 100644 --- a/question/type/multianswer/questiontype.php +++ b/question/type/multianswer/questiontype.php @@ -707,6 +707,34 @@ class embedded_cloze_qtype extends default_questiontype { return $answer_field; } + /** + * Runs all the code required to set up and save an essay question for testing purposes. + * Alternate DB table prefix may be used to facilitate data deletion. + */ + function generate_test($name, $courseid = null) { + global $DB; + list($form, $question) = parent::generate_test($name, $courseid); + $question->category = $form->category; + $form->questiontext = "This question consists of some text with an answer embedded right here {1:MULTICHOICE:Wrong answer#Feedback for this wrong answer~Another wrong answer#Feedback for the other wrong answer~=Correct answer#Feedback for correct answer~%50%Answer that gives half the credit#Feedback for half credit answer} and right after that you will have to deal with this short answer {1:SHORTANSWER:Wrong answer#Feedback for this wrong answer~=Correct answer#Feedback for correct answer~%50%Answer that gives half the credit#Feedback for half credit answer} and finally we have a floating point number {2:NUMERICAL:=23.8:0.1#Feedback for correct answer 23.8~%50%23.8:2#Feedback for half credit answer in the nearby region of the correct answer}. + +Note that addresses like www.moodle.org and smileys :-) all work as normal: + a) How good is this? {:MULTICHOICE:=Yes#Correct~No#We have a different opinion} + b) What grade would you give it? {3:NUMERICAL:=3:2} + +Good luck! +"; + $form->feedback = "feedback"; + $form->generalfeedback = "General feedback"; + $form->fraction = 0; + $form->penalty = 0.1; + $form->versioning = 0; + + if ($courseid) { + $course = $DB->get_record('course', array('id' => $courseid)); + } + + return $this->save_question($question, $form, $course); + } } //// END OF CLASS //// diff --git a/question/type/multichoice/questiontype.php b/question/type/multichoice/questiontype.php index c4104fee70..f5da87a84a 100644 --- a/question/type/multichoice/questiontype.php +++ b/question/type/multichoice/questiontype.php @@ -676,6 +676,35 @@ class question_multichoice_qtype extends default_questiontype { } } } + + /** + * Runs all the code required to set up and save an essay question for testing purposes. + * Alternate DB table prefix may be used to facilitate data deletion. + */ + function generate_test($name, $courseid = null) { + global $DB; + list($form, $question) = parent::generate_test($name, $courseid); + $question->category = $form->category; + $form->questiontext = "How old is the sun?"; + $form->generalfeedback = "General feedback"; + $form->penalty = 0.1; + $form->single = 1; + $form->shuffleanswers = 1; + $form->answernumbering = 'abc'; + $form->noanswers = 3; + $form->answer = array('Ancient', '5 billion years old', '4.5 billion years old'); + $form->fraction = array(0.3, 0.9, 1); + $form->feedback = array('True, but lacking in accuracy', 'Close, but no cigar!', 'Yep, that is it!'); + $form->correctfeedback = 'Excellent!'; + $form->incorrectfeedback = 'Nope!'; + $form->partiallycorrectfeedback = 'Not bad'; + + if ($courseid) { + $course = $DB->get_record('course', array('id' => $courseid)); + } + + return $this->save_question($question, $form, $course); + } } // Register this question type with the question bank. diff --git a/question/type/numerical/questiontype.php b/question/type/numerical/questiontype.php index 56a1c066d3..cc3cf8407d 100644 --- a/question/type/numerical/questiontype.php +++ b/question/type/numerical/questiontype.php @@ -488,6 +488,34 @@ class question_numerical_qtype extends question_shortanswer_qtype { return $status; } + /** + * Runs all the code required to set up and save an essay question for testing purposes. + * Alternate DB table prefix may be used to facilitate data deletion. + */ + function generate_test($name, $courseid = null) { + global $DB; + list($form, $question) = default_questiontype::generate_test($name, $courseid); + $question->category = $form->category; + + $form->questiontext = "What is 674 * 36?"; + $form->generalfeedback = "Thank you"; + $form->penalty = 0.1; + $form->defaultgrade = 1; + $form->noanswers = 3; + $form->answer = array('24264', '24264', '1'); + $form->tolerance = array(10, 100, 0); + $form->fraction = array(1, 0.5, 0); + $form->nounits = 2; + $form->unit = array(0 => null, 1 => null); + $form->multiplier = array(1, 0); + $form->feedback = array('Very good', 'Close, but not quite there', 'Well at least you tried....'); + + if ($courseid) { + $course = $DB->get_record('course', array('id' => $courseid)); + } + + return $this->save_question($question, $form, $course); + } } // INITIATION - Without this line the question type is not in use. diff --git a/question/type/questiontype.php b/question/type/questiontype.php index 2c0821271e..7824debf67 100644 --- a/question/type/questiontype.php +++ b/question/type/questiontype.php @@ -1582,5 +1582,29 @@ class default_questiontype { // There is nothing to decode return $state->answer; } + + /** + * Abstract function implemented by each question type. It runs all the code + * required to set up and save a question of any type for testing purposes. + * Alternate DB table prefix may be used to facilitate data deletion. + */ + function generate_test($name, $courseid=null) { + $form = new stdClass(); + $form->name = $name; + $form->questiontextformat = 1; + $form->questiontext = 'test question, generated by script'; + $form->defaultgrade = 1; + $form->penalty = 0.1; + $form->generalfeedback = "Well done"; + + $context = get_context_instance(CONTEXT_COURSE, $courseid); + $newcategory = question_make_default_categories(array($context)); + $form->category = $newcategory->id . ',1'; + + $question = new stdClass(); + $question->courseid = $courseid; + $question->qtype = $this->qtype; + return array($form, $question); + } } ?> diff --git a/question/type/shortanswer/questiontype.php b/question/type/shortanswer/questiontype.php index d5066688d9..c6e0904be4 100644 --- a/question/type/shortanswer/questiontype.php +++ b/question/type/shortanswer/questiontype.php @@ -445,6 +445,35 @@ class question_shortanswer_qtype extends default_questiontype { } } } + + /** + * Runs all the code required to set up and save an essay question for testing purposes. + * Alternate DB table prefix may be used to facilitate data deletion. + */ + function generate_test($name, $courseid = null) { + global $DB; + list($form, $question) = parent::generate_test($name, $courseid); + $question->category = $form->category; + + $form->questiontext = "What is the purpose of life, the universe, and everything"; + $form->generalfeedback = "Congratulations, you may have solved my biggest problem!"; + $form->penalty = 0.1; + $form->usecase = false; + $form->defaultgrade = 1; + $form->noanswers = 3; + $form->answer = array('42', 'who cares?', 'Be happy'); + $form->fraction = array(1, 0.6, 0.8); + $form->feedback = array('True, but what does that mean?', 'Well you do, dont you?', 'Yes, but thats not funny...'); + $form->correctfeedback = 'Excellent!'; + $form->incorrectfeedback = 'Nope!'; + $form->partiallycorrectfeedback = 'Not bad'; + + if ($courseid) { + $course = $DB->get_record('course', array('id' => $courseid)); + } + + return $this->save_question($question, $form, $course); + } } //// END OF CLASS //// diff --git a/question/type/truefalse/questiontype.php b/question/type/truefalse/questiontype.php index ef01281dab..5f3c00eb5f 100644 --- a/question/type/truefalse/questiontype.php +++ b/question/type/truefalse/questiontype.php @@ -350,6 +350,28 @@ class question_truefalse_qtype extends default_questiontype { } } + /** + * Runs all the code required to set up and save an essay question for testing purposes. + * Alternate DB table prefix may be used to facilitate data deletion. + */ + function generate_test($name, $courseid = null) { + global $DB; + list($form, $question) = parent::generate_test($name, $courseid); + $question->category = $form->category; + + $form->questiontext = "This question is really stupid"; + $form->penalty = 1; + $form->defaultgrade = 1; + $form->correctanswer = 0; + $form->feedbacktrue = 'Can you justify such a hasty judgment?'; + $form->feedbackfalse = 'Wisdom has spoken!'; + + if ($courseid) { + $course = $DB->get_record('course', array('id' => $courseid)); + } + + return $this->save_question($question, $form, $course); + } } //// END OF CLASS //// -- 2.39.5