MDL-20636 Start of work on a local plugin to help with the question engine upgrade on large sites.

This commit is contained in:
Tim Hunt
2011-05-11 15:32:16 +01:00
parent 0859ff6503
commit d98389ffed
13 changed files with 1472 additions and 0 deletions
+39
View File
@@ -0,0 +1,39 @@
This plugin can help upgrade site with a large number of question attempts from
Moodle 2.0 to 2.1.
With a lot of question attempts, doing the whole conversion on upgrade is very
slow. The plugin can help with that in various ways.
When installed in a Moodle 2.0 site:
1. It provies a report of how much data there is to upgrade.
2. It can extract test-cases from the database. This can help you report bugs
in the upgrade process to the developers.
3. This plugin can also do a dry-run of the upgrade. (It loads the old data from
the database, transform it to the new form, but not write the transformed data
to the database.)
If this plugin is present during upgrade:
4. then only a subset of attempts are upgraded. (You can edit a function in
this plugin to control which attempts are upgraded immediately.)
If this plugin is present in a Moodle 2.0 site after upgrade:
5. If not all attempts have been upgraded in a 2.1 site, then this plugin
displays a list of how many quizzes still need to be upgraded
6. ... and can be used to complete the upgrade manually ...
7. or this plugin has a cron script that can be used to finish the upgrade
automatically after the main upgrade has finished.
(Note that none of the above acutally works yet. It is just a statement of
intent. Lots of the code here is a partial implementation of the concepts.)
+73
View File
@@ -0,0 +1,73 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Script to upgrade the attempts at a particular quiz, after confirmation.
*
* @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');
$quizid = required_param('quizid', PARAM_INT);
$confirmed = optional_param('confirmed', false, PARAM_BOOL);
require_login();
require_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM));
$quizsummary = report_quizupgrade_get_quiz($quizid);
if (!$quizsummary) {
print_error('invalidquizid', 'report_quizupgrade', report_quizupgrade_url('index.php'));
}
$quizsummary->name = format_string($quizsummary->name);
admin_externalpage_setup('reportquizupgrade');
if ($confirmed && data_submitted() && confirm_sesskey()) {
// Actually do the conversion.
admin_externalpage_print_header();
print_heading(get_string('upgradingquizattempts', 'report_quizupgrade', $quizsummary));
$upgrader = new report_quizupgrade_attempt_upgrader($quizsummary->id, $quizsummary->numtoconvert);
$upgrader->convert_all_quiz_attempts();
print_heading(get_string('conversioncomplete', 'report_quizupgrade'));
echo '<p><a href="' . $CFG->wwwroot . '/mod/quiz/report.php?q=' . $quizsummary->id .
'">' . get_string('gotoquizreport', 'report_quizupgrade') . '</a></p>';
print_continue(report_quizupgrade_url('index.php'));
admin_externalpage_print_footer();
exit;
}
// Print an are-you-sure page.
admin_externalpage_print_header();
print_heading(get_string('areyousure', 'report_quizupgrade'));
$message = get_string('areyousuremessage', 'report_quizupgrade', $quizsummary);
$params = array('quizid' => $quizsummary->id, 'confirmed' => 1, 'sesskey' => sesskey());
notice_yesno($message, report_quizupgrade_url('convertquiz.php'),
report_quizupgrade_url('index.php'), $params, null, 'post', 'get');
admin_externalpage_print_footer();
+255
View File
@@ -0,0 +1,255 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* 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 "<pre>
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);
}
</pre>';
}
+82
View File
@@ -0,0 +1,82 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Ad-hoc quiz upgrade plugin.
*
* Allows the attempt data for quizzes that were not upgraded during the main
* upgrade to be upgraded at any time.
*
* This screen lists all the quizzes that still 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));
// Start the page.
admin_externalpage_setup('reportquizupgrade');
admin_externalpage_print_header();
$quizzes = report_quizupgrade_get_upgradable_quizzes();
if (empty($quizzes)) {
print_heading(get_string('alreadydone', 'report_quizupgrade'));
} else {
print_heading(get_string('quizzeswithunconverted', 'report_quizupgrade'));
print_box(get_string('intro', 'report_quizupgrade'));
$table = new stdClass;
$table->head = array(
get_string('quizid', 'report_quizupgrade'),
get_string('course'),
get_string('modulename', 'quiz'),
get_string('attemptstoconvert', 'report_quizupgrade'),
get_string('actions', 'report_quizupgrade'),
);
foreach ($quizzes as $quiz) {
$table->data[] = array(
$quiz->id,
'<a href="' . $CFG->wwwroot . '/course/view.php?id=' . $quiz->courseid .
'">' . format_string($quiz->shortname) . '</a>',
'<a href="' . $CFG->wwwroot . '/mod/quiz/view.php?q=' . $quiz->id .
'">' . format_string($quiz->name) . '</a>',
$quiz->numtoconvert,
'<a href="' . report_quizupgrade_url('convertquiz.php?quizid=' . $quiz->id) .
'">' . get_string('convertattempts', 'report_quizupgrade') . '</a>',
);
}
print_table($table);
}
echo '<p><a href="' . report_quizupgrade_url('resetindex.php') . '">' .
get_string('gotoresetlink', 'report_quizupgrade') . '</a></p>';
admin_externalpage_print_footer();
+33
View File
@@ -0,0 +1,33 @@
<?php
/**
* Lang strings for admin/report/customsql
*
* @package report_customsql
* @copyright &copy; 2009 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['actions'] = 'Actions';
$string['alreadydone'] = 'Everything has already been converted';
$string['areyousure'] = 'Are you sure?';
$string['areyousuremessage'] = 'Do you wish to proceed with upgrading all {$a->numtoconvert} attempts at quiz \'{$a->name}\' in course {$a->shortname}?';
$string['areyousureresetmessage'] = 'Quiz \'{$a->name}\' in course {$a->shortname} has {$a->totalattempts} attempts, of which {$a->convertedattempts} were upgraded from the old system. Of those, {$a->resettableattempts} can be reset, for later re-conversion. Do you want to proceed with this?';
$string['attemptstoconvert'] = 'Attempts to convert';
$string['conversioncomplete'] = 'Conversion complete';
$string['convertattempts'] = 'Convert attempts...';
$string['convertedattempts'] = 'Converted attempts';
$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['intro'] = 'Please do not convert any attempts unless you have discussed it with Phil Butcher or Tim Hunt.';
$string['invalidquizid'] = 'Invaid quiz id. Either the quiz does not exist, or it has no attempts to convert.';
$string['pluginname'] = 'Question engine upgrade helper';
$string['quizid'] = 'Quiz id';
$string['quizupgrade'] = 'Quiz upgrade status';
$string['quizzeswithunconverted'] = 'The following quizzes have attempts that need to be converted';
$string['quizzesthatcanbereset'] = 'The following quizzes have converted attempts that you may be able to reset';
$string['resetattempts'] = 'Reset attempts...';
$string['resetcomplete'] = 'Reset complete';
$string['resettingquizattempts'] = 'Resetting quiz attempts';
$string['upgradingquizattempts'] = 'Upgrading the attempts for quiz \'{$a->name}\' in course {$a->shortname}';
+310
View File
@@ -0,0 +1,310 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Library code for the Quiz upgrade status report.
*
* @package local
* @subpackage qeupgradehelper
* @copyright 2010 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
require_once($CFG->dirroot . '/question/engine/upgradefromoldqe/upgrade.php');
/**
* @param string $localurl part to go at the end of the URL.
* @return string the full URL of that page within this report.
*/
function report_quizupgrade_url($localurl) {
global $CFG;
return $CFG->wwwroot . '/' . $CFG->admin . '/report/quizupgrade/' . $localurl;
}
/**
* Get the information about a quizzes that can be upgraded.
* @return array of objects with information about the quizzes that need upgrading.
* has fields quiz id, quiz name, course shortname, couresid and number of
* attempts that need converting.
*/
function report_quizupgrade_get_upgradable_quizzes() {
global $CFG;
return get_records_sql("
SELECT
quiz.id,
quiz.name,
c.shortname,
c.id AS courseid,
COUNT(1) AS numtoconvert
FROM {$CFG->prefix}quiz_attempts quiza
JOIN {$CFG->prefix}quiz quiz ON quiz.id = quiza.quiz
JOIN {$CFG->prefix}course c ON c.id = quiz.course
WHERE quiza.preview = 0
AND quiza.needsupgradetonewqe = 1
GROUP BY quiz.id, quiz.name, c.shortname, c.id
ORDER BY c.shortname, quiz.name, quiz.id");
}
/**
* Get the information about a quiz to be upgraded.
* @param integer $quizid the quiz id.
* @return object the information about that quiz, as for
* {@link report_quizupgrade_get_upgradable_quizzes()}.
*/
function report_quizupgrade_get_quiz($quizid) {
global $CFG;
return get_record_sql("
SELECT
quiz.id,
quiz.name,
c.shortname,
c.id AS courseid,
COUNT(1) AS numtoconvert
FROM {$CFG->prefix}quiz_attempts quiza
JOIN {$CFG->prefix}quiz quiz ON quiz.id = quiza.quiz
JOIN {$CFG->prefix}course c ON c.id = quiz.course
WHERE quiza.preview = 0
AND quiza.needsupgradetonewqe = 1
AND quiz.id = {$quizid}
GROUP BY quiz.id, quiz.name, c.shortname, c.id
ORDER BY c.shortname, quiz.name, quiz.id");
}
/**
* Get the information about quizzes that can be reset.
* @return array of objects with information about the quizzes that need upgrading.
* has fields quiz id, quiz name, course shortname, couresid and number of
* converted attempts.
*/
function report_quizupgrade_get_resettable_quizzes() {
global $CFG;
return get_records_sql("
SELECT
quiz.id,
quiz.name,
c.shortname,
c.id AS courseid,
COUNT(1) AS convertedattempts
FROM {$CFG->prefix}quiz_attempts quiza
JOIN {$CFG->prefix}quiz quiz ON quiz.id = quiza.quiz
JOIN {$CFG->prefix}course c ON c.id = quiz.course
WHERE quiza.preview = 0
AND quiza.needsupgradetonewqe = 0
AND EXISTS(SELECT 1 FROM {$CFG->prefix}question_states
WHERE attempt = quiza.uniqueid)
GROUP BY quiz.id, quiz.name, c.shortname, c.id
ORDER BY c.shortname, quiz.name, quiz.id");
}
/**
* Get the information about a quiz to be upgraded.
* @param integer $quizid the quiz id.
* @return object the information about that quiz, as for
* {@link report_quizupgrade_get_resettable_quizzes()}, but with extra fields
* totalattempts and resettableattempts.
*/
function report_quizupgrade_get_resettable_quiz($quizid) {
global $CFG;
return get_record_sql("
SELECT
quiz.id,
quiz.name,
c.shortname,
c.id AS courseid,
COUNT(1) AS totalattempts,
SUM(CASE WHEN quiza.needsupgradetonewqe = 0 AND
oldtimemodified.time IS NOT NULL THEN 1 ELSE 0 END) AS convertedattempts,
SUM(CASE WHEN quiza.needsupgradetonewqe = 0 AND
oldtimemodified.time >= newtimemodified.time THEN 1 ELSE 0 END) AS resettableattempts
FROM {$CFG->prefix}quiz_attempts quiza
JOIN {$CFG->prefix}quiz quiz ON quiz.id = quiza.quiz
JOIN {$CFG->prefix}course c ON c.id = quiz.course
LEFT JOIN (
SELECT attempt, MAX(timestamp) AS time
FROM {$CFG->prefix}question_states
GROUP BY attempt
) AS oldtimemodified ON oldtimemodified.attempt = quiza.uniqueid
LEFT JOIN (
SELECT qa.questionusageid, MAX(qas.timecreated) AS time
FROM {$CFG->prefix}question_attempts qa
JOIN {$CFG->prefix}question_attempt_steps qas ON qas.questionattemptid = qa.id
GROUP BY qa.questionusageid
) AS newtimemodified ON newtimemodified.questionusageid = quiza.uniqueid
WHERE quiza.preview = 0
AND quiz.id = {$quizid}
GROUP BY quiz.id, quiz.name, c.shortname, c.id");
}
class report_quizupgrade_attempt_upgrader extends question_engine_attempt_upgrader {
public $quizid;
public $attemptsdone = 0;
public $attemptstodo;
public function __construct($quizid, $attemptstodo) {
$this->quizid = $quizid;
$this->attemptstodo = $attemptstodo;
}
protected function get_quiz_ids() {
return array($this->quizid => 1);
}
protected function print_progress($done, $outof, $quizid) {
}
protected function convert_quiz_attempt($quiz, $attempt, $questionsessionsrs, $questionsstatesrs) {
$this->attemptsdone += 1;
print_progress($this->attemptsdone, $this->attemptstodo);
return parent::convert_quiz_attempt($quiz, $attempt, $questionsessionsrs, $questionsstatesrs);
}
protected function get_resettable_attempts($quiz) {
global $CFG;
return get_records_sql("
SELECT
quiza.*
FROM {$CFG->prefix}quiz_attempts quiza
LEFT JOIN (
SELECT attempt, MAX(timestamp) AS time
FROM {$CFG->prefix}question_states
GROUP BY attempt
) AS oldtimemodified ON oldtimemodified.attempt = quiza.uniqueid
LEFT JOIN (
SELECT qa.questionusageid, MAX(qas.timecreated) AS time
FROM {$CFG->prefix}question_attempts qa
JOIN {$CFG->prefix}question_attempt_steps qas ON qas.questionattemptid = qa.id
GROUP BY qa.questionusageid
) AS newtimemodified ON newtimemodified.questionusageid = quiza.uniqueid
WHERE quiza.preview = 0
AND quiza.needsupgradetonewqe = 0
AND oldtimemodified.time >= newtimemodified.time
AND quiza.quiz = {$quiz->id}");
}
public function reset_all_resettable_attempts() {
begin_sql();
$quiz = get_record('quiz', 'id', $this->quizid);
$attempts = $this->get_resettable_attempts($quiz);
foreach ($attempts as $attempt) {
$this->reset_attempt($quiz, $attempt);
}
commit_sql();
}
protected function reset_attempt($quiz, $attempt) {
global $CFG;
$this->attemptsdone += 1;
print_progress($this->attemptsdone, $this->attemptstodo);
$questionids = explode(',', $quiz->questions);
$slottoquestionid = array(0 => 0);
foreach ($questionids as $questionid) {
if ($questionid) {
$slottoquestionid[] = $questionid;
}
}
$slotlayout = explode(',', $attempt->layout);
$oldlayout = array();
$ok = true;
foreach ($slotlayout as $slot) {
if (array_key_exists($slot, $slottoquestionid)) {
$oldlayout[] = $slottoquestionid[$slot];
} else {
$ok = false;
break;
}
}
if ($ok) {
$layout = implode(',', $oldlayout);
} else {
$layout = $attempt->layout;
}
delete_records_select('question_attempt_step_data', "attemptstepid IN (
SELECT qas.id
FROM {$CFG->prefix}question_attempts qa
JOIN {$CFG->prefix}question_attempt_steps qas ON qas.questionattemptid = qa.id
WHERE questionusageid = {$attempt->uniqueid})");
delete_records_select('question_attempt_steps', "questionattemptid IN (
SELECT qa.id
FROM {$CFG->prefix}question_attempts qa
WHERE questionusageid = {$attempt->uniqueid})");
delete_records('question_attempts', 'questionusageid', $attempt->uniqueid);
set_field('question_usages', 'preferredbehaviour', 'to_be_set_later',
'id', $attempt->uniqueid);
set_field('quiz_attempts', 'layout', $layout,
'uniqueid', $attempt->uniqueid);
set_field('quiz_attempts', 'needsupgradetonewqe', 1,
'uniqueid', $attempt->uniqueid);
}
}
class grabber_question_engine_attempt_upgrader extends question_engine_attempt_upgrader {
public function __construct() {
$this->questionloader = new question_engine_upgrade_question_loader(null);
}
public function format_var($name, $var) {
$out = var_export($var, true);
$out = str_replace('<', '&lt;', $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";
}
public function display_convert_attempt_input($quiz, $attempt, $question, $qsession, $qstates) {
echo $this->format_var('$quiz', $quiz);
echo $this->format_var('$attempt', $attempt);
echo $this->format_var('$question', $question);
echo $this->format_var('$qsession', $qsession);
echo $this->format_var('$qstates', $qstates);
}
}
+243
View File
@@ -0,0 +1,243 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* 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 "<pre>
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);
}
</pre>';
}
function format_var($name, $var) {
$out = var_export($var, true);
$out = str_replace('<', '&lt;', $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 = '<p>' . get_string('warningmissingtype', 'quiz') . '</p>' . $question->questiontext;
}
$QTYPES[$question->qtype]->get_question_options($question);
return$question;
}
+224
View File
@@ -0,0 +1,224 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* 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 '<br />';
}
echo "qs {$qsession->id} ";
} else {
if ($this->qsdone % 100 == 0) {
echo '<br />';
}
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 '<br />';
}
echo "missing {$question->id} ";
} else {
if ($this->qsdone % 100 == 0) {
echo '<br />';
}
echo "M";
}
$qa = parent::supply_missing_question_attempt($quiz, $attempt, $question);
$qa->_fromqsession = 'missing';
return $qa;
}
protected function print_progress($done, $outof, $quizid) {
echo "</div>\n\n<h2>Quiz {$done}/{$outof} ({$quizid})</h2>\n\n<div>";
gc_collect_cycles();
echo '<p>Current memory usage: ' . memory_get_usage() . '/' . memory_get_peak_usage() . '</p>';
}
}
// =============================================================
$fromquiz = required_param('from', PARAM_INT);
$toquiz = optional_param('to', $fromquiz + 1, PARAM_INT);
print_header('Question engine upgrade tester');
echo "\n\n<h1>Starting pretend upgrade of database '$CFG->dbhost', prefix '$CFG->prefix' on host '$CFG->dbhost'.</h1>\n\n<div>";
$timestart = time();
$qsconverted = do_pretend_upgrade($fromquiz, $toquiz);
$totaltime = time() - $timestart;
echo "</div>\n\n<p>{$qsconverted} question sessions converted in {$totaltime} seconds.</p>\n\n";
if ($qsconverted > 0) {
echo "<p>Estimate for 5 million: " . format_time(5000000 / $qsconverted * $totaltime) . "</p>\n\n";
}
$number = $toquiz - $fromquiz;
echo "</div>\n\n<p>";
if (record_exists_select('quiz', "id < {$fromquiz}")) {
$newfrom = $fromquiz - $number;
echo "<a href='pretendupgrade.php?from={$newfrom}&amp;to={$fromquiz}'>Previous {$number} quizzes</a> ";
}
if (record_exists_select('quiz', "id >= {$toquiz}")) {
$newto = $toquiz + $number;
echo "<a href='pretendupgrade.php?from={$toquiz}&amp;to={$newto}'>Next {$number} quizzes</a> ";
}
echo "<p>\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;
}
+79
View File
@@ -0,0 +1,79 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Ad-hoc quiz upgrade plugin. This screen lists quizzes with attempts that can
* be reset.
*
* @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));
// Start the page.
admin_externalpage_setup('reportquizupgrade');
admin_externalpage_print_header();
$quizzes = report_quizupgrade_get_resettable_quizzes();
if (empty($quizzes)) {
print_heading(get_string('none'));
} else {
print_heading(get_string('quizzesthatcanbereset', 'report_quizupgrade'));
print_box(get_string('intro', 'report_quizupgrade'));
$table = new stdClass;
$table->head = array(
get_string('quizid', 'report_quizupgrade'),
get_string('course'),
get_string('modulename', 'quiz'),
get_string('convertedattempts', 'report_quizupgrade'),
get_string('actions', 'report_quizupgrade'),
);
foreach ($quizzes as $quiz) {
$table->data[] = array(
$quiz->id,
'<a href="' . $CFG->wwwroot . '/course/view.php?id=' . $quiz->courseid .
'">' . format_string($quiz->shortname) . '</a>',
'<a href="' . $CFG->wwwroot . '/mod/quiz/view.php?q=' . $quiz->id .
'">' . format_string($quiz->name) . '</a>',
$quiz->convertedattempts,
'<a href="' . $CFG->wwwroot . '/' . $CFG->admin .
'/report/quizupgrade/resetquiz.php?quizid=' . $quiz->id .
'">' . get_string('resetattempts', 'report_quizupgrade') . '</a>',
);
}
print_table($table);
}
echo '<p><a href="' . report_quizupgrade_url('index.php') . '">' .
get_string('gotoindex', 'report_quizupgrade') . '</a></p>';
admin_externalpage_print_footer();
+71
View File
@@ -0,0 +1,71 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Script to reset the attempts at a particular quiz, after confirmation.
*
* @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');
$quizid = required_param('quizid', PARAM_INT);
$confirmed = optional_param('confirmed', false, PARAM_BOOL);
require_login();
require_capability('moodle/site:config', get_context_instance(CONTEXT_SYSTEM));
$quizsummary = report_quizupgrade_get_resettable_quiz($quizid);
if (!$quizsummary) {
print_error('invalidquizid', 'report_quizupgrade', report_quizupgrade_url('index.php'));
}
$quizsummary->name = format_string($quizsummary->name);
admin_externalpage_setup('reportquizupgrade');
if ($confirmed && data_submitted() && confirm_sesskey()) {
// Actually do the conversion.
admin_externalpage_print_header();
print_heading(get_string('resettingquizattempts', 'report_quizupgrade', $quizsummary));
$upgrader = new report_quizupgrade_attempt_upgrader($quizsummary->id, $quizsummary->resettableattempts);
$upgrader->reset_all_resettable_attempts();
print_heading(get_string('resetcomplete', 'report_quizupgrade'));
print_continue(report_quizupgrade_url('resetindex.php'));
admin_externalpage_print_footer();
exit;
}
// Print an are-you-sure page.
admin_externalpage_print_header();
print_heading(get_string('areyousure', 'report_quizupgrade'));
$message = get_string('areyousureresetmessage', 'report_quizupgrade', $quizsummary);
$params = array('quizid' => $quizsummary->id, 'confirmed' => 1, 'sesskey' => sesskey());
notice_yesno($message, report_quizupgrade_url('resetquiz.php'),
report_quizupgrade_url('resetindex.php'), $params, null, 'post', 'get');
admin_externalpage_print_footer();
+32
View File
@@ -0,0 +1,32 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Add page to admin menu.
*
* @package local
* @subpackage qeupgradehelper
* @copyright 2011 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
if ($hassiteconfig) { // needs this condition or there is error on login page
$ADMIN->add('root', new admin_externalpage('qeupgradehelper',
get_string('pluginname', 'local_qeupgradehelper'),
new moodle_url('/local/qeupgradehelper/')));
}
+2
View File
@@ -0,0 +1,2 @@
#page-local-qeupgradehelper-index {
}
+29
View File
@@ -0,0 +1,29 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Version details.
*
* @package local
* @subpackage qeupgradehelper
* @copyright 2011 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die;
$plugin->version = 2011040400;
$plugin->requires = 2010080300;