Skip to content
Snippets Groups Projects
Commit 67118b8c authored by Tim Hunt's avatar Tim Hunt
Browse files

Deploying and undeploying variants.

This is handled by the question tests page.
parent 5cfae5f0
Branches
No related tags found
No related merge requests found
......@@ -147,7 +147,7 @@
<INDEX NAME="questionid-testcase-inputname" UNIQUE="true" FIELDS="questionid, testcase, inputname"/>
</INDEXES>
</TABLE>
<TABLE NAME="qtype_stack_qtest_expected" COMMENT="Holds the expected outcomes for each PRT for this question test." PREVIOUS="qtype_stack_qtest_inputs">
<TABLE NAME="qtype_stack_qtest_expected" COMMENT="Holds the expected outcomes for each PRT for this question test." PREVIOUS="qtype_stack_qtest_inputs" NEXT="qtype_stack_deployed_seeds">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="questionid"/>
<FIELD NAME="questionid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Foreign key reference to question.id." PREVIOUS="id" NEXT="testcase"/>
......@@ -165,5 +165,19 @@
<INDEX NAME="questionid-testcase-prtname" UNIQUE="true" FIELDS="questionid, testcase, prtname"/>
</INDEXES>
</TABLE>
<TABLE NAME="qtype_stack_deployed_seeds" COMMENT="Holds the seeds for the variants of each question that have been deployed." PREVIOUS="qtype_stack_qtest_expected">
<FIELDS>
<FIELD NAME="id" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="true" NEXT="questionid"/>
<FIELD NAME="questionid" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="Foreign key link to question.id" PREVIOUS="id" NEXT="seed"/>
<FIELD NAME="seed" TYPE="int" LENGTH="10" NOTNULL="true" SEQUENCE="false" COMMENT="The seed that has been deployed." PREVIOUS="questionid"/>
</FIELDS>
<KEYS>
<KEY NAME="primary" TYPE="primary" FIELDS="id" NEXT="questionid"/>
<KEY NAME="questionid" TYPE="foreign" FIELDS="questionid" REFTABLE="question" REFFIELDS="id" PREVIOUS="primary"/>
</KEYS>
<INDEXES>
<INDEX NAME="questionid-seed" UNIQUE="true" FIELDS="questionid, seed"/>
</INDEXES>
</TABLE>
</TABLES>
</XMLDB>
......@@ -352,5 +352,31 @@ function xmldb_qtype_stack_upgrade($oldversion) {
upgrade_plugin_savepoint(true, 2012032202, 'qtype', 'stack');
}
if ($oldversion < 2012032300) {
// Define table qtype_stack_deployed_seeds to be created
$table = new xmldb_table('qtype_stack_deployed_seeds');
// Adding fields to table qtype_stack_deployed_seeds
$table->add_field('id', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, XMLDB_SEQUENCE, null);
$table->add_field('questionid', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
$table->add_field('seed', XMLDB_TYPE_INTEGER, '10', null, XMLDB_NOTNULL, null, null);
// Adding keys to table qtype_stack_deployed_seeds
$table->add_key('primary', XMLDB_KEY_PRIMARY, array('id'));
$table->add_key('questionid', XMLDB_KEY_FOREIGN, array('questionid'), 'question', array('id'));
// Adding indexes to table qtype_stack_deployed_seeds
$table->add_index('questionid-seed', XMLDB_INDEX_UNIQUE, array('questionid', 'seed'));
// Conditionally launch create table for qtype_stack_deployed_seeds
if (!$dbman->table_exists($table)) {
$dbman->create_table($table);
}
// stack savepoint reached
upgrade_plugin_savepoint(true, 2012032300, 'qtype', 'stack');
}
return true;
}
<?php
// This file is part of Stack - http://stack.bham.ac.uk/
//
// Stack 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.
//
// Stack 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 Stack. If not, see <http://www.gnu.org/licenses/>.
/**
* This script handles the various deploy/undeploy actions from questiontestrun.php.
*
* @copyright 2012 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(dirname(__FILE__) . '/locallib.php');
// Get the parameters from the URL.
$questionid = required_param('questionid', PARAM_INT);
// Load the necessary data.
$questiondata = $DB->get_record('question', array('id' => $questionid), '*', MUST_EXIST);
$question = question_bank::load_question($questionid);
$context = $question->get_context();
// Check permissions.
require_login();
question_require_capability_on($questiondata, 'edit');
require_sesskey();
// Initialise $PAGE.
$PAGE->set_url('/question/type/stack/questiontestrun.php', array('questionid' => $question->id));
$PAGE->set_context($context);
// Process deploy if applicable.
$deploy = optional_param('deploy', null, PARAM_INT);
if (!is_null($deploy)) {
$record = new stdClass();
$record->questionid = $question->id;
$record->seed = $deploy;
$DB->insert_record('qtype_stack_deployed_seeds', $record);
redirect(new moodle_url('/question/type/stack/questiontestrun.php',
array('questionid' => $question->id, 'seed' => $deploy)));
}
// Process undeploy if applicable.
$undeploy = optional_param('undeploy', null, PARAM_INT);
if (!is_null($undeploy)) {
$DB->delete_records('qtype_stack_deployed_seeds',
array('questionid' => $question->id, 'seed' => $undeploy));
redirect(new moodle_url('/question/type/stack/questiontestrun.php',
array('questionid' => $question->id, 'seed' => $undeploy)));
}
redirect(new moodle_url('/question/type/stack/questiontestrun.php', array('questionid' => $question->id)));
......@@ -206,9 +206,13 @@ $string['addanothertestcase'] = 'Add another test case...';
$string['addatestcase'] = 'Add a test case...';
$string['addingatestcase'] = 'Adding a test case to question {$a}';
$string['createtestcase'] = 'Create test case';
$string['currentlyselectedvariant'] = 'This is the variant shown below';
$string['deletetestcase'] = 'Delete test case {$a->no} for question {$a->question}';
$string['deletetestcaseareyousure'] = 'Are you sure you want to delete test case {$a->no} for question {$a->question}?';
$string['deletethistestcase'] = 'Delete this test case...';
$string['deploy'] = 'Deploy';
$string['deployedvariantoptions'] = 'The following variants have been deployed: {$a}';
$string['deployedvariants'] = 'Deployed variants';
$string['editingtestcase'] = 'Editing test case {$a->no} for question {$a->question}';
$string['editthistestcase'] = 'Edit this test case...';
$string['expectedanswernote'] = 'Expected answer note';
......@@ -226,11 +230,18 @@ $string['inputstatusnamescore'] = 'Score';
$string['notestcasesyet'] = 'No test cases have been added yet.';
$string['penalty'] = 'Penalty';
$string['prtname'] = 'PRT name';
$string['questiondoesnotuserandomisation'] = 'This question does not use randomisation.';
$string['questionnotdeployedyet'] = 'Not variants of this question have been deployed yet.';
$string['questionpreview'] = 'Question preview';
$string['questiontests'] = 'Question tests';
$string['runquestiontests'] = 'Run the question tests...';
$string['showingundeployedvariant'] = 'Showing undeployed variant: {$a}';
$string['switchtovariant'] = 'Switch to arbitrary variant';
$string['testcasexresult'] = 'Test case {$a->no} {$a->result}';
$string['testingquestion'] = 'Testing question {$a}';
$string['testinputs'] = 'Test inputs';
$string['testthisvariant'] = 'Switch to test this variant';
$string['undeploy'] = 'Un-deploy';
// Support scripts (CAS chat, healthcheck, etc.)
$string['all'] = 'All';
......
......@@ -98,10 +98,15 @@ class qtype_stack_question extends question_graded_automatically {
*/
public $options;
/**
* @var array of seed values that have been deployed.
*/
public $deployedseeds;
/**
* @var int STACK specific: seeds Maxima's random number generator.
*/
public $seed;
public $seed = null;
/**
* @var array stack_cas_session STACK specific: session of variables.
......@@ -155,9 +160,22 @@ class qtype_stack_question extends question_graded_automatically {
public function start_attempt(question_attempt_step $step, $variant) {
// Completely unscientific approach to spreading the seed numbers around a bit.
// TODO needs to be sciencyfied!
$this->seed = $variant * 4321 + 12345;
// Work out the right seed to use.
if (!is_null($this->seed)) {
// Nasty hack, but if seed has already been set, then use that. This is
// used by the questiontestrun.php script to allow non-deployed
// variants to be browsed.
} else if (!$this->has_random_variants()) {
// Randomisation not used.
$this->seed = 1;
} else if (!empty($this->deployedseeds)) {
// Question has a fixed number of variants.
$this->seed = $this->deployedseeds[$variant - 1] + 0;
// Don't know why this is coming out as a string. + 0 converts to int.
} else {
// This question uses completely free randomisation.
$this->seed = $variant;
}
$step->set_qt_var('_seed', $this->seed);
// Build up the question session out of all the bits that need to go into it.
......@@ -392,12 +410,26 @@ class qtype_stack_question extends question_graded_automatically {
return $this->prtresults[$index];
}
/**
* @return bool whether this question uses randomisation.
*/
public function has_random_variants() {
return preg_match('~\brand~', $this->questionvariables);
}
public function get_num_variants() {
if (preg_match('~\brand~', $this->questionvariables)) {
return 20; // Only a limited number of variants for now, to improve caching.
} else {
if (!$this->has_random_variants()) {
// This question does not use randomisation. Only declare one variant.
return 1;
}
if (!empty($this->deployedseeds)) {
// Fixed number of deployed versions, declare that.
return count($this->deployedseeds);
}
// Random question without fixed variants. We will use the seed from Moodle raw.
return 1000000;
}
public function check_file_access($qa, $options, $component, $filearea, $args, $forcedownload) {
......
......@@ -54,7 +54,7 @@ $canedit = question_has_capability_on($questiondata, 'edit');
// Initialise $PAGE.
$urlparams = array('questionid' => $question->id);
if (!is_null($seed)) {
if (!is_null($seed) && $question->has_random_variants()) {
$urlparams['seed'] = $seed;
}
$PAGE->set_url('/question/type/stack/questiontestrun.php', $urlparams);
......@@ -66,8 +66,13 @@ $PAGE->set_title($title);
// Create the question usage we will use.
$quba = question_engine::make_questions_usage_by_activity('qtype_stack', $context);
$quba->set_preferred_behaviour('adaptive');
if (!is_null($seed)) {
// This is a bit of a hack to force the question to use a particular seed,
// even if it is not one of the deployed seeds.
$question->seed = $seed;
}
$slot = $quba->add_question($question, $question->defaultmark);
$quba->start_question($slot, $seed);
$quba->start_question($slot);
// Prepare the display options.
$options = new question_display_options();
......@@ -93,9 +98,68 @@ echo $OUTPUT->header();
echo $OUTPUT->heading($title);
// Display the list of deployed variants, with UI to edit the list.
// TODO
echo $OUTPUT->heading(get_string('deployedvariants', 'qtype_stack'), 3);
$variantmatched = false;
if (!$question->has_random_variants()) {
echo html_writer::tag('p', get_string('questiondoesnotuserandomisation', 'qtype_stack'));
$variantmatched = true;
} else if (empty($question->deployedseeds)) {
echo html_writer::tag('p', get_string('questionnotdeployedyet', 'qtype_stack'));
} else {
$seedchoices = array();
foreach ($question->deployedseeds as $deployedseed) {
if (!is_null($question->seed) && $question->seed == $deployedseed) {
$choice= html_writer::tag('b', $deployedseed,
array('title' => get_string('currentlyselectedvariant', 'qtype_stack')));;
$variantmatched = true;
} else {
$choice = html_writer::link(new moodle_url($PAGE->url, array('seed' => $deployedseed)),
$deployedseed, array('title' => get_string('testthisvariant', 'qtype_stack')));
}
if ($canedit) {
$choice .= ' ' . $OUTPUT->action_icon(new moodle_url('/question/type/stack/deploy.php',
array('questionid' => $question->id, 'undeploy' => $deployedseed, 'sesskey' => sesskey())),
new pix_icon('t/delete', get_string('undeploy', 'qtype_stack')));
}
$seedchoices[] = $choice;
}
echo html_writer::tag('p', get_string('deployedvariantoptions', 'qtype_stack',
implode(' | ', $seedchoices)));
}
if (!$variantmatched) {
if ($canedit) {
$deploybutton = ' ' . $OUTPUT->single_button(new moodle_url('/question/type/stack/deploy.php',
array('questionid' => $question->id, 'deploy' => $question->seed)),
get_string('deploy', 'qtype_stack'));
} else {
$deploybutton = '';
}
echo html_writer::tag('div', get_string('showingundeployedvariant', 'qtype_stack',
html_writer::tag('b', $question->seed)) . $deploybutton,
array('class' => 'undeployedvariant'));
}
echo html_writer::start_tag('form', array('method' => 'get', 'class' => 'switchtovariant',
'action' => new moodle_url('/question/type/stack/questiontestrun.php')));
echo html_writer::start_tag('p');
echo html_writer::input_hidden_params($PAGE->url, array('seed'));
echo html_writer::tag('label', get_string('switchtovariant', 'qtype_stack'), array('for' => 'seedfield'));
echo ' ' . html_writer::empty_tag('input', array('type' => 'text', 'size' => 7,
'id' => 'seedfield', 'name' => 'seed', 'value' => mt_rand()));
echo ' ' . html_writer::empty_tag('input', array('type' => 'submit', 'value' => get_string('go')));
echo html_writer::end_tag('p');
echo html_writer::end_tag('form');
// Display the question.
echo $OUTPUT->heading(get_string('questionpreview', 'qtype_stack'), 3);
echo $quba->render_question($slot, $options);
// Display the question note
......
......@@ -223,6 +223,12 @@ class qtype_stack extends question_type {
}
$noders->close();
$question->deployedseeds = $DB->get_fieldset_sql('
SELECT seed
FROM {qtype_stack_deployed_seeds}
WHERE questionid = ?
ORDER BY id', array($question->id));
return true;
}
......@@ -300,10 +306,13 @@ class qtype_stack extends question_type {
(bool) $prtdata->autosimplify, $prtdata->value,
$feedbackvariables->get_session(), $nodes);
}
$question->deployedseeds = array_values($questiondata->deployedseeds);
}
public function delete_question($questionid, $contextid) {
global $DB;
$DB->delete_records('qtype_stack_deployed_seeds', array('questionid' => $questionid));
$DB->delete_records('qtype_stack_qtest_expected', array('questionid' => $questionid));
$DB->delete_records('qtype_stack_qtest_inputs', array('questionid' => $questionid));
$DB->delete_records('qtype_stack_qtests', array('questionid' => $questionid));
......
......@@ -107,3 +107,11 @@ body.path-question-type-stack .questionvariables {
body.path-question-type-stack div.questionvariables {
padding-bottom: 0.1em;
}
body.path-question-type-stack .undeployedvariant .singlebutton,
body.path-question-type-stack .undeployedvariant .singlebutton div,
body.path-question-type-stack .undeployedvariant .singlebutton form {
display: inline;
}
body.path-question-type-stack .switchtovariant {
margin-top: 1em;
}
......@@ -24,7 +24,7 @@
defined('MOODLE_INTERNAL') || die();
$plugin->version = 2012032202;
$plugin->version = 2012032300;
$plugin->requires = 2012020200;
$plugin->cron = 0;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment