diff --git a/local/qeupgradehelper/convertquiz.php b/local/qeupgradehelper/convertquiz.php index 3cd966421df..69636b726e0 100755 --- a/local/qeupgradehelper/convertquiz.php +++ b/local/qeupgradehelper/convertquiz.php @@ -34,6 +34,8 @@ $confirmed = optional_param('confirmed', false, PARAM_BOOL); require_login(); require_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM)); +local_qeupgradehelper_require_upgraded(); + admin_externalpage_setup('qeupgradehelper', '', array(), local_qeupgradehelper_url('convertquiz', array('quizid' => $quizid))); $PAGE->navbar->add(get_string('listtodo', 'local_qeupgradehelper'), diff --git a/local/qeupgradehelper/cronsetup.php b/local/qeupgradehelper/cronsetup.php index 22fbe306ea6..790d5a6dec8 100755 --- a/local/qeupgradehelper/cronsetup.php +++ b/local/qeupgradehelper/cronsetup.php @@ -30,6 +30,7 @@ require_once($CFG->libdir . '/adminlib.php'); require_login(); require_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM)); + admin_externalpage_setup('qeupgradehelper', '', array(), local_qeupgradehelper_url('cronsetup')); $PAGE->navbar->add(get_string('cronsetup', 'local_qeupgradehelper')); diff --git a/local/qeupgradehelper/makeupgradetest.php b/local/qeupgradehelper/extracttestcase.php similarity index 95% rename from local/qeupgradehelper/makeupgradetest.php rename to local/qeupgradehelper/extracttestcase.php index 4e5f7a0a563..2835a0ce232 100755 --- a/local/qeupgradehelper/makeupgradetest.php +++ b/local/qeupgradehelper/extracttestcase.php @@ -1,243 +1,255 @@ -. - - -/** - * This is a simple script to facilitate testing. You need to run it before - * you go to /admin/ to upgrade your database. It extracts all the data for - * one particular attempt at one question, in a form that makes it easy to - * write a unit test for upgrade logic for that particular case. - * - * (The theory is that if the upgrade dies with an error, you can restore the - * database from backup, and then use this script to extract the problem case - * as a unit test. Then you can fix that unit tests. Then you can repeat the upgrade.) - * - * @package moodlecore - * @subpackage questionengine - * @copyright 2009 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - - -require_once(dirname(__FILE__) . '/../../../config.php'); -require_once($CFG->libdir . '/questionlib.php'); -require_once($CFG->libdir . '/formslib.php'); - -// ============================================================= -// Settings form -class grab_settings_form extends moodleform { - public function definition() { - $mform = $this->_form; - - $behaviour = array( - 0 => 'Deferred feedback', - 1 => 'Interactive', - ); - - $qtypes = array( - 'ddwtos' => 'Drag-drop', - 'description' => 'Description', - 'essay' => 'Essay', - 'match' => 'Matching', - 'multichoice' => 'Multiple choice', - 'numerical' => 'Numerical', - 'opaque' => 'OpenMark', - 'oumultiresponse' => 'OU multiple-response', - 'random' => 'Random', - 'shortanswer' => 'Short-answer', - 'truefalse' => 'True/false', - ); - - $mform->addElement('header', 'h1', 'Either extract a specific question_session'); - $mform->addElement('text', 'qsid', 'Question session id', array('size' => '10')); - $mform->addElement('header', 'h2', 'Or find and extract an example by type'); - $mform->addElement('select', 'behaviour', 'Behaviour', $behaviour); - $mform->addElement('text', 'statehistory', 'State history', array('size' => '10')); - $mform->addElement('select', 'qtype', 'Question type', $qtypes); - $mform->addElement('text', 'extratests', 'Extra conditions', array('size' => '50')); - $this->add_action_buttons(false, 'Create test case'); - } -} - -print_header('Question engine upgrade test case extractor'); - -$mform = new grab_settings_form($CFG->wwwroot . '/question/engine/upgradefromoldqe/grabexample.php', null, 'get'); -if ($fromform = $mform->get_data()) { - if (!empty($fromform->qsid)) { - generate_unit_test($fromform->qsid, 'qsession' . $fromform->qsid); - } else { - notify('Searching ...', 'notifysuccess'); - flush(); - $qsid = find_test_case($fromform->behaviour, $fromform->statehistory, - $fromform->qtype, $fromform->extratests); - if ($qsid) { - generate_unit_test($qsid, 'history' . $fromform->statehistory); - } else { - notify('No suitable attempts found.'); - } - } -} - -$mform->display(); -print_footer('empty'); - -/** - * Identify the question session id of a question attempt matching certain - * requirements. - * @param integer $behaviour 0 = deferred feedback, 1 = interactive. - * @param string $statehistory of states, last first. E.g. 620. - * @param string $qtype question type. - * @return integer question_session.id. - */ -function find_test_case($behaviour, $statehistory, $qtype, $extratests) { - global $CFG; - $possibleids = get_records_sql_menu(" - SELECT - qsess.id, - 1 - - FROM {$CFG->prefix}question_sessions qsess - JOIN {$CFG->prefix}question_states qst ON qst.attempt = qsess.attemptid - AND qst.question = qsess.questionid - JOIN {$CFG->prefix}quiz_attempts quiza ON quiza.uniqueid = qsess.attemptid - JOIN {$CFG->prefix}quiz quiz ON quiz.id = quiza.quiz - JOIN {$CFG->prefix}question q ON q.id = qsess.questionid - - WHERE q.qtype = '{$qtype}' - AND quiz.optionflags = {$behaviour} - - GROUP BY - qsess.id - - HAVING SUM( - (CASE WHEN qst.event = 10 THEN 1 ELSE qst.event END) * - POWER(10, CAST(qst.seq_number AS NUMERIC(110,0))) - ) = {$statehistory} - {$extratests}", 0, 100); - - if (!$possibleids) { - return null; - } - - return array_rand($possibleids); -} - -/** - * Grab all the data that upgrade will need for upgrading one - * attempt at one question from the old DB. - */ -function generate_unit_test($questionsessionid, $namesuffix) { - $qsession = get_record('question_sessions', 'id', $questionsessionid); - $attempt = get_record('quiz_attempts', 'uniqueid', $qsession->attemptid); - $quiz = get_record('quiz', 'id', $attempt->quiz); - $qstates = get_records_select('question_states', - "attempt = {$qsession->attemptid} AND question = {$qsession->questionid}", - 'seq_number, id'); - - $question = load_question($qsession->questionid, $quiz->id); - - if ($quiz->optionflags) { - $quiz->preferredbehaviour = 'interactive'; - } else { - $quiz->preferredbehaviour = 'deferredfeedback'; - } - echo "
- public function test_{$question->qtype}_{$quiz->preferredbehaviour}_{$namesuffix}() {
-";
- $upgrader->display_convert_attempt_input($quiz, $attempt,
- $question, $qsession, $qstates);
- echo '
- $qa = $this->updater->convert_question_attempt($quiz, $attempt, $question, $qsession, $qstates);
-
- $expectedqa = (object) array(';
- echo "
- 'behaviour' => '{$quiz->preferredbehaviour}',
- 'questionid' => {$question->id},
- 'maxmark' => {$question->maxmark},
- 'minfraction' => 0,
- 'flagged' => 0,
- 'questionsummary' => '',
- 'rightanswer' => '',
- 'responsesummary' => '',
- 'timemodified' => 0,
- 'steps' => array(";
- foreach ($qstates as $state) {
- echo "
- {$state->seq_number} => (object) array(
- 'sequencenumber' => {$state->seq_number},
- 'state' => '',
- 'fraction' => null,
- 'timecreated' => {$state->timestamp},
- 'userid' => {$attempt->userid},
- 'data' => array(),
- ),";
- }
- echo '
- ),
- );
-
- $this->assertEqual($expectedqa, $qa);
- }
-';
-}
-
-function format_var($name, $var) {
- $out = var_export($var, true);
- $out = str_replace('<', '<', $out);
- $out = str_replace('ADOFetchObj::__set_state(array(', '(object) array(', $out);
- $out = str_replace('stdClass::__set_state(array(', '(object) array(', $out);
- $out = str_replace('array (', 'array(', $out);
- $out = preg_replace('/=> \n\s*/', '=> ', $out);
- $out = str_replace(')),', '),', $out);
- $out = str_replace('))', ')', $out);
- $out = preg_replace('/\n (?! )/', "\n ", $out);
- $out = preg_replace('/\n (?! )/', "\n ", $out);
- $out = preg_replace('/\n (?! )/', "\n ", $out);
- $out = preg_replace('/\n (?! )/', "\n ", $out);
- $out = preg_replace('/\n (?! )/', "\n ", $out);
- $out = preg_replace('/\n (?! )/', "\n ", $out);
- $out = preg_replace('/\n (?! )/', "\n ", $out);
- $out = preg_replace('/\n(?! )/', "\n ", $out);
- return " $name = $out;\n";
-}
-
-function display_convert_attempt_input($quiz, $attempt, $question, $qsession, $qstates) {
- echo format_var('$quiz', $quiz);
- echo format_var('$attempt', $attempt);
- echo format_var('$question', $question);
- echo format_var('$qsession', $qsession);
- echo format_var('$qstates', $qstates);
-}
-
-function load_question($questionid, $quizid) {
- global $CFG, $QTYPES;
-
- $question = get_record_sql("
- SELECT q.*, qqi.grade AS maxmark
- FROM {$CFG->prefix}question q
- JOIN {$CFG->prefix}quiz_question_instances qqi ON qqi.question = q.id
- WHERE q.id = $questionid AND qqi.quiz = $quizid");
-
- if (!array_key_exists($question->qtype, $QTYPES)) {
- $question->qtype = 'missingtype';
- $question->questiontext = '' . get_string('warningmissingtype', 'quiz') . '
' . $question->questiontext; - } - - $QTYPES[$question->qtype]->get_question_options($question); - - return$question; -} +. + + +/** + * This is a simple script to facilitate testing. You need to run it before + * you go to /admin/ to upgrade your database. It extracts all the data for + * one particular attempt at one question, in a form that makes it easy to + * write a unit test for upgrade logic for that particular case. + * + * (The theory is that if the upgrade dies with an error, you can restore the + * database from backup, and then use this script to extract the problem case + * as a unit test. Then you can fix that unit tests. Then you can repeat the upgrade.) + * + * @package moodlecore + * @subpackage questionengine + * @copyright 2009 The Open University + * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later + */ + + +require_once(dirname(__FILE__) . '/../../../config.php'); +require_once($CFG->libdir . '/questionlib.php'); +require_once($CFG->libdir . '/formslib.php'); + +// ============================================================= +// Settings form +class grab_settings_form extends moodleform { + public function definition() { + $mform = $this->_form; + + $behaviour = array( + 0 => 'Deferred feedback', + 1 => 'Interactive', + ); + + $qtypes = array( + 'ddwtos' => 'Drag-drop', + 'description' => 'Description', + 'essay' => 'Essay', + 'match' => 'Matching', + 'multichoice' => 'Multiple choice', + 'numerical' => 'Numerical', + 'opaque' => 'OpenMark', + 'oumultiresponse' => 'OU multiple-response', + 'random' => 'Random', + 'shortanswer' => 'Short-answer', + 'truefalse' => 'True/false', + ); + + $mform->addElement('header', 'h1', 'Either extract a specific question_session'); + $mform->addElement('text', 'qsid', 'Question session id', array('size' => '10')); + $mform->addElement('header', 'h2', 'Or find and extract an example by type'); + $mform->addElement('select', 'behaviour', 'Behaviour', $behaviour); + $mform->addElement('text', 'statehistory', 'State history', array('size' => '10')); + $mform->addElement('select', 'qtype', 'Question type', $qtypes); + $mform->addElement('text', 'extratests', 'Extra conditions', array('size' => '50')); + $this->add_action_buttons(false, 'Create test case'); + } +} + +print_header('Question engine upgrade test case extractor'); + +$mform = new grab_settings_form($CFG->wwwroot . '/question/engine/upgradefromoldqe/grabexample.php', null, 'get'); +if ($fromform = $mform->get_data()) { + if (!empty($fromform->qsid)) { + generate_unit_test($fromform->qsid, 'qsession' . $fromform->qsid); + } else { + notify('Searching ...', 'notifysuccess'); + flush(); + $qsid = find_test_case($fromform->behaviour, $fromform->statehistory, + $fromform->qtype, $fromform->extratests); + if ($qsid) { + generate_unit_test($qsid, 'history' . $fromform->statehistory); + } else { + notify('No suitable attempts found.'); + } + } +} + +$mform->display(); +print_footer('empty'); + +/** + * Identify the question session id of a question attempt matching certain + * requirements. + * @param integer $behaviour 0 = deferred feedback, 1 = interactive. + * @param string $statehistory of states, last first. E.g. 620. + * @param string $qtype question type. + * @return integer question_session.id. + */ +function find_test_case($behaviour, $statehistory, $qtype, $extratests) { + global $CFG; + $possibleids = get_records_sql_menu(" + SELECT + qsess.id, + 1 + + FROM {$CFG->prefix}question_sessions qsess + JOIN {$CFG->prefix}question_states qst ON qst.attempt = qsess.attemptid + AND qst.question = qsess.questionid + JOIN {$CFG->prefix}quiz_attempts quiza ON quiza.uniqueid = qsess.attemptid + JOIN {$CFG->prefix}quiz quiz ON quiz.id = quiza.quiz + JOIN {$CFG->prefix}question q ON q.id = qsess.questionid + + WHERE q.qtype = '{$qtype}' + AND quiz.optionflags = {$behaviour} + + GROUP BY + qsess.id + + HAVING SUM( + (CASE WHEN qst.event = 10 THEN 1 ELSE qst.event END) * + POWER(10, CAST(qst.seq_number AS NUMERIC(110,0))) + ) = {$statehistory} + {$extratests}", 0, 100); + + if (!$possibleids) { + return null; + } + + return array_rand($possibleids); +} + +/** + * Grab all the data that upgrade will need for upgrading one + * attempt at one question from the old DB. + */ +function generate_unit_test($questionsessionid, $namesuffix) { + $qsession = get_record('question_sessions', 'id', $questionsessionid); + $attempt = get_record('quiz_attempts', 'uniqueid', $qsession->attemptid); + $quiz = get_record('quiz', 'id', $attempt->quiz); + $qstates = get_records_select('question_states', + "attempt = {$qsession->attemptid} AND question = {$qsession->questionid}", + 'seq_number, id'); + + $question = load_question($qsession->questionid, $quiz->id); + + if ($quiz->optionflags) { + $quiz->preferredbehaviour = 'interactive'; + } else { + $quiz->preferredbehaviour = 'deferredfeedback'; + } + echo "
+ public function test_{$question->qtype}_{$quiz->preferredbehaviour}_{$namesuffix}() {
+";
+ $upgrader->display_convert_attempt_input($quiz, $attempt,
+ $question, $qsession, $qstates);
+
+ if ($question->qtype == 'random') {
+ list($randombit, $realanswer) = explode('-', reset($qstates)->answer, 2);
+ $newquestionid = substr($randombit, 6);
+ $newquestion = $upgrader->load_question($newquestionid);
+ $newquestion->maxmark = $question->maxmark;
+
+ echo $upgrader->format_var('$realquestion', $newquestion);
+ echo ' $this->loader->put_question_in_cache($realquestion);
+';
+ }
+
+ echo '
+ $qa = $this->updater->convert_question_attempt($quiz, $attempt, $question, $qsession, $qstates);
+
+ $expectedqa = (object) array(';
+ echo "
+ 'behaviour' => '{$quiz->preferredbehaviour}',
+ 'questionid' => {$question->id},
+ 'maxmark' => {$question->maxmark},
+ 'minfraction' => 0,
+ 'flagged' => 0,
+ 'questionsummary' => '',
+ 'rightanswer' => '',
+ 'responsesummary' => '',
+ 'timemodified' => 0,
+ 'steps' => array(";
+ foreach ($qstates as $state) {
+ echo "
+ {$state->seq_number} => (object) array(
+ 'sequencenumber' => {$state->seq_number},
+ 'state' => '',
+ 'fraction' => null,
+ 'timecreated' => {$state->timestamp},
+ 'userid' => {$attempt->userid},
+ 'data' => array(),
+ ),";
+ }
+ echo '
+ ),
+ );
+
+ $this->assertEqual($expectedqa, $qa);
+ }
+';
+}
+
+function format_var($name, $var) {
+ $out = var_export($var, true);
+ $out = str_replace('<', '<', $out);
+ $out = str_replace('ADOFetchObj::__set_state(array(', '(object) array(', $out);
+ $out = str_replace('stdClass::__set_state(array(', '(object) array(', $out);
+ $out = str_replace('array (', 'array(', $out);
+ $out = preg_replace('/=> \n\s*/', '=> ', $out);
+ $out = str_replace(')),', '),', $out);
+ $out = str_replace('))', ')', $out);
+ $out = preg_replace('/\n (?! )/', "\n ", $out);
+ $out = preg_replace('/\n (?! )/', "\n ", $out);
+ $out = preg_replace('/\n (?! )/', "\n ", $out);
+ $out = preg_replace('/\n (?! )/', "\n ", $out);
+ $out = preg_replace('/\n (?! )/', "\n ", $out);
+ $out = preg_replace('/\n (?! )/', "\n ", $out);
+ $out = preg_replace('/\n (?! )/', "\n ", $out);
+ $out = preg_replace('/\n(?! )/', "\n ", $out);
+ return " $name = $out;\n";
+}
+
+function display_convert_attempt_input($quiz, $attempt, $question, $qsession, $qstates) {
+ echo format_var('$quiz', $quiz);
+ echo format_var('$attempt', $attempt);
+ echo format_var('$question', $question);
+ echo format_var('$qsession', $qsession);
+ echo format_var('$qstates', $qstates);
+}
+
+function load_question($questionid, $quizid) {
+ global $CFG, $QTYPES;
+
+ $question = get_record_sql("
+ SELECT q.*, qqi.grade AS maxmark
+ FROM {$CFG->prefix}question q
+ JOIN {$CFG->prefix}quiz_question_instances qqi ON qqi.question = q.id
+ WHERE q.id = $questionid AND qqi.quiz = $quizid");
+
+ if (!array_key_exists($question->qtype, $QTYPES)) {
+ $question->qtype = 'missingtype';
+ $question->questiontext = '' . get_string('warningmissingtype', 'quiz') . '
' . $question->questiontext; + } + + $QTYPES[$question->qtype]->get_question_options($question); + + return$question; +} diff --git a/local/qeupgradehelper/grabexample.php b/local/qeupgradehelper/grabexample.php deleted file mode 100755 index 8844bf80032..00000000000 --- a/local/qeupgradehelper/grabexample.php +++ /dev/null @@ -1,255 +0,0 @@ -. - - -/** - * This script is a bit of a hack. It is like makeupgradetest.php but has been - * hacked so you can extract test cases from a different databse. - * - * To make this work, you need to fill in the details below, and add - * - * if (defined('NASTY_HACK_IGNORE_CONFIGPHP')) { - * return; - * } - * - * to the very top of your config.php file. - * - * @package moodlecore - * @subpackage questionengine - * @copyright 2009 The Open University - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - - -error_reporting(E_ALL); -ini_set('display_errors', 1); -define('NASTY_HACK_IGNORE_CONFIGPHP', true); - -// Clone config.php to point at the learnacct DB read-only. -unset($CFG); // Ignore this line -$CFG = new stdClass(); - -$CFG->debug = 6143; -$CFG->debugdisplay = 1; - -// The following block points this site at learnacct database, read-only. -$CFG->dbtype = 'postgres7'; -$CFG->dbhost = ''; // TODO to use this script, complete this section -$CFG->dbname = ''; // with details of the database you want to -$CFG->dbuser = ''; // connect to. -$CFG->dbpass = ''; -$CFG->prefix = ''; - -$CFG->wwwroot = ''; // TODO to use this script, complete this section -$CFG->dirroot = ''; // with data copied from this Moodle's config.php -$CFG->dataroot = ''; -$CFG->directorypermissions = 02777; - -$CFG->admin = 'admin'; - -require_once($CFG->dirroot . '/local/ouflags/ouflags.class.php'); -$OUFLAGS = new ouflags('vle','dev'); - -require_once($CFG->dirroot . '/lib/setup.php'); -require_once($CFG->libdir . '/formslib.php'); -require_once($CFG->dirroot . '/question/engine/lib.php'); -require_once($CFG->dirroot . '/question/engine/upgradefromoldqe/upgrade.php'); - -$CFG->querylog = ''; - -// ============================================================= -// Settings form -class grab_settings_form extends moodleform { - public function definition() { - $mform = $this->_form; - - $behaviour = array( - 0 => 'Deferred feedback', - 1 => 'Interactive', - ); - - $qtypes = array( - 'ddwtos' => 'Drag-drop', - 'description' => 'Description', - 'essay' => 'Essay', - 'match' => 'Matching', - 'multichoice' => 'Multiple choice', - 'numerical' => 'Numerical', - 'opaque' => 'OpenMark', - 'oumultiresponse' => 'OU multiple-response', - 'random' => 'Random', - 'shortanswer' => 'Short-answer', - 'truefalse' => 'True/false', - ); - - $mform->addElement('header', 'h1', 'Either extract a specific question_session'); - $mform->addElement('text', 'qsid', 'Question session id', array('size' => '10')); - $mform->addElement('header', 'h2', 'Or find and extract an example by type'); - $mform->addElement('select', 'behaviour', 'Behaviour', $behaviour); - $mform->addElement('text', 'statehistory', 'State history', array('size' => '10')); - $mform->addElement('select', 'qtype', 'Question type', $qtypes); - $mform->addElement('text', 'extratests', 'Extra conditions', array('size' => '50')); - $this->add_action_buttons(false, 'Create test case'); - } -} - -class grabber_question_engine_attempt_upgrader extends question_engine_attempt_upgrader { - public function __construct() { - $this->questionloader = new question_engine_upgrade_question_loader(null); - } -} - -if ($sesskey = optional_param('sesskey', '', PARAM_RAW)) { - $USER->sesskey = $sesskey; -} - -print_header('Question engine upgrade test case extractor'); - -$mform = new grab_settings_form($CFG->wwwroot . '/question/engine/upgradefromoldqe/grabexample.php', null, 'get'); -if ($fromform = $mform->get_data()) { - if (!empty($fromform->qsid)) { - generate_unit_test($fromform->qsid, 'qsession' . $fromform->qsid); - } else { - notify('Searching ...', 'notifysuccess'); - flush(); - $qsid = find_test_case($fromform->behaviour, $fromform->statehistory, - $fromform->qtype, $fromform->extratests); - if ($qsid) { - generate_unit_test($qsid, 'history' . $fromform->statehistory); - } else { - notify('No suitable attempts found.'); - } - } -} - -$mform->display(); -print_footer('empty'); - -/** - * Identify the question session id of a question attempt matching certain - * requirements. - * @param integer $behaviour 0 = deferred feedback, 1 = interactive. - * @param string $statehistory of states, last first. E.g. 620. - * @param string $qtype question type. - * @return integer question_session.id. - */ -function find_test_case($behaviour, $statehistory, $qtype, $extratests) { - global $CFG; - $possibleids = get_records_sql_menu(" - SELECT - qsess.id, - 1 - - FROM {$CFG->prefix}question_sessions qsess - JOIN {$CFG->prefix}question_states qst ON qst.attempt = qsess.attemptid - AND qst.question = qsess.questionid - JOIN {$CFG->prefix}quiz_attempts quiza ON quiza.uniqueid = qsess.attemptid - JOIN {$CFG->prefix}quiz quiz ON quiz.id = quiza.quiz - JOIN {$CFG->prefix}question q ON q.id = qsess.questionid - - WHERE q.qtype = '{$qtype}' - AND quiz.optionflags = {$behaviour} - - GROUP BY - qsess.id - - HAVING SUM( - (CASE WHEN qst.event = 10 THEN 1 ELSE qst.event END) * - POWER(10, CAST(qst.seq_number AS NUMERIC(110,0))) - ) = {$statehistory} - {$extratests}", 0, 100); - - if (!$possibleids) { - return null; - } - - return array_rand($possibleids); -} - -/** - * Grab all the data that upgrade will need for upgrading one - * attempt at one question from the old DB. - */ -function generate_unit_test($questionsessionid, $namesuffix) { - $qsession = get_record('question_sessions', 'id', $questionsessionid); - $attempt = get_record('quiz_attempts', 'uniqueid', $qsession->attemptid); - $quiz = get_record('quiz', 'id', $attempt->quiz); - $qstates = get_records_select('question_states', - "attempt = {$qsession->attemptid} AND question = {$qsession->questionid}", - 'seq_number, id'); - - $upgrader = new grabber_question_engine_attempt_upgrader(); - - $question = $upgrader->load_question($qsession->questionid, $quiz->id); - - if ($quiz->optionflags) { - $quiz->preferredbehaviour = 'interactive'; - } else { - $quiz->preferredbehaviour = 'deferredfeedback'; - } - echo "
- public function test_{$question->qtype}_{$quiz->preferredbehaviour}_{$namesuffix}() {
-";
- $upgrader->display_convert_attempt_input($quiz, $attempt,
- $question, $qsession, $qstates);
-
- if ($question->qtype == 'random') {
- list($randombit, $realanswer) = explode('-', reset($qstates)->answer, 2);
- $newquestionid = substr($randombit, 6);
- $newquestion = $upgrader->load_question($newquestionid);
- $newquestion->maxmark = $question->maxmark;
-
- echo $upgrader->format_var('$realquestion', $newquestion);
- echo ' $this->loader->put_question_in_cache($realquestion);
-';
- }
-
- echo '
- $qa = $this->updater->convert_question_attempt($quiz, $attempt, $question, $qsession, $qstates);
-
- $expectedqa = (object) array(';
- echo "
- 'behaviour' => '{$quiz->preferredbehaviour}',
- 'questionid' => {$question->id},
- 'maxmark' => {$question->maxmark},
- 'minfraction' => 0,
- 'flagged' => 0,
- 'questionsummary' => '',
- 'rightanswer' => '',
- 'responsesummary' => '',
- 'timemodified' => 0,
- 'steps' => array(";
- foreach ($qstates as $state) {
- echo "
- {$state->seq_number} => (object) array(
- 'sequencenumber' => {$state->seq_number},
- 'state' => '',
- 'fraction' => null,
- 'timecreated' => {$state->timestamp},
- 'userid' => {$attempt->userid},
- 'data' => array(),
- ),";
- }
- echo '
- ),
- );
-
- $this->assertEqual($expectedqa, $qa);
- }
-';
-}
-
diff --git a/local/qeupgradehelper/index.php b/local/qeupgradehelper/index.php
index 938aaf7a9af..09dcda03fb3 100755
--- a/local/qeupgradehelper/index.php
+++ b/local/qeupgradehelper/index.php
@@ -38,7 +38,7 @@ admin_externalpage_setup('qeupgradehelper');
$renderer = $PAGE->get_renderer('local_qeupgradehelper');
$actions = array();
-if (local_qeupgradehelper_isupgraded()) {
+if (local_qeupgradehelper_is_upgraded()) {
$detected = get_string('upgradedsitedetected', 'local_qeupgradehelper');
$actions[] = local_qeupgradehelper_action::make('listtodo');
$actions[] = local_qeupgradehelper_action::make('listupgraded');
diff --git a/local/qeupgradehelper/lang/en/local_qeupgradehelper.php b/local/qeupgradehelper/lang/en/local_qeupgradehelper.php
index 8b670507058..d682b7a4343 100755
--- a/local/qeupgradehelper/lang/en/local_qeupgradehelper.php
+++ b/local/qeupgradehelper/lang/en/local_qeupgradehelper.php
@@ -41,12 +41,16 @@ $string['gotoindex'] = 'Back to the list of quizzes that can be upgraded';
$string['gotoquizreport'] = 'Go to the reports for this quiz, to check the upgrade';
$string['gotoresetlink'] = 'Go to the list of quizzes that can be reset';
$string['invalidquizid'] = 'Invaid quiz id. Either the quiz does not exist, or it has no attempts to convert.';
+$string['listpreupgrade'] = 'List quizzes and attempts';
+$string['listpreupgrade_desc'] = 'This will show a report of all the quizzes on the system and how many attempts they have. This will give you an idea of the scope of the upgrade you have to do.';
$string['listtodo'] = 'List quizzes still to upgrade';
$string['listtodo_desc'] = 'This will show a report of all the quizzes on the system (if any) that have attempts that still need to be upgraded to the new question engine.';
$string['listtodointro'] = 'These are all the quizzes with attempt data that still needs to be converted. You can convert the attempts by clicking the link.';
$string['listupgraded'] = 'List already upgrade quizzes that can be reset';
$string['listupgraded_desc'] = 'This will show a report of all the quizzes on the system whose attepmts have been upgraded, and where the old data is still present so the upgrade could be reset and redone.';
+$string['noquizattempts'] = 'Your site does not have any quiz attempts at all!';
$string['nothingupgradedyet'] = 'No upgraded attempts that can be reset';
+$string['notupgradedsiterequired'] = 'This script can only work before the site has been upgraded.';
$string['oldsitedetected'] = 'This appears to be a site that has not yet been upgraded to include the new question engine.';
$string['pluginname'] = 'Question engine upgrade helper';
$string['quizid'] = 'Quiz id';
@@ -58,3 +62,5 @@ $string['resetcomplete'] = 'Reset complete';
$string['resettingquizattempts'] = 'Resetting quiz attempts';
$string['upgradingquizattempts'] = 'Upgrading the attempts for quiz \'{$a->name}\' in course {$a->shortname}';
$string['upgradedsitedetected'] = 'This appears to be a site that has been upgraded to include the new question engine.';
+$string['upgradedsiterequired'] = 'This script can only work after the site has been upgraded.';
+
diff --git a/local/qeupgradehelper/listpreupgrade.php b/local/qeupgradehelper/listpreupgrade.php
new file mode 100755
index 00000000000..8f03ab8c7d9
--- /dev/null
+++ b/local/qeupgradehelper/listpreupgrade.php
@@ -0,0 +1,49 @@
+.
+
+/**
+ * Script to show all the quizzes in the site with how many attempts they have
+ * that will need to be upgraded.
+ *
+ * @package local
+ * @subpackage qeupgradehelper
+ * @copyright 2010 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+
+require_once(dirname(__FILE__) . '/../../config.php');
+require_once(dirname(__FILE__) . '/locallib.php');
+require_once($CFG->libdir . '/adminlib.php');
+
+require_login();
+require_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM));
+local_qeupgradehelper_require_not_upgraded();
+
+admin_externalpage_setup('qeupgradehelper', '', array(),
+ local_qeupgradehelper_url('listpreupgrade'));
+$PAGE->navbar->add(get_string('listpreupgrade', 'local_qeupgradehelper'));
+
+$renderer = $PAGE->get_renderer('local_qeupgradehelper');
+
+$quizzes = local_qeupgradehelper_get_upgradable_quizzes();
+
+if (empty($quizzes)) {
+ echo $renderer->simple_message_page(get_string('noquizattempts', 'local_qeupgradehelper'));
+
+} else {
+ echo $renderer->list_quizzes_pre_upgrade($quizzes);
+}
diff --git a/local/qeupgradehelper/listtodo.php b/local/qeupgradehelper/listtodo.php
index f4e2d4d7bbe..ead16af8e00 100755
--- a/local/qeupgradehelper/listtodo.php
+++ b/local/qeupgradehelper/listtodo.php
@@ -31,6 +31,8 @@ require_once($CFG->libdir . '/adminlib.php');
require_login();
require_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM));
+local_qeupgradehelper_require_upgraded();
+
admin_externalpage_setup('qeupgradehelper', '', array(),
local_qeupgradehelper_url('listtodo'));
$PAGE->navbar->add(get_string('listtodo', 'local_qeupgradehelper'));
diff --git a/local/qeupgradehelper/listupgraded.php b/local/qeupgradehelper/listupgraded.php
index 072ab567279..466a82e58fc 100755
--- a/local/qeupgradehelper/listupgraded.php
+++ b/local/qeupgradehelper/listupgraded.php
@@ -32,6 +32,8 @@ require_once($CFG->libdir . '/adminlib.php');
require_login();
require_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM));
+local_qeupgradehelper_require_upgraded();
+
admin_externalpage_setup('qeupgradehelper', '', array(),
local_qeupgradehelper_url('listupgraded'));
$PAGE->navbar->add(get_string('listupgraded', 'local_qeupgradehelper'));
diff --git a/local/qeupgradehelper/locallib.php b/local/qeupgradehelper/locallib.php
index bedfb1adf94..5dca915357e 100755
--- a/local/qeupgradehelper/locallib.php
+++ b/local/qeupgradehelper/locallib.php
@@ -29,10 +29,33 @@ defined('MOODLE_INTERNAL') || die();
/**
* Detect whether this site has been upgraded to the new question engine yet.
+ * @return bool whether the site has been upgraded.
*/
-function local_qeupgradehelper_isupgraded() {
- global $CFG;
- return is_readable($CFG->dirroot . '/question/engine/upgrade/upgradelib.php');
+function local_qeupgradehelper_is_upgraded() {
+ global $CFG, $DB;
+ $dbman = $DB->get_manager();
+ return is_readable($CFG->dirroot . '/question/engine/upgrade/upgradelib.php') &&
+ $dbman->table_exists('question_usages');
+}
+
+/**
+ * If the site has not yet been upgraded, display an error.
+ */
+function local_qeupgradehelper_require_upgraded() {
+ if (!local_qeupgradehelper_is_upgraded()) {
+ throw new moodle_exception('upgradedsiterequired', 'local_qeupgradehelper',
+ local_qeupgradehelper_url('index'));
+ }
+}
+
+/**
+ * If the site has been upgraded, display an error.
+ */
+function local_qeupgradehelper_require_not_upgraded() {
+ if (!local_qeupgradehelper_is_upgraded()) {
+ throw new moodle_exception('notupgradedsiterequired', 'local_qeupgradehelper',
+ local_qeupgradehelper_url('index'));
+ }
}
/**
diff --git a/local/qeupgradehelper/pretendupgrade.php b/local/qeupgradehelper/pretendupgrade.php
index 651e17ac161..b399f99ff90 100755
--- a/local/qeupgradehelper/pretendupgrade.php
+++ b/local/qeupgradehelper/pretendupgrade.php
@@ -1,224 +1,174 @@
-.
-
-
-/**
- * This script is a bit of a hack. It connects to another database and tries to
- * run most of the question engine upgrade. That is, it loads the old data, and
- * tries to convert it to the new structure, but it does not try to output it
- * at all.
- *
- * The idea is that this should find most of the logic errors, since the code to
- * save the new data to the DB is quite simple.
- *
- * To make this work, you need to fill in the details below, and add
- *
- * if (defined('NASTY_HACK_IGNORE_CONFIGPHP')) {
- * return;
- * }
- *
- * to the very top of your config.php file.
- *
- * @package moodlecore
- * @subpackage questionengine
- * @copyright 2010 The Open University
- * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
- */
-
-
-error_reporting(E_ALL);
-ini_set('display_errors', 1);
-define('NASTY_HACK_IGNORE_CONFIGPHP', true);
-
-// Clone config.php to point at the learnacct DB read-only.
-unset($CFG); // Ignore this line
-$CFG = new stdClass();
-
-$CFG->debug = 6143;
-$CFG->debugdisplay = 1;
-
-// The following block points this site at learnacct database, read-only.
-$CFG->dbtype = 'postgres7';
-$CFG->dbhost = ''; // TODO to use this script, complete this section
-$CFG->dbname = ''; // with details of the database you want to
-$CFG->dbuser = ''; // connect to.
-$CFG->dbpass = '';
-$CFG->prefix = '';
-
-$CFG->wwwroot = ''; // TODO to use this script, complete this section
-$CFG->dirroot = ''; // with data copied from this Moodle's config.php
-$CFG->dataroot = '';
-$CFG->directorypermissions = 02777;
-
-$CFG->admin = 'admin';
-
-require_once($CFG->dirroot . '/local/ouflags/ouflags.class.php');
-$OUFLAGS = new ouflags('vle','dev');
-
-require_once($CFG->dirroot . '/lib/setup.php');
-require_once($CFG->libdir . '/formslib.php');
-require_once($CFG->dirroot . '/question/engine/lib.php');
-require_once($CFG->dirroot . '/question/engine/upgradefromoldqe/upgrade.php');
-
-$CFG->querylog = '';
-$CFG->rcache = false;
-raise_memory_limit('1024M');
-
-// =============================================================
-
-class pretend_question_engine_attempt_upgrader extends question_engine_attempt_upgrader {
- public $fromquiz = 0;
- public $toquiz = 1000000;
- public $qsdone = 0;
-
- protected function get_quiz_ids() {
- return get_records_select_menu('quiz',
- "id >= {$this->fromquiz} AND id < {$this->toquiz}", 'id', 'id,1');
- }
-
- public function get_attemtps_where($quizid) {
- return "quiz = {$quizid} AND preview = 0";
- }
-
- protected function set_quba_preferred_behaviour($qubaid, $preferredbehaviour) {
- return true;
- }
-
- protected function set_quiz_attempt_layout($qubaid, $layout) {
- return true;
- }
-
- protected function delete_quiz_attempt($qubaid) {
- return true;
- }
-
- protected function insert_record($table, $record, $saveid = true) {
- if ($table == 'question_attempts') {
- if ($this->toquiz - $this->fromquiz <= 10) {
- echo "saving qa from {$record->_fromqsession} ";
- } else {
- echo 'S';
- }
- }
- $this->escape_fields($record);
- if ($table == 'question_attempt_steps' && is_null($record->sequencenumber)) {
- notify('Null sequencenumber found.');
- }
- if ($saveid) {
- $record->id = 666;
- }
- return true;
- }
-
- protected function convert_quiz_attempt($quiz, $attempt, $questionsessionsrs, $questionsstatesrs) {
- if (empty($quiz->preferredbehaviour)) {
- if ($quiz->optionflags == 0) {
- $quiz->preferredbehaviour = 'deferredfeedback';
- } else {
- $quiz->preferredbehaviour = 'interactive';
- }
- }
- return parent::convert_quiz_attempt($quiz, $attempt, $questionsessionsrs, $questionsstatesrs);
- }
-
- public function convert_question_attempt($quiz, $attempt, $question, $qsession, $qstates) {
- if ($this->toquiz - $this->fromquiz <= 10) {
- if ($this->qsdone % 10 == 0) {
- echo 'Current memory usage: ' . memory_get_usage() . '/' . memory_get_peak_usage() . '
'; - } -} - -// ============================================================= - -$fromquiz = required_param('from', PARAM_INT); -$toquiz = optional_param('to', $fromquiz + 1, PARAM_INT); - -print_header('Question engine upgrade tester'); -echo "\n\n{$qsconverted} question sessions converted in {$totaltime} seconds.
\n\n"; - -if ($qsconverted > 0) { - echo "Estimate for 5 million: " . format_time(5000000 / $qsconverted * $totaltime) . "
\n\n"; -} - -$number = $toquiz - $fromquiz; -echo ""; -if (record_exists_select('quiz', "id < {$fromquiz}")) { - $newfrom = $fromquiz - $number; - echo "Previous {$number} quizzes "; -} -if (record_exists_select('quiz', "id >= {$toquiz}")) { - $newto = $toquiz + $number; - echo "Next {$number} quizzes "; -} -echo "
\n\n";
-
-print_footer('empty');
-
-function do_pretend_upgrade($fromquiz, $toquiz) {
- $upgrader = new pretend_question_engine_attempt_upgrader();
- $upgrader->fromquiz = $fromquiz;
- $upgrader->toquiz = $toquiz;
-
-// xhprof_enable(XHPROF_FLAGS_MEMORY + XHPROF_FLAGS_NO_BUILTINS);
-
- $upgrader->convert_all_quiz_attempts();
-
-// $xhprof_data = xhprof_disable();
-// print_object($xhprof_data);
-
- return $upgrader->qsdone;
-}
+.
+
+
+/**
+ * This script is a bit of a hack. It connects to another database and tries to
+ * run most of the question engine upgrade. That is, it loads the old data, and
+ * tries to convert it to the new structure, but it does not try to output it
+ * at all.
+ *
+ * The idea is that this should find most of the logic errors, since the code to
+ * save the new data to the DB is quite simple.
+ *
+ * @package moodlecore
+ * @subpackage questionengine
+ * @copyright 2010 The Open University
+ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
+ */
+
+raise_memory_limit('1024M');
+
+// =============================================================
+
+class pretend_question_engine_attempt_upgrader extends question_engine_attempt_upgrader {
+ public $fromquiz = 0;
+ public $toquiz = 1000000;
+ public $qsdone = 0;
+
+ protected function get_quiz_ids() {
+ return get_records_select_menu('quiz',
+ "id >= {$this->fromquiz} AND id < {$this->toquiz}", 'id', 'id,1');
+ }
+
+ public function get_attemtps_where($quizid) {
+ return "quiz = {$quizid} AND preview = 0";
+ }
+
+ protected function set_quba_preferred_behaviour($qubaid, $preferredbehaviour) {
+ return true;
+ }
+
+ protected function set_quiz_attempt_layout($qubaid, $layout) {
+ return true;
+ }
+
+ protected function delete_quiz_attempt($qubaid) {
+ return true;
+ }
+
+ protected function insert_record($table, $record, $saveid = true) {
+ if ($table == 'question_attempts') {
+ if ($this->toquiz - $this->fromquiz <= 10) {
+ echo "saving qa from {$record->_fromqsession} ";
+ } else {
+ echo 'S';
+ }
+ }
+ $this->escape_fields($record);
+ if ($table == 'question_attempt_steps' && is_null($record->sequencenumber)) {
+ notify('Null sequencenumber found.');
+ }
+ if ($saveid) {
+ $record->id = 666;
+ }
+ return true;
+ }
+
+ protected function convert_quiz_attempt($quiz, $attempt, $questionsessionsrs, $questionsstatesrs) {
+ if (empty($quiz->preferredbehaviour)) {
+ if ($quiz->optionflags == 0) {
+ $quiz->preferredbehaviour = 'deferredfeedback';
+ } else {
+ $quiz->preferredbehaviour = 'interactive';
+ }
+ }
+ return parent::convert_quiz_attempt($quiz, $attempt, $questionsessionsrs, $questionsstatesrs);
+ }
+
+ public function convert_question_attempt($quiz, $attempt, $question, $qsession, $qstates) {
+ if ($this->toquiz - $this->fromquiz <= 10) {
+ if ($this->qsdone % 10 == 0) {
+ echo '
';
+ }
+ echo "qs {$qsession->id} ";
+ } else {
+ if ($this->qsdone % 100 == 0) {
+ echo '
';
+ }
+ echo "C";
+ }
+ $qa = parent::convert_question_attempt($quiz, $attempt, $question, $qsession, $qstates);
+ $qa->_fromqsession = $qsession->id;
+ $this->qsdone++;
+ return $qa;
+ }
+
+ public function supply_missing_question_attempt($quiz, $attempt, $question) {
+ if ($this->toquiz - $this->fromquiz <= 10) {
+ if ($this->qsdone % 10 == 0) {
+ echo '
';
+ }
+ echo "missing {$question->id} ";
+ } else {
+ if ($this->qsdone % 100 == 0) {
+ echo '
';
+ }
+ echo "M";
+ }
+ $qa = parent::supply_missing_question_attempt($quiz, $attempt, $question);
+ $qa->_fromqsession = 'missing';
+ return $qa;
+ }
+
+ protected function print_progress($done, $outof, $quizid) {
+ echo "\n\n
Current memory usage: ' . memory_get_usage() . '/' . memory_get_peak_usage() . '
'; + } +} + +// ============================================================= + +$fromquiz = required_param('from', PARAM_INT); +$toquiz = optional_param('to', $fromquiz + 1, PARAM_INT); + +print_header('Question engine upgrade tester'); +echo "\n\n{$qsconverted} question sessions converted in {$totaltime} seconds.
\n\n"; + +if ($qsconverted > 0) { + echo "Estimate for 5 million: " . format_time(5000000 / $qsconverted * $totaltime) . "
\n\n"; +} + +$number = $toquiz - $fromquiz; +echo ""; +if (record_exists_select('quiz', "id < {$fromquiz}")) { + $newfrom = $fromquiz - $number; + echo "Previous {$number} quizzes "; +} +if (record_exists_select('quiz', "id >= {$toquiz}")) { + $newto = $toquiz + $number; + echo "Next {$number} quizzes "; +} +echo "
\n\n"; + +print_footer('empty'); + +function do_pretend_upgrade($fromquiz, $toquiz) { + $upgrader = new pretend_question_engine_attempt_upgrader(); + $upgrader->fromquiz = $fromquiz; + $upgrader->toquiz = $toquiz; + + $upgrader->convert_all_quiz_attempts(); + + return $upgrader->qsdone; +} diff --git a/local/qeupgradehelper/resetquiz.php b/local/qeupgradehelper/resetquiz.php index 9a9e8e08845..6f1f9218187 100755 --- a/local/qeupgradehelper/resetquiz.php +++ b/local/qeupgradehelper/resetquiz.php @@ -33,6 +33,8 @@ $confirmed = optional_param('confirmed', false, PARAM_BOOL); require_login(); require_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM)); +local_qeupgradehelper_require_upgraded(); + admin_externalpage_setup('qeupgradehelper', '', array(), local_qeupgradehelper_url('resetquiz', array('quizid' => $quizid))); $PAGE->navbar->add(get_string('listupgraded', 'local_qeupgradehelper'),