Commit 8f9145bb authored by Tim Hunt's avatar Tim Hunt
Browse files

MDL-27505 Remove the ou-specific question types from the main branch.

The OU question types are now available from https://github.com/timhunt.
parent 9c197f44
<?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 behaviour that is used when the actual qim was not
* available.
*
* @package qbehaviour
* @subpackage opaque
* @copyright 2010 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* This behaviour is specifically for use with the Opaque question type.
*
* @copyright 2010 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qbehaviour_opaque extends question_behaviour {
/** @var string */
protected $preferredbehaviour;
/** @var string */
protected $questionsummary;
public function __construct(question_attempt $qa, $preferredbehaviour) {
parent::__construct($qa, $preferredbehaviour);
$this->preferredbehaviour = $preferredbehaviour;
}
public function required_question_definition_type() {
return 'qtype_opaque_question';
}
public function get_state_string($showcorrectness) {
$state = $this->qa->get_state();
$omstate = $this->qa->get_last_behaviour_var('_statestring');
if ($state->is_finished()) {
return $state->default_string($showcorrectness);
} else if ($omstate) {
return $omstate;
} else {
return get_string('notcomplete', 'qbehaviour_opaque');
}
}
public function init_first_step(question_attempt_step $step) {
global $USER;
// Set up the random seed to be the current time in milliseconds.
list($micros, $sec) = explode(' ', microtime());
$step->set_behaviour_var('_randomseed', $sec . floor($micros * 1000));
$step->set_behaviour_var('_userid', $USER->id);
$step->set_behaviour_var('_language', current_language());
$step->set_behaviour_var('_preferredbehaviour', $this->preferredbehaviour);
$opaquestate = qtype_opaque_update_state($this->qa, $step);
$step->set_behaviour_var('_statestring', $opaquestate->progressinfo);
// Remember the question summary.
$this->questionsummary = html_to_text($opaquestate->xhtml, 0, false);
}
public function get_question_summary() {
return $this->questionsummary;
}
protected function is_same_response(question_attempt_step $pendingstep) {
$newdata = $pendingstep->get_submitted_data();
foreach ($newdata as $key => $ignored) {
// If an omact_ button has been clicked, never treat this as a duplicate submission.
if (strpos($key, 'omact_') === 0) {
return false;
}
}
$olddata = $this->qa->get_last_step()->get_submitted_data();
return question_utils::arrays_have_same_keys_and_values($newdata, $olddata);
}
public function summarise_action(question_attempt_step $step) {
if ($step->has_behaviour_var('finish')) {
return $this->summarise_finish($step);
} else if ($step->has_behaviour_var('comment')) {
return $this->summarise_manual_comment($step);
} else {
$data = qtype_opaque_get_submitted_data($step);
$formatteddata = array();
foreach ($data as $name => $value) {
$formatteddata[] = $name . ' => ' . s($value);
}
if ($formatteddata) {
return get_string('submitted', 'question', implode(', ', $formatteddata));
} else {
return $this->summarise_start($step);
}
}
}
public function process_action(question_attempt_pending_step $pendingstep) {
if ($pendingstep->has_behaviour_var('finish')) {
return $this->process_finish($pendingstep);
}
if ($pendingstep->has_behaviour_var('comment')) {
return $this->process_comment($pendingstep);
} else if ($this->is_same_response($pendingstep) ||
$this->qa->get_state()->is_finished()) {
return question_attempt::DISCARD;
} else {
return $this->process_remote_action($pendingstep);
}
}
public function process_finish(question_attempt_pending_step $pendingstep) {
if ($this->qa->get_state()->is_finished()) {
return question_attempt::DISCARD;
}
// Try to get the question to stop.
$result = $this->process_remote_action($pendingstep);
if ($result == question_attempt::KEEP && !$this->qa->get_state()->is_finished()) {
// They tried to finish but the question is not finished, so all we
// can do is to set the state to gave up. This lets the renderer
// handle the review page appropriately.
$pendingstep->set_state(question_state::$gaveup);
}
return $result;
}
public function process_remote_action(question_attempt_pending_step $pendingstep) {
try {
$opaquestate = qtype_opaque_update_state($this->qa, $pendingstep);
} catch (SoapFault $sf) {
print_object($sf);
return question_attempt::DISCARD; // TODO better Opaque error handling.
}
if ($opaquestate->resultssequencenumber != $this->qa->get_num_steps()) {
$pendingstep->set_state(question_state::$todo);
$pendingstep->set_behaviour_var('_statestring', $opaquestate->progressinfo);
} else {
// Look for a score on the default axis.
$pendingstep->set_fraction(0);
foreach ($opaquestate->results->scores as $score) {
if ($score->axis == '') {
$pendingstep->set_fraction($score->marks / $this->question->defaultmark);
}
}
if ($opaquestate->results->attempts > 0) {
$pendingstep->set_state(question_state::$gradedright);
} else {
$pendingstep->set_state(
question_state::graded_state_for_fraction($pendingstep->get_fraction()));
}
if (!empty($opaquestate->results->questionLine)) {
$this->qa->set_question_summary(
$this->cleanup_results($opaquestate->results->questionLine));
}
if (!empty($opaquestate->results->answerLine)) {
$pendingstep->set_new_response_summary(
$this->cleanup_results($opaquestate->results->answerLine));
}
if (!empty($opaquestate->results->actionSummary)) {
$pendingstep->set_behaviour_var('_actionsummary',
$this->cleanup_results($opaquestate->results->actionSummary));
}
}
return question_attempt::KEEP;
}
protected function cleanup_results($line) {
return preg_replace('/\\s+/', ' ', $line);
}
}
<?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/>.
/**
* Strings for component 'qbehaviour_opaque', language 'en'.
*
* @package qbehaviour
* @subpackage opaque
* @copyright 2009 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['notcomplete'] = 'Not complete';
$string['pluginname'] = 'Question managed by a remote engine';
<?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/>.
/**
* Defines the renderer for the Opaque behaviour.
*
* @package qbehaviour
* @subpackage opaque
* @copyright 2010 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Renderer for outputting parts of a question when the actual behaviour
* used is not available.
*
* @copyright 2010 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qbehaviour_opaque_renderer extends qbehaviour_renderer {
public function controls(question_attempt $qa, question_display_options $options) {
if ($qa->get_state()->is_gave_up()) {
return html_writer::tag('div', get_string('notcompletedmessage', 'qtype_opaque'),
array('class' => 'question_aborted'));
}
try {
$opaquestate = qtype_opaque_update_state($qa);
} catch (SoapFault $sf) {
return html_writer::tag('div', get_string('errorconnecting', 'qtype_opaque') .
html_writer::tag('pre', get_string('soapfault', 'qtype_opaque', $sf),
array('class' => 'notifytiny')),
array('class' => 'opaqueerror'));
}
return html_writer::tag('div', $opaquestate->xhtml,
array('class' => qtype_opaque_browser_type()));
}
public function head_code(question_attempt $qa) {
$output = '';
try {
$opaquestate = qtype_opaque_update_state($qa);
} catch (SoapFault $sf) {
// Errors are reported properly elsewhere.
return '';
}
$question = $qa->get_question();
$resourcecache = new qtype_opaque_resource_cache($question->engineid,
$question->remoteid, $question->remoteversion);
if (!empty($opaquestate->cssfilename) &&
$resourcecache->file_in_cache($opaquestate->cssfilename)) {
$this->page->requires->css($resourcecache->file_url($opaquestate->cssfilename));
}
return $output;
}
}
\ No newline at end of file
<?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 file contains tests for the Opaque behaviour.
*
* @package qbehaviour
* @subpackage opaque
* @copyright 2010 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
require_once(dirname(__FILE__) . '/../../../engine/lib.php');
require_once(dirname(__FILE__) . '/../../../engine/simpletest/helpers.php');
require_once(dirname(__FILE__) . '/../behaviour.php');
/**
* Unit tests for the Opaque behaviour.
*
* @copyright 2010 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qbehaviour_opaque_test extends qbehaviour_walkthrough_test_base {
/**
* Makes an Opaque question that refers to one of the sample questions
* supplied by OpenMark.
* @return unknown_type
*/
protected function make_standard_om_question() {
global $DB;
$engineid = $DB->get_field('question_opaque_engines', 'MIN(id)', array());
if (empty($engineid)) {
throw new coding_exception('Cannot test Opaque. No question engines configured.');
}
question_bank::load_question_definition_classes('opaque');
$q = new qtype_opaque_question();
test_question_maker::initialise_a_question($q);
$q->name = 'samples.mu120.module5.question01';
$q->qtype = question_bank::get_qtype('opaque');
$q->defaultmark = 3;
$q->engineid = $engineid;
$q->remoteid = 'samples.mu120.module5.question01';
$q->remoteversion = '1.0';
return $q;
}
public function test_wrong_three_times() {
$q = $this->make_standard_om_question();
$this->start_attempt_at_question($q, 'interactive');
$qa = $this->quba->get_question_attempt($this->slot);
// Check the initial state.
$this->check_current_state(question_state::$todo);
$this->check_current_mark(null);
$this->check_current_output(
new PatternExpectation('/Below is a plan of a proposed garden/'),
new PatternExpectation('/You have 3 attempts/'),
$this->get_contains_button_expectation(
$qa->get_qt_field_name('omact_gen_14'), 'Check'));
$this->assertPattern('/^\s*Below is a plan of a proposed garden./',
$qa->get_question_summary());
$this->assertNull($qa->get_right_answer_summary());
// Submit a wrong answer.
$this->process_submission(array('omval_response1' => 1, 'omval_response2' => 666,
'omact_gen_14' => 'Check'));
// Verify.
$this->check_current_state(question_state::$todo);
$this->check_current_mark(null);
$this->check_current_output(
new PatternExpectation('/Below is a plan of a proposed garden/'),
new PatternExpectation('/incorrect/'),
new PatternExpectation('/' .
preg_quote(get_string('notcomplete', 'qbehaviour_opaque')) . '/'),
$this->get_contains_button_expectation(
$qa->get_qt_field_name('omact_ok'), 'Try again'));
// Try again.
$this->process_submission(array('omact_ok' => 'Try again'));
// Verify.
$this->check_current_state(question_state::$todo);
$this->check_current_mark(null);
$this->check_current_output(
new PatternExpectation('/You have 2 attempts/'));
// Submit a wrong answer again.
$this->process_submission(array('omval_response1' => 1, 'omval_response2' => 666,
'omact_gen_14' => 'Check'));
// Verify.
$this->check_current_state(question_state::$todo);
$this->check_current_mark(null);
$this->check_current_output(
new PatternExpectation('/Below is a plan of a proposed garden/'),
new PatternExpectation('/still incorrect/'),
new PatternExpectation('/' .
preg_quote(get_string('notcomplete', 'qbehaviour_opaque')) . '/'));
// Try again.
$this->process_submission(array('omact_ok' => 'Try again'));
// Verify.
$this->check_current_state(question_state::$todo);
$this->check_current_mark(null);
$this->check_current_output(
new PatternExpectation('/This is your last attempt/'));
// Submit a wrong answer third time.
$this->process_submission(array('omval_response1' => 1, 'omval_response2' => 666,
'omact_gen_14' => 'Check'));
// Verify.
$this->check_current_state(question_state::$gradedwrong);
$this->check_current_mark(0);
$this->check_current_output(
new PatternExpectation(
'/Please see MU120 Preparatory Resource Book B section 5.1/'),
new PatternExpectation('/still incorrect/'));
$this->assertTrue(preg_match(
'/What is \(X\*W\) (\d+\.\d+)\*(\d+), \(X\*L\)(\d+\.\d+)\*(\d+)\?/',
$qa->get_question_summary(), $matches));
$this->assertNull($qa->get_right_answer_summary());
$this->assertPattern('/' . $matches[1]*$matches[2] . '.*, ' . $matches[3]*$matches[4] . '/',
$qa->get_response_summary());
}
public function test_right_first_time() {
$q = $this->make_standard_om_question();
$this->start_attempt_at_question($q, 'interactive');
$qa = $this->quba->get_question_attempt($this->slot);
// Work out right answer (yuck!)
$html = $this->quba->render_question($this->slot, $this->displayoptions);
preg_match('/(0\.5|2\.0|3\.0) metres/', $html, $matches);
$scale = $matches[1];
preg_match('/Patio|Summer House|Flowerbed|Vegetable Plot|Pond/', $html, $matches);
$feature = $matches[0];
$sizes = array(
'Patio' => array(4, 7),
'Summer House' => array(3, 5),
'Flowerbed' => array(2, 7),
'Vegetable Plot' => array(3, 10),
'Pond' => array(2, 3),
);
$size = $sizes[$feature];
// Check the initial state.
$this->check_current_state(question_state::$todo);
$this->check_current_mark(null);
$this->check_current_output(
new PatternExpectation('/Below is a plan of a proposed garden/'),
new PatternExpectation('/You have 3 attempts/'),
$this->get_contains_button_expectation(
$qa->get_qt_field_name('omact_gen_14'), 'Check'));
// Submit the right answer.
$this->process_submission(array('omval_response1' => $size[0] * $scale,
'omval_response2' => $size[1] * $scale, 'omact_gen_14' => 'Check'));
// Verify.
$this->check_current_state(question_state::$gradedright);
$this->check_current_mark(3);
$this->check_current_output(
new PatternExpectation('/Below is a plan of a proposed garden/'),
new PatternExpectation('/correct/'));
}
public function test_different_max() {
$q = $this->make_standard_om_question();
$this->start_attempt_at_question($q, 'interactive', 6.0);
$qa = $this->quba->get_question_attempt($this->slot);
// Work out right answer (yuck!)
$html = $this->quba->render_question($this->slot, $this->displayoptions);
preg_match('/(0\.5|2\.0|3\.0) metres/', $html, $matches);
$scale = $matches[1];
preg_match('/Patio|Summer House|Flowerbed|Vegetable Plot|Pond/', $html, $matches);
$feature = $matches[0];
$sizes = array(
'Patio' => array(4, 7),
'Summer House' => array(3, 5),
'Flowerbed' => array(2, 7),
'Vegetable Plot' => array(3, 10),
'Pond' => array(2, 3),
);
$size = $sizes[$feature];
// Check the initial state.
$this->check_current_state(question_state::$todo);
$this->check_current_mark(null);
$this->check_current_output(
new PatternExpectation('/Below is a plan of a proposed garden/'),
new PatternExpectation('/You have 3 attempts/'),
$this->get_contains_button_expectation(
$qa->get_qt_field_name('omact_gen_14'), 'Check'));
// Submit the right answer.
$this->process_submission(array('omval_response1' => $size[0] * $scale,
'omval_response2' => $size[1] * $scale, 'omact_gen_14' => 'Check'));
// Verify.
$this->check_current_state(question_state::$gradedright);
$this->check_current_mark(6);
$this->check_current_output(
new PatternExpectation('/Below is a plan of a proposed garden/'),
new PatternExpectation('/correct/'));
}
public function test_gave_up() {
$q = $this->make_standard_om_question();
$this->start_attempt_at_question($q, 'interactive');
$this->quba->finish_all_questions();
$this->check_current_state(question_state::$gaveup);
$this->check_current_mark(null);
$this->check_current_output(
new PatternExpectation('/' .
preg_quote(get_string('notcompletedmessage', 'qtype_opaque')) . '/'));
}
}
<?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/>.
/**
* @package moodlecore
* @subpackage backup-moodle2
* @copyright 2011 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Provides the information to backup ddwtos questions
*
* @copyright 2011 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class backup_qtype_ddwtos_plugin extends backup_qtype_plugin {
/**
* Returns the qtype information to attach to question element
*/
protected function define_question_plugin_structure() {
// Define the virtual plugin element with the condition to fulfill
$plugin = $this->get_plugin_element(null, '../../qtype', 'ddwtos');
// Create one standard named plugin element (the visible container)
$pluginwrapper = new backup_nested_element($this->get_recommended_name());
// connect the visible container ASAP
$plugin->add_child($pluginwrapper