MDL-20636 Split question_definition::init_first_step into start_attempt and apply_attempt_state.
This commit is contained in:
@@ -179,7 +179,7 @@ abstract class question_behaviour {
|
||||
|
||||
/**
|
||||
* What is the minimum fraction that can be scored for this question.
|
||||
* Normally this will be based on $this->question->init_first_step($step),
|
||||
* Normally this will be based on $this->question->get_min_fraction(),
|
||||
* but may be modified in some way by the model.
|
||||
*
|
||||
* @return number the minimum fraction when this question is attempted under
|
||||
@@ -325,15 +325,17 @@ abstract class question_behaviour {
|
||||
abstract public function summarise_action(question_attempt_step $step);
|
||||
|
||||
/**
|
||||
* Initialise the first step in a question attempt.
|
||||
* Initialise the first step in a question attempt when a new
|
||||
* {@link question_attempt} is being started.
|
||||
*
|
||||
* This method must call $this->question->init_first_step($step), and may
|
||||
* This method must call $this->question->start_attempt($step), and may
|
||||
* perform additional processing if the model requries it.
|
||||
*
|
||||
* @param question_attempt_step $step the step being initialised.
|
||||
* @param question_attempt_step $step the first step of the
|
||||
* question_attempt being started.
|
||||
*/
|
||||
public function init_first_step(question_attempt_step $step) {
|
||||
$this->question->init_first_step($step);
|
||||
$this->question->start_attempt($step);
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -65,11 +65,6 @@ class qbehaviour_opaque extends question_behaviour {
|
||||
public function init_first_step(question_attempt_step $step) {
|
||||
global $USER;
|
||||
|
||||
if ($step->has_behaviour_var('_randomseed')) {
|
||||
// Reinitialising, nothing to do.
|
||||
return;
|
||||
}
|
||||
|
||||
// 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));
|
||||
|
||||
+10
-8
@@ -1891,16 +1891,16 @@ class question_attempt {
|
||||
}
|
||||
|
||||
/**
|
||||
* Start (or re-start) this question attempt.
|
||||
* Start this question attempt.
|
||||
*
|
||||
* You should not call this method directly. Call
|
||||
* {@link question_usage_by_activity::start_question()} instead.
|
||||
*
|
||||
* @param string|question_behaviour $preferredbehaviour the name of the
|
||||
* desired archetypal behaviour, or an actual model instance.
|
||||
* @param $submitteddata optional, used when re-starting to keep the same initial state.
|
||||
* @param $timestamp optional, the timstamp to record for this action. Defaults to now.
|
||||
* @param $userid optional, the user to attribute this action to. Defaults to the current user.
|
||||
* @param array $submitteddata optional, used when re-starting to keep the same initial state.
|
||||
* @param int $timestamp optional, the timstamp to record for this action. Defaults to now.
|
||||
* @param int $userid optional, the user to attribute this action to. Defaults to the current user.
|
||||
*/
|
||||
public function start($preferredbehaviour, $submitteddata = array(), $timestamp = null, $userid = null) {
|
||||
// Initialise the behaviour.
|
||||
@@ -1918,12 +1918,14 @@ class question_attempt {
|
||||
// Initialise the first step.
|
||||
$firststep = new question_attempt_step($submitteddata, $timestamp, $userid);
|
||||
$firststep->set_state(question_state::$todo);
|
||||
$this->behaviour->init_first_step($firststep);
|
||||
if ($submitteddata) {
|
||||
$this->question->apply_attempt_state($firststep);
|
||||
} else {
|
||||
$this->behaviour->init_first_step($firststep);
|
||||
}
|
||||
$this->add_step($firststep);
|
||||
|
||||
// Record questionline and correct answer.
|
||||
// TODO we should only really do this for new attempts, not when called
|
||||
// via load_from_records.
|
||||
$this->questionsummary = $this->behaviour->get_question_summary();
|
||||
$this->rightanswer = $this->behaviour->get_right_answer_summary();
|
||||
}
|
||||
@@ -2243,7 +2245,7 @@ class question_attempt {
|
||||
while ($record && $record->questionattemptid == $questionattemptid && !is_null($record->attemptstepid)) {
|
||||
$qa->steps[$i] = question_attempt_step::load_from_records($records, $record->attemptstepid);
|
||||
if ($i == 0) {
|
||||
$question->init_first_step($qa->steps[0]);
|
||||
$question->apply_attempt_state($qa->steps[0]);
|
||||
}
|
||||
$i++;
|
||||
$record = current($records);
|
||||
|
||||
@@ -55,7 +55,7 @@ class qtype_ddwtos_question_test extends UnitTestCase {
|
||||
public function test_summarise_response() {
|
||||
$dd = qtype_ddwtos_test_helper::make_a_ddwtos_question();
|
||||
$dd->shufflechoices = false;
|
||||
$dd->init_first_step(new question_attempt_step());
|
||||
$dd->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual('{quick} {fox} {lazy}',
|
||||
$dd->summarise_response(array('p1' => '1', 'p2' => '1', 'p3' => '1')));
|
||||
@@ -64,7 +64,7 @@ class qtype_ddwtos_question_test extends UnitTestCase {
|
||||
public function test_summarise_response_maths() {
|
||||
$dd = qtype_ddwtos_test_helper::make_a_maths_ddwtos_question();
|
||||
$dd->shufflechoices = false;
|
||||
$dd->init_first_step(new question_attempt_step());
|
||||
$dd->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual('{+} {-} {+} {-}',
|
||||
$dd->summarise_response(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2')));
|
||||
@@ -83,7 +83,7 @@ class qtype_ddwtos_question_test extends UnitTestCase {
|
||||
public function test_get_right_choice_for() {
|
||||
$dd = qtype_ddwtos_test_helper::make_a_ddwtos_question();
|
||||
$dd->shufflechoices = false;
|
||||
$dd->init_first_step(new question_attempt_step());
|
||||
$dd->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(1, $dd->get_right_choice_for(1));
|
||||
$this->assertEqual(1, $dd->get_right_choice_for(2));
|
||||
@@ -92,7 +92,7 @@ class qtype_ddwtos_question_test extends UnitTestCase {
|
||||
public function test_get_right_choice_for_maths() {
|
||||
$dd = qtype_ddwtos_test_helper::make_a_maths_ddwtos_question();
|
||||
$dd->shufflechoices = false;
|
||||
$dd->init_first_step(new question_attempt_step());
|
||||
$dd->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(1, $dd->get_right_choice_for(1));
|
||||
$this->assertEqual(2, $dd->get_right_choice_for(2));
|
||||
@@ -101,7 +101,7 @@ class qtype_ddwtos_question_test extends UnitTestCase {
|
||||
public function test_clear_wrong_from_response() {
|
||||
$dd = qtype_ddwtos_test_helper::make_a_maths_ddwtos_question();
|
||||
$dd->shufflechoices = false;
|
||||
$dd->init_first_step(new question_attempt_step());
|
||||
$dd->start_attempt(new question_attempt_step());
|
||||
|
||||
$initialresponse = array('p1' => '1', 'p2' => '1', 'p3' => '1', 'p4' => '1');
|
||||
$this->assertEqual(array('p1' => '1', 'p2' => '0', 'p3' => '1', 'p4' => '0'),
|
||||
@@ -111,7 +111,7 @@ class qtype_ddwtos_question_test extends UnitTestCase {
|
||||
public function test_get_num_parts_right() {
|
||||
$dd = qtype_ddwtos_test_helper::make_a_ddwtos_question();
|
||||
$dd->shufflechoices = false;
|
||||
$dd->init_first_step(new question_attempt_step());
|
||||
$dd->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array(2, 3),
|
||||
$dd->get_num_parts_right(array('p1' => '1', 'p2' => '1', 'p3' => '2')));
|
||||
@@ -122,7 +122,7 @@ class qtype_ddwtos_question_test extends UnitTestCase {
|
||||
public function test_get_num_parts_right_maths() {
|
||||
$dd = qtype_ddwtos_test_helper::make_a_maths_ddwtos_question();
|
||||
$dd->shufflechoices = false;
|
||||
$dd->init_first_step(new question_attempt_step());
|
||||
$dd->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array(2, 4),
|
||||
$dd->get_num_parts_right(array('p1' => '1', 'p2' => '1', 'p3' => '1', 'p4' => '1')));
|
||||
@@ -130,7 +130,7 @@ class qtype_ddwtos_question_test extends UnitTestCase {
|
||||
|
||||
public function test_get_expected_data() {
|
||||
$dd = qtype_ddwtos_test_helper::make_a_ddwtos_question();
|
||||
$dd->init_first_step(new question_attempt_step());
|
||||
$dd->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array('p1' => PARAM_INT, 'p2' => PARAM_INT, 'p3' => PARAM_INT),
|
||||
$dd->get_expected_data());
|
||||
@@ -139,7 +139,7 @@ class qtype_ddwtos_question_test extends UnitTestCase {
|
||||
public function test_get_correct_response() {
|
||||
$dd = qtype_ddwtos_test_helper::make_a_ddwtos_question();
|
||||
$dd->shufflechoices = false;
|
||||
$dd->init_first_step(new question_attempt_step());
|
||||
$dd->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array('p1' => '1', 'p2' => '1', 'p3' => '1'),
|
||||
$dd->get_correct_response());
|
||||
@@ -148,7 +148,7 @@ class qtype_ddwtos_question_test extends UnitTestCase {
|
||||
public function test_get_correct_response_maths() {
|
||||
$dd = qtype_ddwtos_test_helper::make_a_maths_ddwtos_question();
|
||||
$dd->shufflechoices = false;
|
||||
$dd->init_first_step(new question_attempt_step());
|
||||
$dd->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2'),
|
||||
$dd->get_correct_response());
|
||||
@@ -156,7 +156,7 @@ class qtype_ddwtos_question_test extends UnitTestCase {
|
||||
|
||||
public function test_is_same_response() {
|
||||
$dd = qtype_ddwtos_test_helper::make_a_ddwtos_question();
|
||||
$dd->init_first_step(new question_attempt_step());
|
||||
$dd->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertTrue($dd->is_same_response(
|
||||
array(),
|
||||
@@ -180,7 +180,7 @@ class qtype_ddwtos_question_test extends UnitTestCase {
|
||||
}
|
||||
public function test_is_complete_response() {
|
||||
$dd = qtype_ddwtos_test_helper::make_a_ddwtos_question();
|
||||
$dd->init_first_step(new question_attempt_step());
|
||||
$dd->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertFalse($dd->is_complete_response(array()));
|
||||
$this->assertFalse($dd->is_complete_response(
|
||||
@@ -192,7 +192,7 @@ class qtype_ddwtos_question_test extends UnitTestCase {
|
||||
|
||||
public function test_is_gradable_response() {
|
||||
$dd = qtype_ddwtos_test_helper::make_a_ddwtos_question();
|
||||
$dd->init_first_step(new question_attempt_step());
|
||||
$dd->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertFalse($dd->is_gradable_response(array()));
|
||||
$this->assertFalse($dd->is_gradable_response(
|
||||
@@ -207,7 +207,7 @@ class qtype_ddwtos_question_test extends UnitTestCase {
|
||||
public function test_grading() {
|
||||
$dd = qtype_ddwtos_test_helper::make_a_ddwtos_question();
|
||||
$dd->shufflechoices = false;
|
||||
$dd->init_first_step(new question_attempt_step());
|
||||
$dd->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array(1, question_state::$gradedright),
|
||||
$dd->grade_response(array('p1' => '1', 'p2' => '1', 'p3' => '1')));
|
||||
@@ -220,7 +220,7 @@ class qtype_ddwtos_question_test extends UnitTestCase {
|
||||
public function test_grading_maths() {
|
||||
$dd = qtype_ddwtos_test_helper::make_a_maths_ddwtos_question();
|
||||
$dd->shufflechoices = false;
|
||||
$dd->init_first_step(new question_attempt_step());
|
||||
$dd->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array(1, question_state::$gradedright),
|
||||
$dd->grade_response(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2')));
|
||||
@@ -233,7 +233,7 @@ class qtype_ddwtos_question_test extends UnitTestCase {
|
||||
public function test_classify_response() {
|
||||
$dd = qtype_ddwtos_test_helper::make_a_ddwtos_question();
|
||||
$dd->shufflechoices = false;
|
||||
$dd->init_first_step(new question_attempt_step());
|
||||
$dd->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array(
|
||||
1 => new question_classified_response(1, 'quick', 1),
|
||||
|
||||
@@ -66,27 +66,33 @@ abstract class qtype_gapselect_question_base extends question_graded_automatical
|
||||
/** @var array shuffled choice indexes. */
|
||||
protected $choiceorder;
|
||||
|
||||
public function init_first_step(question_attempt_step $step) {
|
||||
public function start_attempt(question_attempt_step $step) {
|
||||
foreach ($this->choices as $group => $choices) {
|
||||
$varname = '_choiceorder' . $group;
|
||||
|
||||
if ($step->has_qt_var($varname)) {
|
||||
$choiceorder = explode(',', $step->get_qt_var($varname));
|
||||
|
||||
} else {
|
||||
$choiceorder = array_keys($choices);
|
||||
if ($this->shufflechoices) {
|
||||
shuffle($choiceorder);
|
||||
}
|
||||
$choiceorder = array_keys($choices);
|
||||
if ($this->shufflechoices) {
|
||||
shuffle($choiceorder);
|
||||
}
|
||||
$step->set_qt_var('_choiceorder' . $group, implode(',', $choiceorder));
|
||||
$this->set_choiceorder($group, $choiceorder);
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($choiceorder as $key => $value) {
|
||||
$this->choiceorder[$group][$key + 1] = $value;
|
||||
}
|
||||
public function apply_attempt_state(question_attempt_step $step) {
|
||||
foreach ($this->choices as $group => $choices) {
|
||||
$this->set_choiceorder($group, explode(',',
|
||||
$step->get_qt_var('_choiceorder' . $group)));
|
||||
}
|
||||
}
|
||||
|
||||
if (!$step->has_qt_var($varname)) {
|
||||
$step->set_qt_var($varname, implode(',', $this->choiceorder[$group]));
|
||||
}
|
||||
/**
|
||||
* Helper method used by both {@link start_attempt()} and
|
||||
* {@link apply_attempt_state()}.
|
||||
* @param int $group the group number.
|
||||
* @param array $choiceorder the choices, in order.
|
||||
*/
|
||||
protected function set_choiceorder($group, $choiceorder) {
|
||||
foreach ($choiceorder as $key => $value) {
|
||||
$this->choiceorder[$group][$key + 1] = $value;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -55,7 +55,7 @@ class qtype_gapselect_question_test extends UnitTestCase {
|
||||
public function test_summarise_response() {
|
||||
$gapselect = qtype_gapselect_test_helper::make_a_gapselect_question();
|
||||
$gapselect->shufflechoices = false;
|
||||
$gapselect->init_first_step(new question_attempt_step());
|
||||
$gapselect->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual('{quick} {fox} {lazy}',
|
||||
$gapselect->summarise_response(array('p1' => '1', 'p2' => '1', 'p3' => '1')));
|
||||
@@ -64,7 +64,7 @@ class qtype_gapselect_question_test extends UnitTestCase {
|
||||
public function test_summarise_response_maths() {
|
||||
$gapselect = qtype_gapselect_test_helper::make_a_maths_gapselect_question();
|
||||
$gapselect->shufflechoices = false;
|
||||
$gapselect->init_first_step(new question_attempt_step());
|
||||
$gapselect->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual('{+} {-} {+} {-}',
|
||||
$gapselect->summarise_response(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2')));
|
||||
@@ -83,7 +83,7 @@ class qtype_gapselect_question_test extends UnitTestCase {
|
||||
public function test_get_right_choice_for() {
|
||||
$gapselect = qtype_gapselect_test_helper::make_a_gapselect_question();
|
||||
$gapselect->shufflechoices = false;
|
||||
$gapselect->init_first_step(new question_attempt_step());
|
||||
$gapselect->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(1, $gapselect->get_right_choice_for(1));
|
||||
$this->assertEqual(1, $gapselect->get_right_choice_for(2));
|
||||
@@ -92,7 +92,7 @@ class qtype_gapselect_question_test extends UnitTestCase {
|
||||
public function test_get_right_choice_for_maths() {
|
||||
$gapselect = qtype_gapselect_test_helper::make_a_maths_gapselect_question();
|
||||
$gapselect->shufflechoices = false;
|
||||
$gapselect->init_first_step(new question_attempt_step());
|
||||
$gapselect->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(1, $gapselect->get_right_choice_for(1));
|
||||
$this->assertEqual(2, $gapselect->get_right_choice_for(2));
|
||||
@@ -101,7 +101,7 @@ class qtype_gapselect_question_test extends UnitTestCase {
|
||||
public function test_clear_wrong_from_response() {
|
||||
$gapselect = qtype_gapselect_test_helper::make_a_maths_gapselect_question();
|
||||
$gapselect->shufflechoices = false;
|
||||
$gapselect->init_first_step(new question_attempt_step());
|
||||
$gapselect->start_attempt(new question_attempt_step());
|
||||
|
||||
$initialresponse = array('p1' => '1', 'p2' => '1', 'p3' => '1', 'p4' => '1');
|
||||
$this->assertEqual(array('p1' => '1', 'p2' => '0', 'p3' => '1', 'p4' => '0'),
|
||||
@@ -111,7 +111,7 @@ class qtype_gapselect_question_test extends UnitTestCase {
|
||||
public function test_get_num_parts_right() {
|
||||
$gapselect = qtype_gapselect_test_helper::make_a_gapselect_question();
|
||||
$gapselect->shufflechoices = false;
|
||||
$gapselect->init_first_step(new question_attempt_step());
|
||||
$gapselect->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array(2, 3),
|
||||
$gapselect->get_num_parts_right(array('p1' => '1', 'p2' => '1', 'p3' => '2')));
|
||||
@@ -122,7 +122,7 @@ class qtype_gapselect_question_test extends UnitTestCase {
|
||||
public function test_get_num_parts_right_maths() {
|
||||
$gapselect = qtype_gapselect_test_helper::make_a_maths_gapselect_question();
|
||||
$gapselect->shufflechoices = false;
|
||||
$gapselect->init_first_step(new question_attempt_step());
|
||||
$gapselect->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array(2, 4),
|
||||
$gapselect->get_num_parts_right(array('p1' => '1', 'p2' => '1', 'p3' => '1', 'p4' => '1')));
|
||||
@@ -130,7 +130,7 @@ class qtype_gapselect_question_test extends UnitTestCase {
|
||||
|
||||
public function test_get_expected_data() {
|
||||
$gapselect = qtype_gapselect_test_helper::make_a_gapselect_question();
|
||||
$gapselect->init_first_step(new question_attempt_step());
|
||||
$gapselect->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array('p1' => PARAM_INT, 'p2' => PARAM_INT, 'p3' => PARAM_INT),
|
||||
$gapselect->get_expected_data());
|
||||
@@ -139,7 +139,7 @@ class qtype_gapselect_question_test extends UnitTestCase {
|
||||
public function test_get_correct_response() {
|
||||
$gapselect = qtype_gapselect_test_helper::make_a_gapselect_question();
|
||||
$gapselect->shufflechoices = false;
|
||||
$gapselect->init_first_step(new question_attempt_step());
|
||||
$gapselect->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array('p1' => '1', 'p2' => '1', 'p3' => '1'),
|
||||
$gapselect->get_correct_response());
|
||||
@@ -148,7 +148,7 @@ class qtype_gapselect_question_test extends UnitTestCase {
|
||||
public function test_get_correct_response_maths() {
|
||||
$gapselect = qtype_gapselect_test_helper::make_a_maths_gapselect_question();
|
||||
$gapselect->shufflechoices = false;
|
||||
$gapselect->init_first_step(new question_attempt_step());
|
||||
$gapselect->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2'),
|
||||
$gapselect->get_correct_response());
|
||||
@@ -156,7 +156,7 @@ class qtype_gapselect_question_test extends UnitTestCase {
|
||||
|
||||
public function test_is_same_response() {
|
||||
$gapselect = qtype_gapselect_test_helper::make_a_gapselect_question();
|
||||
$gapselect->init_first_step(new question_attempt_step());
|
||||
$gapselect->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertTrue($gapselect->is_same_response(
|
||||
array(),
|
||||
@@ -180,7 +180,7 @@ class qtype_gapselect_question_test extends UnitTestCase {
|
||||
}
|
||||
public function test_is_complete_response() {
|
||||
$gapselect = qtype_gapselect_test_helper::make_a_gapselect_question();
|
||||
$gapselect->init_first_step(new question_attempt_step());
|
||||
$gapselect->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertFalse($gapselect->is_complete_response(array()));
|
||||
$this->assertFalse($gapselect->is_complete_response(
|
||||
@@ -192,7 +192,7 @@ class qtype_gapselect_question_test extends UnitTestCase {
|
||||
|
||||
public function test_is_gradable_response() {
|
||||
$gapselect = qtype_gapselect_test_helper::make_a_gapselect_question();
|
||||
$gapselect->init_first_step(new question_attempt_step());
|
||||
$gapselect->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertFalse($gapselect->is_gradable_response(array()));
|
||||
$this->assertFalse($gapselect->is_gradable_response(
|
||||
@@ -207,7 +207,7 @@ class qtype_gapselect_question_test extends UnitTestCase {
|
||||
public function test_grading() {
|
||||
$gapselect = qtype_gapselect_test_helper::make_a_gapselect_question();
|
||||
$gapselect->shufflechoices = false;
|
||||
$gapselect->init_first_step(new question_attempt_step());
|
||||
$gapselect->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array(1, question_state::$gradedright),
|
||||
$gapselect->grade_response(array('p1' => '1', 'p2' => '1', 'p3' => '1')));
|
||||
@@ -220,7 +220,7 @@ class qtype_gapselect_question_test extends UnitTestCase {
|
||||
public function test_grading_maths() {
|
||||
$gapselect = qtype_gapselect_test_helper::make_a_maths_gapselect_question();
|
||||
$gapselect->shufflechoices = false;
|
||||
$gapselect->init_first_step(new question_attempt_step());
|
||||
$gapselect->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array(1, question_state::$gradedright),
|
||||
$gapselect->grade_response(array('p1' => '1', 'p2' => '2', 'p3' => '1', 'p4' => '2')));
|
||||
@@ -233,7 +233,7 @@ class qtype_gapselect_question_test extends UnitTestCase {
|
||||
public function test_classify_response() {
|
||||
$gapselect = qtype_gapselect_test_helper::make_a_gapselect_question();
|
||||
$gapselect->shufflechoices = false;
|
||||
$gapselect->init_first_step(new question_attempt_step());
|
||||
$gapselect->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array(
|
||||
1 => new question_classified_response(1, 'quick', 1),
|
||||
|
||||
@@ -57,20 +57,30 @@ class qtype_match_question extends question_graded_automatically_with_countback
|
||||
/** @var array shuffled choice indexes. */
|
||||
protected $choiceorder;
|
||||
|
||||
public function init_first_step(question_attempt_step $step) {
|
||||
if ($step->has_qt_var('_stemorder')) {
|
||||
$this->stemorder = explode(',', $step->get_qt_var('_stemorder'));
|
||||
$choiceorder = explode(',', $step->get_qt_var('_choiceorder'));
|
||||
} else {
|
||||
$this->stemorder = array_keys($this->stems);
|
||||
$choiceorder = array_keys($this->choices);
|
||||
if ($this->shufflestems) {
|
||||
shuffle($this->stemorder);
|
||||
}
|
||||
shuffle($choiceorder);
|
||||
$step->set_qt_var('_stemorder', implode(',', $this->stemorder));
|
||||
$step->set_qt_var('_choiceorder', implode(',', $choiceorder));
|
||||
public function start_attempt(question_attempt_step $step) {
|
||||
$this->stemorder = array_keys($this->stems);
|
||||
if ($this->shufflestems) {
|
||||
shuffle($this->stemorder);
|
||||
}
|
||||
$step->set_qt_var('_stemorder', implode(',', $this->stemorder));
|
||||
|
||||
$choiceorder = array_keys($this->choices);
|
||||
shuffle($choiceorder);
|
||||
$step->set_qt_var('_choiceorder', implode(',', $choiceorder));
|
||||
$this->set_choiceorder($choiceorder);
|
||||
}
|
||||
|
||||
public function apply_attempt_state(question_attempt_step $step) {
|
||||
$this->stemorder = explode(',', $step->get_qt_var('_stemorder'));
|
||||
$this->set_choiceorder(explode(',', $step->get_qt_var('_choiceorder')));
|
||||
}
|
||||
|
||||
/**
|
||||
* Helper method used by both {@link start_attempt()} and
|
||||
* {@link apply_attempt_state()}.
|
||||
* @param array $choiceorder the choices, in order.
|
||||
*/
|
||||
protected function set_choiceorder($choiceorder) {
|
||||
$this->choiceorder = array();
|
||||
foreach ($choiceorder as $key => $value) {
|
||||
$this->choiceorder[$key + 1] = $value;
|
||||
|
||||
@@ -40,7 +40,7 @@ class qtype_match_question_test extends UnitTestCase {
|
||||
|
||||
public function test_get_expected_data() {
|
||||
$question = test_question_maker::make_a_matching_question();
|
||||
$question->init_first_step(new question_attempt_step());
|
||||
$question->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array('sub0' => PARAM_INT, 'sub1' => PARAM_INT,
|
||||
'sub2' => PARAM_INT, 'sub3' => PARAM_INT), $question->get_expected_data());
|
||||
@@ -48,7 +48,7 @@ class qtype_match_question_test extends UnitTestCase {
|
||||
|
||||
public function test_is_complete_response() {
|
||||
$question = test_question_maker::make_a_matching_question();
|
||||
$question->init_first_step(new question_attempt_step());
|
||||
$question->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertFalse($question->is_complete_response(array()));
|
||||
$this->assertFalse($question->is_complete_response(
|
||||
@@ -60,7 +60,7 @@ class qtype_match_question_test extends UnitTestCase {
|
||||
|
||||
public function test_is_gradable_response() {
|
||||
$question = test_question_maker::make_a_matching_question();
|
||||
$question->init_first_step(new question_attempt_step());
|
||||
$question->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertFalse($question->is_gradable_response(array()));
|
||||
$this->assertFalse($question->is_gradable_response(
|
||||
@@ -74,7 +74,7 @@ class qtype_match_question_test extends UnitTestCase {
|
||||
|
||||
public function test_is_same_response() {
|
||||
$question = test_question_maker::make_a_matching_question();
|
||||
$question->init_first_step(new question_attempt_step());
|
||||
$question->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertTrue($question->is_same_response(
|
||||
array(),
|
||||
@@ -100,7 +100,7 @@ class qtype_match_question_test extends UnitTestCase {
|
||||
public function test_grading() {
|
||||
$question = test_question_maker::make_a_matching_question();
|
||||
$question->shufflestems = false;
|
||||
$question->init_first_step(new question_attempt_step());
|
||||
$question->start_attempt(new question_attempt_step());
|
||||
|
||||
$choiceorder = $question->get_choice_order();
|
||||
$orderforchoice = array_combine(array_values($choiceorder), array_keys($choiceorder));
|
||||
@@ -117,7 +117,7 @@ class qtype_match_question_test extends UnitTestCase {
|
||||
public function test_get_correct_response() {
|
||||
$question = test_question_maker::make_a_matching_question();
|
||||
$question->shufflestems = false;
|
||||
$question->init_first_step(new question_attempt_step());
|
||||
$question->start_attempt(new question_attempt_step());
|
||||
|
||||
$choiceorder = $question->get_choice_order();
|
||||
$orderforchoice = array_combine(array_values($choiceorder), array_keys($choiceorder));
|
||||
@@ -128,7 +128,7 @@ class qtype_match_question_test extends UnitTestCase {
|
||||
|
||||
public function test_get_question_summary() {
|
||||
$match = test_question_maker::make_a_matching_question();
|
||||
$match->init_first_step(new question_attempt_step());
|
||||
$match->start_attempt(new question_attempt_step());
|
||||
$qsummary = $match->get_question_summary();
|
||||
$this->assertPattern('/' . preg_quote($match->questiontext) . '/', $qsummary);
|
||||
foreach ($match->stems as $stem) {
|
||||
@@ -142,7 +142,7 @@ class qtype_match_question_test extends UnitTestCase {
|
||||
public function test_summarise_response() {
|
||||
$match = test_question_maker::make_a_matching_question();
|
||||
$match->shufflestems = false;
|
||||
$match->init_first_step(new question_attempt_step());
|
||||
$match->start_attempt(new question_attempt_step());
|
||||
|
||||
$summary = $match->summarise_response(array('sub0' => 2, 'sub1' => 1));
|
||||
|
||||
@@ -152,7 +152,7 @@ class qtype_match_question_test extends UnitTestCase {
|
||||
public function test_classify_response() {
|
||||
$match = test_question_maker::make_a_matching_question();
|
||||
$match->shufflestems = false;
|
||||
$match->init_first_step(new question_attempt_step());
|
||||
$match->start_attempt(new question_attempt_step());
|
||||
|
||||
$choiceorder = $match->get_choice_order();
|
||||
$orderforchoice = array_combine(array_values($choiceorder), array_keys($choiceorder));
|
||||
|
||||
@@ -76,7 +76,8 @@ class qtype_missingtype_question extends question_definition implements question
|
||||
return array();
|
||||
}
|
||||
|
||||
public function init_first_step(question_attempt_step $step) {
|
||||
public function start_attempt(question_attempt_step $step) {
|
||||
throw new coding_exception('This question is of a type that is not installed on your system. No processing is possible.');
|
||||
}
|
||||
|
||||
public function grade_response(array $response) {
|
||||
|
||||
@@ -55,16 +55,16 @@ abstract class qtype_multichoice_base extends question_graded_automatically {
|
||||
|
||||
protected $order = null;
|
||||
|
||||
public function init_first_step(question_attempt_step $step) {
|
||||
if ($step->has_qt_var('_order')) {
|
||||
$this->order = explode(',', $step->get_qt_var('_order'));
|
||||
} else {
|
||||
$this->order = array_keys($this->answers);
|
||||
if ($this->shuffleanswers) {
|
||||
shuffle($this->order);
|
||||
}
|
||||
$step->set_qt_var('_order', implode(',', $this->order));
|
||||
public function start_attempt(question_attempt_step $step) {
|
||||
$this->order = array_keys($this->answers);
|
||||
if ($this->shuffleanswers) {
|
||||
shuffle($this->order);
|
||||
}
|
||||
$step->set_qt_var('_order', implode(',', $this->order));
|
||||
}
|
||||
|
||||
public function apply_attempt_state(question_attempt_step $step) {
|
||||
$this->order = explode(',', $step->get_qt_var('_order'));
|
||||
}
|
||||
|
||||
public function get_question_summary() {
|
||||
|
||||
@@ -62,7 +62,7 @@ class qtype_multichoice_single_question_test extends UnitTestCase {
|
||||
public function test_grading() {
|
||||
$question = test_question_maker::make_a_multichoice_single_question();
|
||||
$question->shuffleanswers = false;
|
||||
$question->init_first_step(new question_attempt_step());
|
||||
$question->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array(1, question_state::$gradedright),
|
||||
$question->grade_response(array('answer' => 0)));
|
||||
@@ -95,7 +95,7 @@ class qtype_multichoice_single_question_test extends UnitTestCase {
|
||||
16 => new question_answer(16, '6', -1, '', FORMAT_HTML),
|
||||
);
|
||||
|
||||
$mc->init_first_step(new question_attempt_step());
|
||||
$mc->start_attempt(new question_attempt_step());
|
||||
|
||||
list($grade, $state) = $mc->grade_response(
|
||||
array('choice0' => 1, 'choice2' => 1, 'choice4' => 1));
|
||||
@@ -106,7 +106,7 @@ class qtype_multichoice_single_question_test extends UnitTestCase {
|
||||
public function test_get_correct_response() {
|
||||
$question = test_question_maker::make_a_multichoice_single_question();
|
||||
$question->shuffleanswers = false;
|
||||
$question->init_first_step(new question_attempt_step());
|
||||
$question->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array('answer' => 0),
|
||||
$question->get_correct_response());
|
||||
@@ -115,7 +115,7 @@ class qtype_multichoice_single_question_test extends UnitTestCase {
|
||||
public function test_summarise_response() {
|
||||
$mc = test_question_maker::make_a_multichoice_single_question();
|
||||
$mc->shuffleanswers = false;
|
||||
$mc->init_first_step(new question_attempt_step());
|
||||
$mc->start_attempt(new question_attempt_step());
|
||||
|
||||
$summary = $mc->summarise_response(array('answer' => 0),
|
||||
test_question_maker::get_a_qa($mc));
|
||||
@@ -126,7 +126,7 @@ class qtype_multichoice_single_question_test extends UnitTestCase {
|
||||
public function test_classify_response() {
|
||||
$mc = test_question_maker::make_a_multichoice_single_question();
|
||||
$mc->shuffleanswers = false;
|
||||
$mc->init_first_step(new question_attempt_step());
|
||||
$mc->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array(
|
||||
$mc->id => new question_classified_response(14, 'B', -0.3333333),
|
||||
@@ -149,7 +149,7 @@ class qtype_multichoice_multi_question_test extends UnitTestCase {
|
||||
|
||||
public function test_get_expected_data() {
|
||||
$question = test_question_maker::make_a_multichoice_multi_question();
|
||||
$question->init_first_step(new question_attempt_step());
|
||||
$question->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array('choice0' => PARAM_BOOL, 'choice1' => PARAM_BOOL,
|
||||
'choice2' => PARAM_BOOL, 'choice3' => PARAM_BOOL), $question->get_expected_data());
|
||||
@@ -157,7 +157,7 @@ class qtype_multichoice_multi_question_test extends UnitTestCase {
|
||||
|
||||
public function test_is_complete_response() {
|
||||
$question = test_question_maker::make_a_multichoice_multi_question();
|
||||
$question->init_first_step(new question_attempt_step());
|
||||
$question->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertFalse($question->is_complete_response(array()));
|
||||
$this->assertFalse($question->is_complete_response(
|
||||
@@ -169,7 +169,7 @@ class qtype_multichoice_multi_question_test extends UnitTestCase {
|
||||
|
||||
public function test_is_gradable_response() {
|
||||
$question = test_question_maker::make_a_multichoice_multi_question();
|
||||
$question->init_first_step(new question_attempt_step());
|
||||
$question->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertFalse($question->is_gradable_response(array()));
|
||||
$this->assertFalse($question->is_gradable_response(
|
||||
@@ -182,7 +182,7 @@ class qtype_multichoice_multi_question_test extends UnitTestCase {
|
||||
public function test_grading() {
|
||||
$question = test_question_maker::make_a_multichoice_multi_question();
|
||||
$question->shuffleanswers = false;
|
||||
$question->init_first_step(new question_attempt_step());
|
||||
$question->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array(1, question_state::$gradedright),
|
||||
$question->grade_response(array('choice0' => '1', 'choice2' => '1')));
|
||||
@@ -197,7 +197,7 @@ class qtype_multichoice_multi_question_test extends UnitTestCase {
|
||||
public function test_get_correct_response() {
|
||||
$question = test_question_maker::make_a_multichoice_multi_question();
|
||||
$question->shuffleanswers = false;
|
||||
$question->init_first_step(new question_attempt_step());
|
||||
$question->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array('choice0' => '1', 'choice2' => '1'),
|
||||
$question->get_correct_response());
|
||||
@@ -205,7 +205,7 @@ class qtype_multichoice_multi_question_test extends UnitTestCase {
|
||||
|
||||
public function test_get_question_summary() {
|
||||
$mc = test_question_maker::make_a_multichoice_single_question();
|
||||
$mc->init_first_step(new question_attempt_step());
|
||||
$mc->start_attempt(new question_attempt_step());
|
||||
|
||||
$qsummary = $mc->get_question_summary();
|
||||
|
||||
@@ -218,7 +218,7 @@ class qtype_multichoice_multi_question_test extends UnitTestCase {
|
||||
public function test_summarise_response() {
|
||||
$mc = test_question_maker::make_a_multichoice_multi_question();
|
||||
$mc->shuffleanswers = false;
|
||||
$mc->init_first_step(new question_attempt_step());
|
||||
$mc->start_attempt(new question_attempt_step());
|
||||
|
||||
$summary = $mc->summarise_response(array('choice1' => 1, 'choice2' => 1),
|
||||
test_question_maker::get_a_qa($mc));
|
||||
@@ -229,7 +229,7 @@ class qtype_multichoice_multi_question_test extends UnitTestCase {
|
||||
public function test_classify_response() {
|
||||
$mc = test_question_maker::make_a_multichoice_multi_question();
|
||||
$mc->shuffleanswers = false;
|
||||
$mc->init_first_step(new question_attempt_step());
|
||||
$mc->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array(
|
||||
13 => new question_classified_response(13, 'A', 0.5),
|
||||
|
||||
@@ -51,14 +51,14 @@ class qtype_numerical_question extends question_graded_by_strategy
|
||||
return array('answer' => PARAM_RAW_TRIMMED);
|
||||
}
|
||||
|
||||
public function init_first_step(question_attempt_step $step) {
|
||||
if ($step->has_qt_var('_separators')) {
|
||||
list($point, $separator) = explode('$', $step->get_qt_var('_separators'));
|
||||
$this->ap->set_characters($point, $separator);
|
||||
} else {
|
||||
$step->set_qt_var('_separators',
|
||||
$this->ap->get_point() . '$' . $this->ap->get_separator());
|
||||
}
|
||||
public function start_attempt(question_attempt_step $step) {
|
||||
$step->set_qt_var('_separators',
|
||||
$this->ap->get_point() . '$' . $this->ap->get_separator());
|
||||
}
|
||||
|
||||
public function apply_attempt_state(question_attempt_step $step) {
|
||||
list($point, $separator) = explode('$', $step->get_qt_var('_separators'));
|
||||
$this->ap->set_characters($point, $separator);
|
||||
}
|
||||
|
||||
public function summarise_response(array $response) {
|
||||
|
||||
@@ -49,7 +49,7 @@ class qtype_oumultiresponse_question_test extends UnitTestCase {
|
||||
public function test_grade_responses_right_right() {
|
||||
$mc = qtype_oumultiresponse_test_helper::make_an_oumultiresponse_two_of_four();
|
||||
$mc->shuffleanswers = false;
|
||||
$mc->init_first_step(new question_attempt_step());
|
||||
$mc->start_attempt(new question_attempt_step());
|
||||
|
||||
list($fraction, $state) = $mc->grade_response(array('choice0' => '1', 'choice2' => '1'));
|
||||
$this->assertWithinMargin(1, $fraction, $this->tolerance);
|
||||
@@ -59,7 +59,7 @@ class qtype_oumultiresponse_question_test extends UnitTestCase {
|
||||
public function test_grade_responses_right() {
|
||||
$mc = qtype_oumultiresponse_test_helper::make_an_oumultiresponse_two_of_four();
|
||||
$mc->shuffleanswers = false;
|
||||
$mc->init_first_step(new question_attempt_step());
|
||||
$mc->start_attempt(new question_attempt_step());
|
||||
|
||||
list($fraction, $state) = $mc->grade_response(array('choice0' => '1'));
|
||||
$this->assertWithinMargin(0.5, $fraction, $this->tolerance);
|
||||
@@ -69,7 +69,7 @@ class qtype_oumultiresponse_question_test extends UnitTestCase {
|
||||
public function test_grade_responses_wrong_wrong() {
|
||||
$mc = qtype_oumultiresponse_test_helper::make_an_oumultiresponse_two_of_four();
|
||||
$mc->shuffleanswers = false;
|
||||
$mc->init_first_step(new question_attempt_step());
|
||||
$mc->start_attempt(new question_attempt_step());
|
||||
|
||||
list($fraction, $state) = $mc->grade_response(array('choice1' => '1', 'choice3' => '1'));
|
||||
$this->assertWithinMargin(0, $fraction, $this->tolerance);
|
||||
@@ -79,7 +79,7 @@ class qtype_oumultiresponse_question_test extends UnitTestCase {
|
||||
public function test_grade_responses_right_wrong_wrong() {
|
||||
$mc = qtype_oumultiresponse_test_helper::make_an_oumultiresponse_two_of_four();
|
||||
$mc->shuffleanswers = false;
|
||||
$mc->init_first_step(new question_attempt_step());
|
||||
$mc->start_attempt(new question_attempt_step());
|
||||
|
||||
list($fraction, $state) = $mc->grade_response(array('choice0' => '1', 'choice1' => '1', 'choice3' => '1'));
|
||||
$this->assertWithinMargin(0, $fraction, $this->tolerance);
|
||||
@@ -89,7 +89,7 @@ class qtype_oumultiresponse_question_test extends UnitTestCase {
|
||||
public function test_grade_responses_right_wrong() {
|
||||
$mc = qtype_oumultiresponse_test_helper::make_an_oumultiresponse_two_of_four();
|
||||
$mc->shuffleanswers = false;
|
||||
$mc->init_first_step(new question_attempt_step());
|
||||
$mc->start_attempt(new question_attempt_step());
|
||||
|
||||
list($fraction, $state) = $mc->grade_response(array('choice0' => '1', 'choice1' => '1'));
|
||||
$this->assertWithinMargin(0.5, $fraction, $this->tolerance);
|
||||
@@ -99,7 +99,7 @@ class qtype_oumultiresponse_question_test extends UnitTestCase {
|
||||
public function test_grade_responses_right_right_wrong() {
|
||||
$mc = qtype_oumultiresponse_test_helper::make_an_oumultiresponse_two_of_four();
|
||||
$mc->shuffleanswers = false;
|
||||
$mc->init_first_step(new question_attempt_step());
|
||||
$mc->start_attempt(new question_attempt_step());
|
||||
|
||||
list($fraction, $state) = $mc->grade_response(array(
|
||||
'choice0' => '1', 'choice2' => '1', 'choice3' => '1'));
|
||||
@@ -110,7 +110,7 @@ class qtype_oumultiresponse_question_test extends UnitTestCase {
|
||||
public function test_grade_responses_right_right_wrong_wrong() {
|
||||
$mc = qtype_oumultiresponse_test_helper::make_an_oumultiresponse_two_of_four();
|
||||
$mc->shuffleanswers = false;
|
||||
$mc->init_first_step(new question_attempt_step());
|
||||
$mc->start_attempt(new question_attempt_step());
|
||||
|
||||
list($fraction, $state) = $mc->grade_response(array(
|
||||
'choice0' => '1', 'choice1' => '1', 'choice2' => '1', 'choice3' => '1'));
|
||||
|
||||
@@ -146,15 +146,36 @@ abstract class question_definition {
|
||||
}
|
||||
|
||||
/**
|
||||
* Initialise the first step of an attempt at this quetsion.
|
||||
* Start a new attempt at this question, storing any information that will
|
||||
* be needed later in the step.
|
||||
*
|
||||
* For example, the multiple choice question type uses this method to
|
||||
* randomly shuffle the choices, if that option has been set in the question.
|
||||
* It then stores that order by calling $step->set_qt_var(...).
|
||||
* This is where the question can do any initialisation required on a
|
||||
* per-attempt basis. For example, this is where the multiple choice
|
||||
* question type randomly shuffles the choices (if that option is set).
|
||||
*
|
||||
* @param question_attempt_step $step the step to be initialised.
|
||||
* Any information about how the question has been set up for this attempt
|
||||
* should be stored in the $step, by calling $step->set_qt_var(...).
|
||||
*
|
||||
* @param question_attempt_step The first step of the {@link question_attempt}
|
||||
* being started. Can be used to store state.
|
||||
*/
|
||||
public function init_first_step(question_attempt_step $step) {
|
||||
public function start_attempt(question_attempt_step $step) {
|
||||
}
|
||||
|
||||
/**
|
||||
* When an in-progress {@link question_attempt} is re-loaded from the
|
||||
* database, this method is called so that the question can re-initialise
|
||||
* its internal state as needed by this attempt.
|
||||
*
|
||||
* For example, the multiple choice question type needs to set the order
|
||||
* of the choices to the order that was set up when start_attempt was called
|
||||
* originally. All the information required to do this should be in the
|
||||
* $step object, which is the first step of the question_attempt being loaded.
|
||||
*
|
||||
* @param question_attempt_step The first step of the {@link question_attempt}
|
||||
* being loaded.
|
||||
*/
|
||||
public function apply_attempt_state(question_attempt_step $step) {
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -132,7 +132,7 @@ class qtype_shortanswer_question_test extends UnitTestCase {
|
||||
|
||||
public function test_classify_response() {
|
||||
$sa = test_question_maker::make_a_shortanswer_question();
|
||||
$sa->init_first_step(new question_attempt_step());
|
||||
$sa->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array(
|
||||
new question_classified_response(13, 'frog', 1.0)),
|
||||
|
||||
@@ -93,7 +93,7 @@ class qtype_truefalse_question_test extends UnitTestCase {
|
||||
|
||||
public function test_classify_response() {
|
||||
$tf = test_question_maker::make_a_truefalse_question();
|
||||
$tf->init_first_step(new question_attempt_step());
|
||||
$tf->start_attempt(new question_attempt_step());
|
||||
|
||||
$this->assertEqual(array(
|
||||
$tf->id => new question_classified_response(0, get_string('false', 'qtype_truefalse'), 0.0)),
|
||||
|
||||
Reference in New Issue
Block a user