Skip to content

Commit

Permalink
MDL-52830 mod_quiz: New Web Service mod_quiz_get_attempt_summary
Browse files Browse the repository at this point in the history
  • Loading branch information
jleyva committed Mar 11, 2016
1 parent 4c08658 commit bc9733e
Show file tree
Hide file tree
Showing 4 changed files with 144 additions and 26 deletions.
97 changes: 84 additions & 13 deletions mod/quiz/classes/external.php
Expand Up @@ -787,6 +787,9 @@ protected static function validate_attempt($params) {

$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);

Expand Down Expand Up @@ -831,23 +834,27 @@ protected static function validate_attempt($params) {
$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');
}
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');
}

// Prevent out of sequence access.
if ($attemptobj->get_currentpage() != $params['page']) {
if ($attemptobj->get_navigation_method() == QUIZ_NAVMETHOD_SEQ &&
$attemptobj->get_currentpage() > $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']) {
throw new moodle_quiz_exception($attemptobj->get_quizobj(), 'Out of sequence access');
throw new moodle_quiz_exception($attemptobj->get_quizobj(), 'Out of sequence access');
}
}
}

// Check slots.
$slots = $attemptobj->get_slots($params['page']);
// Check slots.
$slots = $attemptobj->get_slots($params['page']);

if (empty($slots)) {
throw new moodle_quiz_exception($attemptobj->get_quizobj(), 'noquestionsfound');
if (empty($slots)) {
throw new moodle_quiz_exception($attemptobj->get_quizobj(), 'noquestionsfound');
}
}

return array($attemptobj, $messages);
Expand Down Expand Up @@ -1002,4 +1009,68 @@ public static function get_attempt_data_returns() {
);
}

/**
* 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(),
)
);
}

}
9 changes: 9 additions & 0 deletions mod/quiz/db/services.php
Expand Up @@ -91,4 +91,13 @@
'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)
),
);
62 changes: 50 additions & 12 deletions mod/quiz/tests/external_test.php
Expand Up @@ -764,28 +764,26 @@ public function test_validate_attempt() {
$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;
// Page out of range.
$DB->update_record('quiz', $quiz);

$params['page'] = 4;
try {
testable_mod_quiz_external::validate_attempt($params);
$this->fail('Exception expected due to passed dates.');
$this->fail('Exception expected due to page out of range.');
} catch (moodle_quiz_exception $e) {
$this->assertEquals('attempterror', $e->errorcode);
$this->assertEquals('Invalid page number', $e->errorcode);
}

// Page out of range.
$quiz->timeopen = 0;
$quiz->timeclose = 0;
$params['page'] = 0;
// Try to open attempt in closed quiz.
$quiz->timeopen = time() - WEEKSECS;
$quiz->timeclose = time() - DAYSECS;
$DB->update_record('quiz', $quiz);
$params['page'] = 4;
try {
testable_mod_quiz_external::validate_attempt($params);
$this->fail('Exception expected due to page out of range.');
$this->fail('Exception expected due to passed dates.');
} catch (moodle_quiz_exception $e) {
$this->assertEquals('Invalid page number', $e->errorcode);
$this->assertEquals('attemptalreadyclosed', $e->errorcode);
}

// Finish the attempt.
Expand Down Expand Up @@ -912,4 +910,44 @@ public function test_get_attempt_data() {

}

/**
* 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']);

}

}
2 changes: 1 addition & 1 deletion mod/quiz/version.php
Expand Up @@ -24,7 +24,7 @@

defined('MOODLE_INTERNAL') || die();

$plugin->version = 2015111608;
$plugin->version = 2015111609;
$plugin->requires = 2015111000;
$plugin->component = 'mod_quiz';
$plugin->cron = 60;

0 comments on commit bc9733e

Please sign in to comment.