From 38d42fc4762fab3c51bce1ac952c7a46f49116a4 Mon Sep 17 00:00:00 2001 From: Tim Hunt Date: Mon, 9 May 2011 18:58:49 +0100 Subject: [PATCH] MDL-27408 question engine upgrade, change to update the quiz settings in config_plugins. Also start creating the unit tests for the upgrade. --- local/qedatabase/db/install.php | 56 +- .../engine/upgrade/behaviourconverters.php | 15 +- question/engine/upgrade/simpletest/helper.php | 114 ++++ .../db/simpletest/testupgradelibnewqe.php | 583 ++++++++++++++++++ 4 files changed, 739 insertions(+), 29 deletions(-) create mode 100644 question/engine/upgrade/simpletest/helper.php create mode 100644 question/type/truefalse/db/simpletest/testupgradelibnewqe.php diff --git a/local/qedatabase/db/install.php b/local/qedatabase/db/install.php index dc67fb2f5b53e..76ba795845974 100755 --- a/local/qedatabase/db/install.php +++ b/local/qedatabase/db/install.php @@ -4,13 +4,6 @@ function xmldb_local_qedatabase_install() { global $DB; $dbman = $DB->get_manager(); - // TODO quiz default settings are now in config_plugins. - - // Bit of a hack to prevent errors like "Cannot downgrade local_qedatabase from ... to ...". - $oldversion = 2008000000; - $DB->set_field('config_plugins', 'value', $oldversion, - array('plugin' => 'local_qedatabase', 'name' => 'version')); - // Add new preferredbehaviour column to the quiz table. if ($oldversion < 2008000100) { $table = new xmldb_table('quiz'); @@ -33,8 +26,8 @@ function xmldb_local_qedatabase_install() { $DB->set_field_select('quiz', 'preferredbehaviour', 'adaptivenopenalty', 'optionflags <> 0 AND penaltyscheme = 0'); - set_config('quiz_preferredbehaviour', 'deferredfeedback'); - set_config('quiz_fix_preferredbehaviour', 0); + set_config('preferredbehaviour', 'deferredfeedback', 'quiz'); + set_config('fix_preferredbehaviour', 0, 'quiz'); // quiz savepoint reached upgrade_plugin_savepoint(true, 2008000101, 'local', 'qedatabase'); @@ -58,8 +51,8 @@ function xmldb_local_qedatabase_install() { $field = new xmldb_field('optionflags'); $dbman->drop_field($table, $field); - unset_config('quiz_optionflags'); - unset_config('quiz_fix_optionflags'); + unset_config('optionflags', 'quiz'); + unset_config('fix_optionflags', 'quiz'); // quiz savepoint reached upgrade_plugin_savepoint(true, 2008000103, 'local', 'qedatabase'); @@ -71,8 +64,8 @@ function xmldb_local_qedatabase_install() { $field = new xmldb_field('penaltyscheme'); $dbman->drop_field($table, $field); - unset_config('quiz_penaltyscheme'); - unset_config('quiz_fix_penaltyscheme'); + unset_config('penaltyscheme', 'quiz'); + unset_config('fix_penaltyscheme', 'quiz'); // quiz savepoint reached upgrade_plugin_savepoint(true, 2008000104, 'local', 'qedatabase'); @@ -355,47 +348,54 @@ function xmldb_local_qedatabase_install() { $CFG->quiz_review = 0; } - set_config('quiz_reviewattempt', + set_config('reviewattempt', QUIZ_NEW_DURING | ($CFG->quiz_review & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_RESPONSES ? QUIZ_NEW_IMMEDIATELY_AFTER : 0) | ($CFG->quiz_review & QUIZ_OLD_OPEN & QUIZ_OLD_RESPONSES ? QUIZ_NEW_LATER_WHILE_OPEN : 0) | - ($CFG->quiz_review & QUIZ_OLD_CLOSED & QUIZ_OLD_RESPONSES ? QUIZ_NEW_AFTER_CLOSE : 0)); + ($CFG->quiz_review & QUIZ_OLD_CLOSED & QUIZ_OLD_RESPONSES ? QUIZ_NEW_AFTER_CLOSE : 0), + 'quiz'); - set_config('quiz_reviewcorrectness', + set_config('reviewcorrectness', QUIZ_NEW_DURING | ($CFG->quiz_review & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_SCORES ? QUIZ_NEW_IMMEDIATELY_AFTER : 0) | ($CFG->quiz_review & QUIZ_OLD_OPEN & QUIZ_OLD_SCORES ? QUIZ_NEW_LATER_WHILE_OPEN : 0) | - ($CFG->quiz_review & QUIZ_OLD_CLOSED & QUIZ_OLD_SCORES ? QUIZ_NEW_AFTER_CLOSE : 0)); + ($CFG->quiz_review & QUIZ_OLD_CLOSED & QUIZ_OLD_SCORES ? QUIZ_NEW_AFTER_CLOSE : 0), + 'quiz'); - set_config('quiz_reviewmarks', + set_config('reviewmarks', QUIZ_NEW_DURING | ($CFG->quiz_review & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_SCORES ? QUIZ_NEW_IMMEDIATELY_AFTER : 0) | ($CFG->quiz_review & QUIZ_OLD_OPEN & QUIZ_OLD_SCORES ? QUIZ_NEW_LATER_WHILE_OPEN : 0) | - ($CFG->quiz_review & QUIZ_OLD_CLOSED & QUIZ_OLD_SCORES ? QUIZ_NEW_AFTER_CLOSE : 0)); + ($CFG->quiz_review & QUIZ_OLD_CLOSED & QUIZ_OLD_SCORES ? QUIZ_NEW_AFTER_CLOSE : 0), + 'quiz'); - set_config('quiz_reviewspecificfeedback', + set_config('reviewspecificfeedback', ($CFG->quiz_review & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_FEEDBACK ? QUIZ_NEW_DURING : 0) | ($CFG->quiz_review & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_FEEDBACK ? QUIZ_NEW_IMMEDIATELY_AFTER : 0) | ($CFG->quiz_review & QUIZ_OLD_OPEN & QUIZ_OLD_FEEDBACK ? QUIZ_NEW_LATER_WHILE_OPEN : 0) | - ($CFG->quiz_review & QUIZ_OLD_CLOSED & QUIZ_OLD_FEEDBACK ? QUIZ_NEW_AFTER_CLOSE : 0)); + ($CFG->quiz_review & QUIZ_OLD_CLOSED & QUIZ_OLD_FEEDBACK ? QUIZ_NEW_AFTER_CLOSE : 0), + 'quiz'); - set_config('quiz_reviewgeneralfeedback', + set_config('reviewgeneralfeedback', ($CFG->quiz_review & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_GENERALFEEDBACK ? QUIZ_NEW_DURING : 0) | ($CFG->quiz_review & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_GENERALFEEDBACK ? QUIZ_NEW_IMMEDIATELY_AFTER : 0) | ($CFG->quiz_review & QUIZ_OLD_OPEN & QUIZ_OLD_GENERALFEEDBACK ? QUIZ_NEW_LATER_WHILE_OPEN : 0) | - ($CFG->quiz_review & QUIZ_OLD_CLOSED & QUIZ_OLD_GENERALFEEDBACK ? QUIZ_NEW_AFTER_CLOSE : 0)); + ($CFG->quiz_review & QUIZ_OLD_CLOSED & QUIZ_OLD_GENERALFEEDBACK ? QUIZ_NEW_AFTER_CLOSE : 0), + 'quiz'); - set_config('quiz_reviewrightanswer', + set_config('reviewrightanswer', ($CFG->quiz_review & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_ANSWERS ? QUIZ_NEW_DURING : 0) | ($CFG->quiz_review & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_ANSWERS ? QUIZ_NEW_IMMEDIATELY_AFTER : 0) | ($CFG->quiz_review & QUIZ_OLD_OPEN & QUIZ_OLD_ANSWERS ? QUIZ_NEW_LATER_WHILE_OPEN : 0) | - ($CFG->quiz_review & QUIZ_OLD_CLOSED & QUIZ_OLD_ANSWERS ? QUIZ_NEW_AFTER_CLOSE : 0)); + ($CFG->quiz_review & QUIZ_OLD_CLOSED & QUIZ_OLD_ANSWERS ? QUIZ_NEW_AFTER_CLOSE : 0), + 'quiz'); - set_config('quiz_reviewoverallfeedback', + set_config('reviewoverallfeedback', 0 | ($CFG->quiz_review & QUIZ_OLD_IMMEDIATELY & QUIZ_OLD_OVERALLFEEDBACK ? QUIZ_NEW_IMMEDIATELY_AFTER : 0) | ($CFG->quiz_review & QUIZ_OLD_OPEN & QUIZ_OLD_OVERALLFEEDBACK ? QUIZ_NEW_LATER_WHILE_OPEN : 0) | - ($CFG->quiz_review & QUIZ_OLD_CLOSED & QUIZ_OLD_OVERALLFEEDBACK ? QUIZ_NEW_AFTER_CLOSE : 0)); + ($CFG->quiz_review & QUIZ_OLD_CLOSED & QUIZ_OLD_OVERALLFEEDBACK ? QUIZ_NEW_AFTER_CLOSE : 0), + 'quiz'); // quiz savepoint reached upgrade_plugin_savepoint(true, 2008000217, 'local', 'qedatabase'); @@ -415,7 +415,7 @@ function xmldb_local_qedatabase_install() { } if ($oldversion < 2008000221) { - unset_config('quiz_review'); + unset_config('review', 'quiz'); // quiz savepoint reached upgrade_plugin_savepoint(true, 2008000221, 'local', 'qedatabase'); diff --git a/question/engine/upgrade/behaviourconverters.php b/question/engine/upgrade/behaviourconverters.php index db052561b9895..1b6dbdae7f251 100644 --- a/question/engine/upgrade/behaviourconverters.php +++ b/question/engine/upgrade/behaviourconverters.php @@ -261,7 +261,20 @@ protected function process10($step, $state) { * @return qtype_updater */ protected function make_qtype_updater() { - $class = 'qtype_' . $this->question->qtype . '_updater'; + global $CFG; + $path = $CFG->dirroot . '/question/type/' . $this->question->qtype . '/db/upgradelib.php'; + if (!is_readable($path)) { + throw new coding_exception("Question type {$this->question->qtype} + is missing important code (the file {$path}) + required to run the upgrade to the new question engine."); + } + include_once($path); + $class = 'qtype_' . $this->question->qtype . '_qe2_attempt_updater'; + if (!class_exists($class)) { + throw new coding_exception("Question type {$this->question->qtype} + is missing important code (the class {$class}) + required to run the upgrade to the new question engine."); + } return new $class($this, $this->question, $this->logger); } diff --git a/question/engine/upgrade/simpletest/helper.php b/question/engine/upgrade/simpletest/helper.php new file mode 100644 index 0000000000000..e452c8b8f1a31 --- /dev/null +++ b/question/engine/upgrade/simpletest/helper.php @@ -0,0 +1,114 @@ +. + +/** + * This file contains test helper code for testing the upgrade to the new + * question engine. The acutal tests are organised by question type in files + * like question/type/truefalse/db/simpletest/testupgradelibnewqe.php. + * + * @package moodlecore + * @subpackage questionengine + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once(dirname(__FILE__) . '/../upgradelib.php'); + + +/** + * Subclass of question_engine_attempt_upgrader to help with testing. + * + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class test_question_engine_attempt_upgrader extends question_engine_attempt_upgrader { + public function prevent_timeout() { + } + + public function __construct($loader, $logger) { + $this->questionloader = $loader; + $this->logger = $logger; + } +} + + +/** + * Subclass of question_engine_assumption_logger that does nothing, for testing. + * + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class dummy_question_engine_assumption_logger extends question_engine_assumption_logger { + protected $attemptid; + + public function __construct() { + } + + public function log_assumption($description, $quizattemptid = null) { + } + + public function __destruct() { + } +} + + +/** + * Subclass of question_engine_upgrade_question_loader for unit testing. + * + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class test_question_engine_upgrade_question_loader extends question_engine_upgrade_question_loader { + public function put_question_in_cache($question) { + $this->cache[$question->id] = $question; + } + + public function load_question($questionid, $quizid) { + global $CFG, $QTYPES; + + if (isset($this->cache[$questionid])) { + return $this->cache[$questionid]; + } + + return null; + } +} + + +/** + * Base class for tests that thest the upgrade of one particular attempt and + * one question. + * + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class question_attempt_upgrader_test_base extends UnitTestCase { + protected $updater; + protected $loader; + + public function setUp() { + $logger = new dummy_question_engine_assumption_logger(); + $this->loader = new test_question_engine_upgrade_question_loader($logger); + $this->updater = new test_question_engine_attempt_upgrader($this->loader, $logger); + } + + public function tearDown() { + $this->updater = null; + } +} diff --git a/question/type/truefalse/db/simpletest/testupgradelibnewqe.php b/question/type/truefalse/db/simpletest/testupgradelibnewqe.php new file mode 100644 index 0000000000000..8a54a76090855 --- /dev/null +++ b/question/type/truefalse/db/simpletest/testupgradelibnewqe.php @@ -0,0 +1,583 @@ +. + +/** + * Tests of the upgrade to the new Moodle question engine for attempts at + * truefalse questions. + * + * @package qtype + * @subpackage truefalse + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +defined('MOODLE_INTERNAL') || die(); + +require_once($CFG->dirroot . '/question/engine/upgrade/simpletest/helper.php'); + + +/** + * Subclass of question_engine_attempt_upgrader to help with testing. + * + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ +class qtype_truefalse_attempt_upgrader_test extends question_attempt_upgrader_test_base { + + public function test_truefalse_deferredfeedback_history620() { + $quiz = (object) array( + 'id' => '203', + 'course' => '2359', + 'name' => 'Quiz 1', + 'intro' => '', + 'introformat' => FORMAT_HTML, + 'timeopen' => '0', + 'timeclose' => '0', + 'preferredbehaviour' => 'deferredfeedback', + 'attempts' => '0', + 'attemptonlast' => '1', + 'grademethod' => '1', + 'decimalpoints' => '2', + 'review' => '71760879', + 'questionsperpage' => '2', + 'questiondecimalpoints' => '-1', + 'showuserpicture' => '1', + 'showblocks' => '1', + 'shufflequestions' => '0', + 'shuffleanswers' => '0', + 'questions' => '3859,3860,0,3861,3862,0,3863,3864,0,3865,3866,0,3867,3868,0', + 'sumgrades' => '50', + 'grade' => '50', + 'timecreated' => '0', + 'timemodified' => '1176461532', + 'password' => '', + 'subnet' => '', + 'popup' => '0', + 'delay1' => '0', + 'delay2' => '0', + 'timelimit' => '0', + ); + $attempt = (object) array( + 'id' => '3795', + 'uniqueid' => '3795', + 'quiz' => '203', + 'userid' => '1888', + 'attempt' => '1', + 'sumgrades' => '40', + 'timestart' => '1177841172', + 'timefinish' => '1177841409', + 'timemodified' => '1177841394', + 'layout' => '3859,3860,0,3861,3862,0,3863,3864,0,3865,3866,0,3867,3868,0', + 'preview' => '0', + 'needsupgradetonewqe' => '1', + ); + $question = (object) array( + 'id' => '3865', + 'category' => '187', + 'parent' => '0', + 'name' => 'Question 7', + 'questiontext' => '

The term ‘integration server’ is another name for an application server, true or false?

', + 'questiontextformat' => '1', + 'defaultmark' => '1', + 'penalty' => '0', + 'qtype' => 'truefalse', + 'length' => '1', + 'stamp' => 'learn.open.ac.uk+070404143040+oLimmG', + 'version' => 'learn.open.ac.uk+070405112705+DLhORU', + 'hidden' => '0', + 'generalfeedback' => '

', + 'generalfeedbackformat' => '1', + 'timecreated' => '0', + 'timemodified' => '0', + 'createdby' => NULL, + 'modifiedby' => NULL, + 'unlimited' => NULL, + 'maxmark' => '5', + 'options' => (object) array( + 'id' => '98', + 'question' => '3865', + 'trueanswer' => '11693', + 'falseanswer' => '11694', + 'answers' => array( + 11693 => (object) array( + 'question' => '3865', + 'answer' => 'True', + 'fraction' => '0', + 'feedback' => '', + 'id' => 11693, + ), + 11694 => (object) array( + 'question' => '3865', + 'answer' => 'False', + 'fraction' => '1', + 'feedback' => '', + 'id' => 11694, + ), + ), + ), + 'hints' => false, + ); + $qsession = (object) array( + 'id' => '35137', + 'attemptid' => '3795', + 'questionid' => '3865', + 'newest' => '84791', + 'newgraded' => '84791', + 'sumpenalty' => '0', + 'manualcomment' => '', + 'manualcommentformat' => '1', + 'flagged' => '1', + ); + $qstates = array( + 84771 => (object) array( + 'attempt' => '3795', + 'question' => '3865', + 'originalquestion' => '0', + 'seq_number' => '0', + 'answer' => '', + 'timestamp' => '1177841172', + 'event' => '0', + 'grade' => '0', + 'raw_grade' => '0', + 'penalty' => '0', + 'id' => 84771, + ), + 84785 => (object) array( + 'attempt' => '3795', + 'question' => '3865', + 'originalquestion' => '0', + 'seq_number' => '1', + 'answer' => '11694', + 'timestamp' => '1177841361', + 'event' => '2', + 'grade' => '0', + 'raw_grade' => '5', + 'penalty' => '5', + 'id' => 84785, + ), + 84791 => (object) array( + 'attempt' => '3795', + 'question' => '3865', + 'originalquestion' => '0', + 'seq_number' => '2', + 'answer' => '11694', + 'timestamp' => '1177841361', + 'event' => '6', + 'grade' => '5', + 'raw_grade' => '5', + 'penalty' => '5', + 'id' => 84791, + ), + ); + + $qa = $this->updater->convert_question_attempt($quiz, $attempt, $question, $qsession, $qstates); + + $expectedqa = (object) array( + 'behaviour' => 'deferredfeedback', + 'questionid' => 3865, + 'maxmark' => 5, + 'minfraction' => 0, + 'flagged' => 0, + 'questionsummary' => 'The term ‘integration server’ is another name for an application server, true or false?', + 'rightanswer' => 'False', + 'responsesummary' => 'False', + 'timemodified' => 1177841361, + 'steps' => array( + 0 => (object) array( + 'sequencenumber' => 0, + 'state' => 'todo', + 'fraction' => null, + 'timecreated' => 1177841172, + 'userid' => 1888, + 'data' => array(), + ), + 1 => (object) array( + 'sequencenumber' => 1, + 'state' => 'complete', + 'fraction' => null, + 'timecreated' => 1177841361, + 'userid' => 1888, + 'data' => array('answer' => 0), + ), + 2 => (object) array( + 'sequencenumber' => 2, + 'state' => 'gradedright', + 'fraction' => 1, + 'timecreated' => 1177841361, + 'userid' => 1888, + 'data' => array('answer' => 0, '-finish' => 1), + ), + ), + ); + + $this->assertEqual($expectedqa, $qa); + } + + public function test_truefalse_deferredfeedback_history20() { + $quiz = (object) array( + 'id' => '551', + 'course' => '2828', + 'name' => 'Unit 4 Quiz', + 'intro' => '', + 'introformat' => FORMAT_HTML, + 'questiondecimalpoints' => '-1', + 'showuserpicture' => '1', + 'showblocks' => '1', + 'timeopen' => '0', + 'timeclose' => '0', + 'preferredbehaviour' => 'deferredfeedback', + 'attempts' => '0', + 'attemptonlast' => '0', + 'grademethod' => '1', + 'decimalpoints' => '2', + 'review' => '71760879', + 'questionsperpage' => '1', + 'shufflequestions' => '0', + 'shuffleanswers' => '1', + 'questions' => '9043,0,9057,0,9062,0,9241,0', + 'sumgrades' => '4', + 'grade' => '4', + 'timecreated' => '0', + 'timemodified' => '1190277883', + 'password' => '', + 'subnet' => '', + 'popup' => '0', + 'delay1' => '0', + 'delay2' => '0', + 'timelimit' => '0', + ); + $attempt = (object) array( + 'id' => '23226', + 'uniqueid' => '23226', + 'quiz' => '551', + 'userid' => '80300', + 'attempt' => '2', + 'sumgrades' => '0', + 'timestart' => '1200326384', + 'timefinish' => '0', + 'timemodified' => '1200326384', + 'layout' => '9043,0,9057,0,9062,0,9241,0', + 'preview' => '0', + 'needsupgradetonewqe' => '1', + ); + $question = (object) array( + 'id' => '9062', + 'category' => '481', + 'parent' => '0', + 'name' => 'U04_NA_In viaggio_Q3', + 'questiontext' => '

my market

+

What can you buy in this shop? Is this list accurate?

+


Mark true or false (for the list as a whole).

+ + +

single tickets

+

weekly season tickets

+

monthly season tickets

+

wine and grappa

+

fruit and vegetables

+

tobacco

', + 'questiontextformat' => '1', + 'defaultmark' => '1', + 'penalty' => '0', + 'qtype' => 'truefalse', + 'length' => '1', + 'stamp' => 'learn.open.ac.uk+070820163735+PqUlDM', + 'version' => 'learn.open.ac.uk+080304160318+owhQUb', + 'hidden' => '0', + 'generalfeedback' => '', + 'generalfeedbackformat' => '1', + 'timecreated' => '0', + 'timemodified' => '1204646598', + 'createdby' => NULL, + 'modifiedby' => '97230', + 'unlimited' => '0', + 'maxmark' => '1', + 'options' => (object) array( + 'id' => '199', + 'question' => '9062', + 'trueanswer' => '28221', + 'falseanswer' => '28222', + 'answers' => array( + 28221 => (object) array( + 'question' => '9062', + 'answer' => 'True', + 'fraction' => '0', + 'feedback' => '

The correct answer is \'false\'. The only items on the list not sold at My Market are tobacco and weekly season tickets. It sells everything else!
Biglietto unico is a single ticket for bus, funicular railway or metro (underground) while abbonamento mensile is a monthly season ticket.
The shop also sells wine and grappa, as you can see them in the window, and fruit and vegetables as it says in the sign. Small shops in Naples often sell a variety of things, not always connected!

', + 'id' => 28221, + ), + 28222 => (object) array( + 'question' => '9062', + 'answer' => 'False', + 'fraction' => '1', + 'feedback' => 'The correct anstwer is \'false\'. The only items on the list not sold at My Market are tobacco and weekly season tickets. It sells everything else! Biglietto unico is a single ticket for bus, funicular railway or metro (underground) while \'abbonamento mensile\' is a monthly season ticket. It also sells wine and grappa, as you can see them in the window, and fruit and vegetables as it says in the sign! Small shops in Naples often sell a variety of things, not always connected! ', + 'id' => 28222, + ), + ), + ), + 'hints' => false, + ); + $qsession = (object) array( + 'id' => '351032', + 'attemptid' => '23226', + 'questionid' => '9062', + 'newest' => '848428', + 'newgraded' => '848426', + 'sumpenalty' => '0', + 'manualcomment' => '', + 'manualcommentformat' => '1', + 'flagged' => '1', + ); + $qstates = array( + 848426 => (object) array( + 'attempt' => '23226', + 'question' => '9062', + 'originalquestion' => '0', + 'seq_number' => '0', + 'answer' => '', + 'timestamp' => '1200326384', + 'event' => '0', + 'grade' => '0', + 'raw_grade' => '0', + 'penalty' => '0', + 'id' => 848426, + ), + 848428 => (object) array( + 'attempt' => '23226', + 'question' => '9062', + 'originalquestion' => '0', + 'seq_number' => '1', + 'answer' => '28221', + 'timestamp' => '1200326384', + 'event' => '2', + 'grade' => '0', + 'raw_grade' => '0', + 'penalty' => '1', + 'id' => 848428, + ), + ); + + $qa = $this->updater->convert_question_attempt($quiz, $attempt, $question, $qsession, $qstates); + + $expectedqa = (object) array( + 'behaviour' => 'deferredfeedback', + 'questionid' => 9062, + 'maxmark' => 1, + 'minfraction' => 0, + 'flagged' => 0, + 'questionsummary' => "[my market] \n\nWhat can you buy in this shop? Is this list accurate? \n\nMark true or false (for the list as a whole). \n\n_single tickets_ \n\n_weekly season tickets_ \n\n_monthly season tickets_ \n\n_wine and grappa_ \n\n_fruit and vegetables_ \n\n_tobacco _", + 'rightanswer' => 'False', + 'responsesummary' => 'True', + 'timemodified' => 1200326384, + 'steps' => array( + 0 => (object) array( + 'sequencenumber' => 0, + 'state' => 'todo', + 'fraction' => null, + 'timecreated' => 1200326384, + 'userid' => 80300, + 'data' => array(), + ), + 1 => (object) array( + 'sequencenumber' => 1, + 'state' => 'complete', + 'fraction' => null, + 'timecreated' => 1200326384, + 'userid' => 80300, + 'data' => array('answer' => 1), + ), + ), + ); + + $this->assertEqual($expectedqa, $qa); + } + + public function test_truefalse_deferredfeedback_history90() { + $quiz = (object) array( + 'id' => '3', + 'course' => '1095', + 'name' => 'Introduction quiz', + 'intro' => 'Use this self-assessment quiz after you have read the course introduction. ', + 'introformat' => FORMAT_HTML, + 'questiondecimalpoints' => '-1', + 'showuserpicture' => '1', + 'showblocks' => '1', + 'timeopen' => '1150107000', + 'timeclose' => '0', + 'preferredbehaviour' => 'deferredfeedback', + 'attempts' => '0', + 'attemptonlast' => '0', + 'grademethod' => '1', + 'decimalpoints' => '2', + 'review' => '71760879', + 'questionsperpage' => '0', + 'shufflequestions' => '1', + 'shuffleanswers' => '1', + 'questions' => '96,98,104,106,101,108,111,102,113,0', + 'sumgrades' => '9', + 'grade' => '9', + 'timecreated' => '0', + 'timemodified' => '1150127779', + 'password' => '', + 'subnet' => '', + 'popup' => '0', + 'delay1' => '0', + 'delay2' => '0', + 'timelimit' => '0', + ); + $attempt = (object) array( + 'id' => '19', + 'uniqueid' => '19', + 'quiz' => '3', + 'userid' => '49542', + 'attempt' => '1', + 'sumgrades' => '9', + 'timestart' => '1150301292', + 'timefinish' => '1150301347', + 'timemodified' => '1150454872', + 'layout' => '96,108,102,101,106,113,104,98,111,0', + 'preview' => '0', + 'needsupgradetonewqe' => '1', + ); + $question = (object) array( + 'id' => '111', + 'category' => '5', + 'parent' => '0', + 'name' => 'Q7', + 'questiontext' => 'Web services, integration servers, XML, application servers, message-oriented middleware and remote procedure call can be used to enable integrated systems?', + 'questiontextformat' => '1', + 'defaultgrade' => '1', + 'penalty' => '0', + 'qtype' => 'truefalse', + 'length' => '1', + 'stamp' => 'learn.open.ac.uk+060612113518+uuFWow', + 'version' => 'learn.open.ac.uk+060612154736+HeFOV0', + 'hidden' => '0', + 'generalfeedback' => '', + 'generalfeedbackformat' => '1', + 'timecreated' => '0', + 'timemodified' => '0', + 'createdby' => NULL, + 'modifiedby' => NULL, + 'unlimited' => NULL, + 'maxmark' => '1', + 'options' => (object) array( + 'id' => '24', + 'question' => '111', + 'trueanswer' => '312', + 'falseanswer' => '313', + 'answers' => array( + 312 => (object) array( + 'question' => '111', + 'answer' => 'True', + 'fraction' => '1', + 'feedback' => 'They are all used in storing data or connecting together components of an integrated system.', + 'id' => 312, + ), + 313 => (object) array( + 'question' => '111', + 'answer' => 'False', + 'fraction' => '0', + 'feedback' => 'They are all used in storing data or connecting together components of an integrated system.', + 'id' => 313, + ), + ), + ), + 'hints' => false, + ); + $qsession = (object) array( + 'id' => '169', + 'attemptid' => '19', + 'questionid' => '111', + 'newest' => '252', + 'newgraded' => '252', + 'sumpenalty' => '0', + 'manualcomment' => '', + 'manualcommentformat' => '1', + 'flagged' => '1', + ); + $qstates = array( + 242 => (object) array( + 'attempt' => '19', + 'question' => '111', + 'originalquestion' => '0', + 'seq_number' => '0', + 'answer' => '', + 'timestamp' => '1150301292', + 'event' => '0', + 'grade' => '0', + 'raw_grade' => '0', + 'penalty' => '0', + 'id' => 242, + ), + 252 => (object) array( + 'attempt' => '19', + 'question' => '111', + 'originalquestion' => '0', + 'seq_number' => '1', + 'answer' => '', + 'timestamp' => '1150454872', + 'event' => '9', + 'grade' => '1', + 'raw_grade' => '1', + 'penalty' => '0', + 'id' => 252, + ), + ); + + $qa = $this->updater->convert_question_attempt($quiz, $attempt, $question, $qsession, $qstates); + + $expectedqa = (object) array( + 'behaviour' => 'deferredfeedback', + 'questionid' => 111, + 'maxmark' => 1, + 'minfraction' => 0, + 'flagged' => 0, + 'questionsummary' => 'Web services, integration servers, XML, application servers, message-oriented middleware and remote procedure call can be used to enable integrated systems?', + 'rightanswer' => 'True', + 'responsesummary' => '', + 'timemodified' => 1150454872, + 'steps' => array( + 0 => (object) array( + 'sequencenumber' => 0, + 'state' => 'todo', + 'fraction' => null, + 'timecreated' => 1150301292, + 'userid' => 49542, + 'data' => array(), + ), + 1 => (object) array( + 'sequencenumber' => 1, + 'state' => 'gradedwrong', + 'fraction' => null, + 'timecreated' => 1150454872, + 'userid' => 49542, + 'data' => array('-finish' => 1), + ), + 2 => (object) array( + 'sequencenumber' => 2, + 'state' => 'mangrright', + 'fraction' => 1, + 'timecreated' => 1150454872, + 'userid' => null, + 'data' => array('-comment' => '', '-maxmark' => 1, '-mark' => 1), + ), + ), + ); + + $this->assertEqual($expectedqa, $qa); + } +}