mirror of
https://github.com/moodle/moodle.git
synced 2025-01-19 06:18:28 +01:00
Merge branch 'MDL-69823-master' of git://github.com/jleyva/moodle
This commit is contained in:
commit
e7ea4ae450
@ -902,6 +902,7 @@ class mod_quiz_external extends external_api {
|
||||
It will be returned only if the user is allowed to see it.', VALUE_OPTIONAL),
|
||||
'maxmark' => new external_value(PARAM_FLOAT, 'the maximum mark possible for this question attempt.
|
||||
It will be returned only if the user is allowed to see it.', VALUE_OPTIONAL),
|
||||
'settings' => new external_value(PARAM_RAW, 'Question settings (JSON encoded).', VALUE_OPTIONAL),
|
||||
),
|
||||
'The question data. Some fields may not be returned depending on the quiz display settings.'
|
||||
);
|
||||
@ -927,6 +928,7 @@ class mod_quiz_external extends external_api {
|
||||
foreach ($attemptobj->get_slots($page) as $slot) {
|
||||
$qtype = $attemptobj->get_question_type_name($slot);
|
||||
$qattempt = $attemptobj->get_question_attempt($slot);
|
||||
$questiondef = $qattempt->get_question(true);
|
||||
|
||||
// Get response files (for questions like essay that allows attachments).
|
||||
$responsefileareas = [];
|
||||
@ -948,6 +950,9 @@ class mod_quiz_external extends external_api {
|
||||
}
|
||||
}
|
||||
|
||||
// Check display settings for question.
|
||||
$settings = $questiondef->get_question_definition_for_external_rendering($qattempt, $displayoptions);
|
||||
|
||||
$question = array(
|
||||
'slot' => $slot,
|
||||
'type' => $qtype,
|
||||
@ -957,7 +962,8 @@ class mod_quiz_external extends external_api {
|
||||
'responsefileareas' => $responsefileareas,
|
||||
'sequencecheck' => $qattempt->get_sequence_check_count(),
|
||||
'lastactiontime' => $qattempt->get_last_step()->get_timecreated(),
|
||||
'hasautosavedstep' => $qattempt->has_autosaved_step()
|
||||
'hasautosavedstep' => $qattempt->has_autosaved_step(),
|
||||
'settings' => !empty($settings) ? json_encode($settings) : null,
|
||||
);
|
||||
|
||||
if ($attemptobj->is_real_question($slot)) {
|
||||
|
@ -1034,6 +1034,11 @@ class mod_quiz_external_testcase extends externallib_advanced_testcase {
|
||||
$this->assertEquals(false, $result['questions'][0]['hasautosavedstep']);
|
||||
$this->assertEquals(false, $result['questions'][1]['hasautosavedstep']);
|
||||
|
||||
// Check question options.
|
||||
$this->assertNotEmpty(5, $result['questions'][0]['settings']);
|
||||
// Check at least some settings returned.
|
||||
$this->assertCount(4, (array) json_decode($result['questions'][0]['settings']));
|
||||
|
||||
// Submit a response for the first question.
|
||||
$tosubmit = array(1 => array('answer' => '3.14'));
|
||||
$attemptobj->process_submitted_actions(time(), false, $tosubmit);
|
||||
|
@ -1,5 +1,11 @@
|
||||
This files describes API changes in the quiz code.
|
||||
|
||||
=== 3.10.1 ===
|
||||
|
||||
* External functions mod_quiz_external::get_attempt_data, mod_quiz_external::get_attempt_summary
|
||||
and mod_quiz_external::get_attempt_review now return a new additional optional field:
|
||||
- settings: Containing the question definition settings for displaying the question in an external system.
|
||||
|
||||
=== 3.10 ===
|
||||
|
||||
* External functions mod_quiz_external::get_attempt_data, mod_quiz_external::get_attempt_summary
|
||||
|
@ -178,4 +178,23 @@ class qtype_calculated_question_test extends advanced_testcase {
|
||||
$this->assertEquals('category' . $question->category,
|
||||
$question->get_variants_selection_seed());
|
||||
}
|
||||
|
||||
/**
|
||||
* test_get_question_definition_for_external_rendering
|
||||
*/
|
||||
public function test_get_question_definition_for_external_rendering() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$question = test_question_maker::make_question('calculated');
|
||||
$question->start_attempt(new question_attempt_step(), 1);
|
||||
$qa = test_question_maker::get_a_qa($question);
|
||||
$displayoptions = new question_display_options();
|
||||
|
||||
$options = $question->get_question_definition_for_external_rendering($qa, $displayoptions);
|
||||
$this->assertNotEmpty($options);
|
||||
$this->assertEquals(0, $options['unitgradingtype']);
|
||||
$this->assertEquals(0, $options['unitpenalty']);
|
||||
$this->assertEquals(qtype_numerical::UNITNONE, $options['unitdisplay']);
|
||||
$this->assertEmpty($options['unitsleft']);
|
||||
}
|
||||
}
|
||||
|
@ -185,4 +185,31 @@ class qtype_essay_question extends question_with_responses {
|
||||
$filearea, $args, $forcedownload);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the question settings that define this question as structured data.
|
||||
*
|
||||
* @param question_attempt $qa the current attempt for which we are exporting the settings.
|
||||
* @param question_display_options $options the question display options which say which aspects of the question
|
||||
* should be visible.
|
||||
* @return mixed structure representing the question settings. In web services, this will be JSON-encoded.
|
||||
*/
|
||||
public function get_question_definition_for_external_rendering(question_attempt $qa, question_display_options $options) {
|
||||
// This is a partial implementation, returning only the most relevant question settings for now,
|
||||
// ideally, we should return as much as settings as possible (depending on the state and display options).
|
||||
|
||||
$settings = [
|
||||
'responseformat' => $this->responseformat,
|
||||
'responserequired' => $this->responserequired,
|
||||
'responsefieldlines' => $this->responsefieldlines,
|
||||
'attachments' => $this->attachments,
|
||||
'attachmentsrequired' => $this->attachmentsrequired,
|
||||
'maxbytes' => $this->maxbytes,
|
||||
'filetypeslist' => $this->filetypeslist,
|
||||
'responsetemplate' => $this->responsetemplate,
|
||||
'responsetemplateformat' => $this->responsetemplateformat,
|
||||
];
|
||||
|
||||
return $settings;
|
||||
}
|
||||
}
|
||||
|
@ -239,4 +239,27 @@ class qtype_essay_question_test extends advanced_testcase {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* test_get_question_definition_for_external_rendering
|
||||
*/
|
||||
public function test_get_question_definition_for_external_rendering() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$essay = test_question_maker::make_an_essay_question();
|
||||
$essay->start_attempt(new question_attempt_step(), 1);
|
||||
$qa = test_question_maker::get_a_qa($essay);
|
||||
$displayoptions = new question_display_options();
|
||||
|
||||
$options = $essay->get_question_definition_for_external_rendering($qa, $displayoptions);
|
||||
$this->assertNotEmpty($options);
|
||||
$this->assertEquals('editor', $options['responseformat']);
|
||||
$this->assertEquals(1, $options['responserequired']);
|
||||
$this->assertEquals(15, $options['responsefieldlines']);
|
||||
$this->assertEquals(0, $options['attachments']);
|
||||
$this->assertEquals(0, $options['attachmentsrequired']);
|
||||
$this->assertNull($options['maxbytes']);
|
||||
$this->assertNull($options['filetypeslist']);
|
||||
$this->assertEquals('', $options['responsetemplate']);
|
||||
$this->assertEquals(FORMAT_MOODLE, $options['responsetemplateformat']);
|
||||
}
|
||||
}
|
||||
|
@ -331,4 +331,21 @@ abstract class qtype_gapselect_question_base extends question_graded_automatical
|
||||
$args, $forcedownload);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the question settings that define this question as structured data.
|
||||
*
|
||||
* @param question_attempt $qa the current attempt for which we are exporting the settings.
|
||||
* @param question_display_options $options the question display options which say which aspects of the question
|
||||
* should be visible.
|
||||
* @return mixed structure representing the question settings. In web services, this will be JSON-encoded.
|
||||
*/
|
||||
public function get_question_definition_for_external_rendering(question_attempt $qa, question_display_options $options) {
|
||||
// This is a partial implementation, returning only the most relevant question settings for now,
|
||||
// ideally, we should return as much as settings as possible (depending on the state and display options).
|
||||
|
||||
return [
|
||||
'shufflechoices' => $this->shufflechoices,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -246,4 +246,17 @@ class qtype_gapselect_question_test extends basic_testcase {
|
||||
3 => new question_classified_response(2, 'assiduous', 0),
|
||||
), $gapselect->classify_response(array('p1' => '0', 'p2' => '1', 'p3' => '2')));
|
||||
}
|
||||
|
||||
/**
|
||||
* test_get_question_definition_for_external_rendering
|
||||
*/
|
||||
public function test_get_question_definition_for_external_rendering() {
|
||||
$question = test_question_maker::make_question('gapselect', 'maths');
|
||||
$question->start_attempt(new question_attempt_step(), 1);
|
||||
$qa = test_question_maker::get_a_qa($question);
|
||||
$displayoptions = new question_display_options();
|
||||
|
||||
$options = $question->get_question_definition_for_external_rendering($qa, $displayoptions);
|
||||
$this->assertEquals(1, $options['shufflechoices']);
|
||||
}
|
||||
}
|
||||
|
@ -354,4 +354,21 @@ class qtype_match_question extends question_graded_automatically_with_countback
|
||||
$args, $forcedownload);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the question settings that define this question as structured data.
|
||||
*
|
||||
* @param question_attempt $qa the current attempt for which we are exporting the settings.
|
||||
* @param question_display_options $options the question display options which say which aspects of the question
|
||||
* should be visible.
|
||||
* @return mixed structure representing the question settings. In web services, this will be JSON-encoded.
|
||||
*/
|
||||
public function get_question_definition_for_external_rendering(question_attempt $qa, question_display_options $options) {
|
||||
// This is a partial implementation, returning only the most relevant question settings for now,
|
||||
// ideally, we should return as much as settings as possible (depending on the state and display options).
|
||||
|
||||
return [
|
||||
'shufflestems' => $this->shufflestems,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -233,4 +233,16 @@ class qtype_match_question_test extends advanced_testcase {
|
||||
$this->assertEquals(array(4, 4), $m->get_num_parts_right($postdata));
|
||||
}
|
||||
|
||||
/**
|
||||
* test_get_question_definition_for_external_rendering
|
||||
*/
|
||||
public function test_get_question_definition_for_external_rendering() {
|
||||
$question = test_question_maker::make_question('match');
|
||||
$question->start_attempt(new question_attempt_step(), 1);
|
||||
$qa = test_question_maker::get_a_qa($question);
|
||||
$displayoptions = new question_display_options();
|
||||
|
||||
$options = $question->get_question_definition_for_external_rendering($qa, $displayoptions);
|
||||
$this->assertEquals(1, $options['shufflestems']);
|
||||
}
|
||||
}
|
||||
|
@ -355,4 +355,19 @@ class qtype_multianswer_question extends question_graded_automatically_with_coun
|
||||
$args, $forcedownload);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the question settings that define this question as structured data.
|
||||
*
|
||||
* @param question_attempt $qa the current attempt for which we are exporting the settings.
|
||||
* @param question_display_options $options the question display options which say which aspects of the question
|
||||
* should be visible.
|
||||
* @return mixed structure representing the question settings. In web services, this will be JSON-encoded.
|
||||
*/
|
||||
public function get_question_definition_for_external_rendering(question_attempt $qa, question_display_options $options) {
|
||||
// Empty implementation for now in order to avoid debugging in core questions (generated in the parent class),
|
||||
// ideally, we should return as much as settings as possible (depending on the state and display options).
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -238,4 +238,19 @@ class qtype_multianswer_question_test extends advanced_testcase {
|
||||
$finalgrade = $question->compute_final_grade($responses, 1);
|
||||
$this->assertEquals(1 / 3 * (1 - 3 * 0.2) + 2 / 3 * (1 - 2 * 0.2), $finalgrade);
|
||||
}
|
||||
|
||||
/**
|
||||
* test_get_question_definition_for_external_rendering
|
||||
*/
|
||||
public function test_get_question_definition_for_external_rendering() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$question = test_question_maker::make_question('multianswer');
|
||||
$question->start_attempt(new question_attempt_step(), 1);
|
||||
$qa = test_question_maker::get_a_qa($question);
|
||||
$displayoptions = new question_display_options();
|
||||
|
||||
$options = $question->get_question_definition_for_external_rendering($qa, $displayoptions);
|
||||
$this->assertNull($options);
|
||||
}
|
||||
}
|
||||
|
@ -147,6 +147,26 @@ abstract class qtype_multichoice_base extends question_graded_automatically {
|
||||
$args, $forcedownload);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the question settings that define this question as structured data.
|
||||
*
|
||||
* @param question_attempt $qa the current attempt for which we are exporting the settings.
|
||||
* @param question_display_options $options the question display options which say which aspects of the question
|
||||
* should be visible.
|
||||
* @return mixed structure representing the question settings. In web services, this will be JSON-encoded.
|
||||
*/
|
||||
public function get_question_definition_for_external_rendering(question_attempt $qa, question_display_options $options) {
|
||||
// This is a partial implementation, returning only the most relevant question settings for now,
|
||||
// ideally, we should return as much as settings as possible (depending on the state and display options).
|
||||
|
||||
return [
|
||||
'shuffleanswers' => $this->shuffleanswers,
|
||||
'answernumbering' => $this->answernumbering,
|
||||
'showstandardinstruction' => $this->showstandardinstruction,
|
||||
'layout' => $this->layout,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -175,4 +175,19 @@ class qtype_multichoice_multi_question_test extends advanced_testcase {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* test_get_question_definition_for_external_rendering
|
||||
*/
|
||||
public function test_get_question_definition_for_external_rendering() {
|
||||
$question = test_question_maker::make_a_multichoice_multi_question();
|
||||
$question->start_attempt(new question_attempt_step(), 1);
|
||||
$qa = test_question_maker::get_a_qa($question);
|
||||
$displayoptions = new question_display_options();
|
||||
|
||||
$options = $question->get_question_definition_for_external_rendering($qa, $displayoptions);
|
||||
$this->assertEquals(1, $options['shuffleanswers']);
|
||||
$this->assertEquals('abc', $options['answernumbering']);
|
||||
$this->assertEquals(0, $options['showstandardinstruction']);
|
||||
$this->assertEquals(1, $options['shuffleanswers']);
|
||||
}
|
||||
}
|
||||
|
@ -44,7 +44,8 @@ class qtype_numerical_question extends question_graded_automatically {
|
||||
public $unitgradingtype;
|
||||
/** @var number the penalty for a missing or unrecognised unit. */
|
||||
public $unitpenalty;
|
||||
|
||||
/** @var boolean whether the units come before or after the number */
|
||||
public $unitsleft;
|
||||
/** @var qtype_numerical_answer_processor */
|
||||
public $ap;
|
||||
|
||||
@ -318,6 +319,26 @@ class qtype_numerical_question extends question_graded_automatically {
|
||||
$args, $forcedownload);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the question settings that define this question as structured data.
|
||||
*
|
||||
* @param question_attempt $qa the current attempt for which we are exporting the settings.
|
||||
* @param question_display_options $options the question display options which say which aspects of the question
|
||||
* should be visible.
|
||||
* @return mixed structure representing the question settings. In web services, this will be JSON-encoded.
|
||||
*/
|
||||
public function get_question_definition_for_external_rendering(question_attempt $qa, question_display_options $options) {
|
||||
// This is a partial implementation, returning only the most relevant question settings for now,
|
||||
// ideally, we should return as much as settings as possible (depending on the state and display options).
|
||||
|
||||
return [
|
||||
'unitgradingtype' => $this->unitgradingtype,
|
||||
'unitpenalty' => $this->unitpenalty,
|
||||
'unitdisplay' => $this->unitdisplay,
|
||||
'unitsleft' => $this->unitsleft,
|
||||
];
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -359,6 +359,7 @@ class qtype_numerical extends question_type {
|
||||
$question->unitdisplay = $questiondata->options->showunits;
|
||||
$question->unitgradingtype = $questiondata->options->unitgradingtype;
|
||||
$question->unitpenalty = $questiondata->options->unitpenalty;
|
||||
$question->unitsleft = $questiondata->options->unitsleft;
|
||||
$question->ap = $this->make_answer_processor($questiondata->options->units,
|
||||
$questiondata->options->unitsleft);
|
||||
}
|
||||
|
@ -306,4 +306,23 @@ class qtype_numerical_question_test extends advanced_testcase {
|
||||
new question_classified_response(null, '$abc', 0.0)),
|
||||
$num->classify_response(array('answer' => '$abc')));
|
||||
}
|
||||
|
||||
/**
|
||||
* test_get_question_definition_for_external_rendering
|
||||
*/
|
||||
public function test_get_question_definition_for_external_rendering() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$question = test_question_maker::make_question('numerical', 'unit');
|
||||
$question->start_attempt(new question_attempt_step(), 1);
|
||||
$qa = test_question_maker::get_a_qa($question);
|
||||
$displayoptions = new question_display_options();
|
||||
|
||||
$options = $question->get_question_definition_for_external_rendering($qa, $displayoptions);
|
||||
$this->assertNotEmpty($options);
|
||||
$this->assertEquals(1, $options['unitgradingtype']);
|
||||
$this->assertEquals(0.5, $options['unitpenalty']);
|
||||
$this->assertEquals(qtype_numerical::UNITSELECT, $options['unitdisplay']);
|
||||
$this->assertEmpty($options['unitsleft']);
|
||||
}
|
||||
}
|
||||
|
@ -427,6 +427,31 @@ abstract class question_definition {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the question settings that define this question as structured data.
|
||||
*
|
||||
* This is used by external systems such as the Moodle mobile app, which want to display the question themselves,
|
||||
* rather than using the renderer provided.
|
||||
*
|
||||
* This method should only return the data that the student is allowed to see or know, given the current state of
|
||||
* the question. For example, do not include the 'General feedback' until the student has completed the question,
|
||||
* and even then, only include it if the question_display_options say it should be visible.
|
||||
*
|
||||
* But, within those rules, it is recommended that you return all the settings for the question,
|
||||
* to give maximum flexibility to the external system providing its own rendering of the question.
|
||||
*
|
||||
* @param question_attempt $qa the current attempt for which we are exporting the settings.
|
||||
* @param question_display_options $options the question display options which say which aspects of the question
|
||||
* should be visible.
|
||||
* @return mixed structure representing the question settings. In web services, this will be JSON-encoded.
|
||||
*/
|
||||
public function get_question_definition_for_external_rendering(question_attempt $qa, question_display_options $options) {
|
||||
|
||||
debugging('This question does not implement the get_question_definition_for_external_rendering() method yet.',
|
||||
DEBUG_DEVELOPER);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
@ -184,4 +184,17 @@ class qtype_shortanswer_question extends question_graded_by_strategy
|
||||
$args, $forcedownload);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the question settings that define this question as structured data.
|
||||
*
|
||||
* @param question_attempt $qa the current attempt for which we are exporting the settings.
|
||||
* @param question_display_options $options the question display options which say which aspects of the question
|
||||
* should be visible.
|
||||
* @return mixed structure representing the question settings. In web services, this will be JSON-encoded.
|
||||
*/
|
||||
public function get_question_definition_for_external_rendering(question_attempt $qa, question_display_options $options) {
|
||||
// No need to return anything, external clients do not need additional information for rendering this question type.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -226,4 +226,19 @@ class qtype_shortanswer_question_test extends advanced_testcase {
|
||||
question_classified_response::no_response()),
|
||||
$sa->classify_response(array('answer' => '')));
|
||||
}
|
||||
|
||||
/**
|
||||
* test_get_question_definition_for_external_rendering
|
||||
*/
|
||||
public function test_get_question_definition_for_external_rendering() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$question = test_question_maker::make_question('shortanswer');
|
||||
$question->start_attempt(new question_attempt_step(), 1);
|
||||
$qa = test_question_maker::get_a_qa($question);
|
||||
$displayoptions = new question_display_options();
|
||||
|
||||
$options = $question->get_question_definition_for_external_rendering($qa, $displayoptions);
|
||||
$this->assertNull($options);
|
||||
}
|
||||
}
|
||||
|
@ -123,4 +123,17 @@ class qtype_truefalse_question extends question_graded_automatically {
|
||||
$args, $forcedownload);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the question settings that define this question as structured data.
|
||||
*
|
||||
* @param question_attempt $qa the current attempt for which we are exporting the settings.
|
||||
* @param question_display_options $options the question display options which say which aspects of the question
|
||||
* should be visible.
|
||||
* @return mixed structure representing the question settings. In web services, this will be JSON-encoded.
|
||||
*/
|
||||
public function get_question_definition_for_external_rendering(question_attempt $qa, question_display_options $options) {
|
||||
// No need to return anything, external clients do not need additional information for rendering this question type.
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -107,4 +107,19 @@ class qtype_truefalse_question_test extends advanced_testcase {
|
||||
$tf->id => question_classified_response::no_response()),
|
||||
$tf->classify_response(array()));
|
||||
}
|
||||
|
||||
/**
|
||||
* test_get_question_definition_for_external_rendering
|
||||
*/
|
||||
public function test_get_question_definition_for_external_rendering() {
|
||||
$this->resetAfterTest();
|
||||
|
||||
$question = test_question_maker::make_question('truefalse', 'true');
|
||||
$question->start_attempt(new question_attempt_step(), 1);
|
||||
$qa = test_question_maker::get_a_qa($question);
|
||||
$displayoptions = new question_display_options();
|
||||
|
||||
$options = $question->get_question_definition_for_external_rendering($qa, $displayoptions);
|
||||
$this->assertNull($options);
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user