MDL-52830 mod_quiz: New Web Service mod_quiz_get_attempt_summary
This commit is contained in:
@@ -787,6 +787,9 @@ class mod_quiz_external extends external_api {
|
||||
|
||||
$attemptobj = quiz_attempt::create($params['attemptid']);
|
||||
|
||||
// If the attempt is now overdue, or abandoned, deal with that.
|
||||
$attemptobj->handle_if_time_expired(time(), true);
|
||||
|
||||
$context = context_module::instance($attemptobj->get_cm()->id);
|
||||
self::validate_context($context);
|
||||
|
||||
@@ -831,23 +834,27 @@ class mod_quiz_external extends external_api {
|
||||
$accessmanager->notify_preflight_check_passed($params['attemptid']);
|
||||
}
|
||||
|
||||
// Check if the page is out of range.
|
||||
if ($params['page'] != $attemptobj->force_page_number_into_range($params['page'])) {
|
||||
throw new moodle_quiz_exception($attemptobj->get_quizobj(), 'Invalid page number');
|
||||
}
|
||||
|
||||
// Prevent out of sequence access.
|
||||
if ($attemptobj->get_currentpage() != $params['page']) {
|
||||
if ($attemptobj->get_navigation_method() == QUIZ_NAVMETHOD_SEQ && $attemptobj->get_currentpage() > $params['page']) {
|
||||
throw new moodle_quiz_exception($attemptobj->get_quizobj(), 'Out of sequence access');
|
||||
if (isset($params['page'])) {
|
||||
// Check if the page is out of range.
|
||||
if ($params['page'] != $attemptobj->force_page_number_into_range($params['page'])) {
|
||||
throw new moodle_quiz_exception($attemptobj->get_quizobj(), 'Invalid page number');
|
||||
}
|
||||
}
|
||||
|
||||
// Check slots.
|
||||
$slots = $attemptobj->get_slots($params['page']);
|
||||
// Prevent out of sequence access.
|
||||
if ($attemptobj->get_currentpage() != $params['page']) {
|
||||
if ($attemptobj->get_navigation_method() == QUIZ_NAVMETHOD_SEQ &&
|
||||
$attemptobj->get_currentpage() > $params['page']) {
|
||||
|
||||
if (empty($slots)) {
|
||||
throw new moodle_quiz_exception($attemptobj->get_quizobj(), 'noquestionsfound');
|
||||
throw new moodle_quiz_exception($attemptobj->get_quizobj(), 'Out of sequence access');
|
||||
}
|
||||
}
|
||||
|
||||
// Check slots.
|
||||
$slots = $attemptobj->get_slots($params['page']);
|
||||
|
||||
if (empty($slots)) {
|
||||
throw new moodle_quiz_exception($attemptobj->get_quizobj(), 'noquestionsfound');
|
||||
}
|
||||
}
|
||||
|
||||
return array($attemptobj, $messages);
|
||||
@@ -1002,4 +1009,68 @@ class mod_quiz_external extends external_api {
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the parameters for get_attempt_summary.
|
||||
*
|
||||
* @return external_external_function_parameters
|
||||
* @since Moodle 3.1
|
||||
*/
|
||||
public static function get_attempt_summary_parameters() {
|
||||
return new external_function_parameters (
|
||||
array(
|
||||
'attemptid' => new external_value(PARAM_INT, 'attempt id'),
|
||||
'preflightdata' => new external_multiple_structure(
|
||||
new external_single_structure(
|
||||
array(
|
||||
'name' => new external_value(PARAM_ALPHANUMEXT, 'data name'),
|
||||
'value' => new external_value(PARAM_RAW, 'data value'),
|
||||
)
|
||||
), 'Preflight required data (like passwords)', VALUE_DEFAULT, array()
|
||||
)
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a summary of a quiz attempt before it is submitted.
|
||||
*
|
||||
* @param int $attemptid attempt id
|
||||
* @param int $preflightdata preflight required data (like passwords)
|
||||
* @return array of warnings and the attempt summary data for each question
|
||||
* @since Moodle 3.1
|
||||
*/
|
||||
public static function get_attempt_summary($attemptid, $preflightdata = array()) {
|
||||
|
||||
$warnings = array();
|
||||
|
||||
$params = array(
|
||||
'attemptid' => $attemptid,
|
||||
'preflightdata' => $preflightdata,
|
||||
);
|
||||
$params = self::validate_parameters(self::get_attempt_summary_parameters(), $params);
|
||||
|
||||
list($attemptobj, $messages) = self::validate_attempt($params);
|
||||
|
||||
$result = array();
|
||||
$result['warnings'] = $warnings;
|
||||
$result['questions'] = self::get_attempt_questions_data($attemptobj, false, 'all');
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the get_attempt_summary return value.
|
||||
*
|
||||
* @return external_single_structure
|
||||
* @since Moodle 3.1
|
||||
*/
|
||||
public static function get_attempt_summary_returns() {
|
||||
return new external_single_structure(
|
||||
array(
|
||||
'questions' => new external_multiple_structure(self::question_structure()),
|
||||
'warnings' => new external_warnings(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -91,4 +91,13 @@ $functions = array(
|
||||
'capabilities' => 'mod/quiz:attempt',
|
||||
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
|
||||
),
|
||||
|
||||
'mod_quiz_get_attempt_summary' => array(
|
||||
'classname' => 'mod_quiz_external',
|
||||
'methodname' => 'get_attempt_summary',
|
||||
'description' => 'Returns a summary of a quiz attempt before it is submitted.',
|
||||
'type' => 'read',
|
||||
'capabilities' => 'mod/quiz:attempt',
|
||||
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
|
||||
),
|
||||
);
|
||||
|
||||
@@ -764,21 +764,7 @@ class mod_quiz_external_testcase extends externallib_advanced_testcase {
|
||||
$this->assertEquals($attempt->id, $result[0]->get_attempt()->id);
|
||||
$this->assertEquals([], $result[1]);
|
||||
|
||||
// Try to open attempt in closed quiz.
|
||||
$quiz->timeopen = time() - WEEKSECS;
|
||||
$quiz->timeclose = time() - DAYSECS;
|
||||
$DB->update_record('quiz', $quiz);
|
||||
|
||||
try {
|
||||
testable_mod_quiz_external::validate_attempt($params);
|
||||
$this->fail('Exception expected due to passed dates.');
|
||||
} catch (moodle_quiz_exception $e) {
|
||||
$this->assertEquals('attempterror', $e->errorcode);
|
||||
}
|
||||
|
||||
// Page out of range.
|
||||
$quiz->timeopen = 0;
|
||||
$quiz->timeclose = 0;
|
||||
$DB->update_record('quiz', $quiz);
|
||||
$params['page'] = 4;
|
||||
try {
|
||||
@@ -788,6 +774,18 @@ class mod_quiz_external_testcase extends externallib_advanced_testcase {
|
||||
$this->assertEquals('Invalid page number', $e->errorcode);
|
||||
}
|
||||
|
||||
$params['page'] = 0;
|
||||
// Try to open attempt in closed quiz.
|
||||
$quiz->timeopen = time() - WEEKSECS;
|
||||
$quiz->timeclose = time() - DAYSECS;
|
||||
$DB->update_record('quiz', $quiz);
|
||||
try {
|
||||
testable_mod_quiz_external::validate_attempt($params);
|
||||
$this->fail('Exception expected due to passed dates.');
|
||||
} catch (moodle_quiz_exception $e) {
|
||||
$this->assertEquals('attemptalreadyclosed', $e->errorcode);
|
||||
}
|
||||
|
||||
// Finish the attempt.
|
||||
$attemptobj = quiz_attempt::create($attempt->id);
|
||||
$attemptobj->process_finish(time(), false);
|
||||
@@ -912,4 +910,44 @@ class mod_quiz_external_testcase extends externallib_advanced_testcase {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test get_attempt_summary
|
||||
*/
|
||||
public function test_get_attempt_summary() {
|
||||
|
||||
// Create a new quiz with one attempt started.
|
||||
list($quiz, $context, $quizobj, $attempt, $attemptobj) = $this->create_quiz_with_questions(true);
|
||||
|
||||
$this->setUser($this->student);
|
||||
$result = mod_quiz_external::get_attempt_summary($attempt->id);
|
||||
$result = external_api::clean_returnvalue(mod_quiz_external::get_attempt_summary_returns(), $result);
|
||||
|
||||
// Check the state, flagged and mark data is correct.
|
||||
$this->assertEquals('todo', $result['questions'][0]['state']);
|
||||
$this->assertEquals('todo', $result['questions'][1]['state']);
|
||||
$this->assertEquals(1, $result['questions'][0]['number']);
|
||||
$this->assertEquals(2, $result['questions'][1]['number']);
|
||||
$this->assertFalse($result['questions'][0]['flagged']);
|
||||
$this->assertFalse($result['questions'][1]['flagged']);
|
||||
$this->assertEmpty($result['questions'][0]['mark']);
|
||||
$this->assertEmpty($result['questions'][1]['mark']);
|
||||
|
||||
// Submit a response for the first question.
|
||||
$tosubmit = array(1 => array('answer' => '3.14'));
|
||||
$attemptobj->process_submitted_actions(time(), false, $tosubmit);
|
||||
$result = mod_quiz_external::get_attempt_summary($attempt->id);
|
||||
$result = external_api::clean_returnvalue(mod_quiz_external::get_attempt_summary_returns(), $result);
|
||||
|
||||
// Check it's marked as completed only the first one.
|
||||
$this->assertEquals('complete', $result['questions'][0]['state']);
|
||||
$this->assertEquals('todo', $result['questions'][1]['state']);
|
||||
$this->assertEquals(1, $result['questions'][0]['number']);
|
||||
$this->assertEquals(2, $result['questions'][1]['number']);
|
||||
$this->assertFalse($result['questions'][0]['flagged']);
|
||||
$this->assertFalse($result['questions'][1]['flagged']);
|
||||
$this->assertEmpty($result['questions'][0]['mark']);
|
||||
$this->assertEmpty($result['questions'][1]['mark']);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2015111608;
|
||||
$plugin->version = 2015111609;
|
||||
$plugin->requires = 2015111000;
|
||||
$plugin->component = 'mod_quiz';
|
||||
$plugin->cron = 60;
|
||||
|
||||
Reference in New Issue
Block a user