mirror of
https://github.com/moodle/moodle.git
synced 2025-08-05 08:56:36 +02:00
Merge branch 'MDL-52868-master' of git://github.com/jleyva/moodle
This commit is contained in:
commit
7cc45b2e72
4 changed files with 269 additions and 2 deletions
|
@ -1254,4 +1254,136 @@ class mod_quiz_external extends external_api {
|
|||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Validate an attempt finished for review. The attempt would be reviewed by a user or a teacher.
|
||||
*
|
||||
* @param array $params Array of parameters including the attemptid
|
||||
* @return array containing the attempt object and display options
|
||||
* @since Moodle 3.1
|
||||
* @throws moodle_exception
|
||||
* @throws moodle_quiz_exception
|
||||
*/
|
||||
protected static function validate_attempt_review($params) {
|
||||
|
||||
$attemptobj = quiz_attempt::create($params['attemptid']);
|
||||
$attemptobj->check_review_capability();
|
||||
|
||||
$displayoptions = $attemptobj->get_display_options(true);
|
||||
if ($attemptobj->is_own_attempt()) {
|
||||
if (!$attemptobj->is_finished()) {
|
||||
throw new moodle_quiz_exception($attemptobj->get_quizobj(), 'attemptclosed');
|
||||
} else if (!$displayoptions->attempt) {
|
||||
throw new moodle_exception($attemptobj->cannot_review_message());
|
||||
}
|
||||
} else if (!$attemptobj->is_review_allowed()) {
|
||||
throw new moodle_quiz_exception($attemptobj->get_quizobj(), 'noreviewattempt');
|
||||
}
|
||||
return array($attemptobj, $displayoptions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the parameters for get_attempt_review.
|
||||
*
|
||||
* @return external_external_function_parameters
|
||||
* @since Moodle 3.1
|
||||
*/
|
||||
public static function get_attempt_review_parameters() {
|
||||
return new external_function_parameters (
|
||||
array(
|
||||
'attemptid' => new external_value(PARAM_INT, 'attempt id'),
|
||||
'page' => new external_value(PARAM_INT, 'page number, empty for all the questions in all the pages',
|
||||
VALUE_DEFAULT, -1),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns review information for the given finished attempt, can be used by users or teachers.
|
||||
*
|
||||
* @param int $attemptid attempt id
|
||||
* @param int $page page number, empty for all the questions in all the pages
|
||||
* @return array of warnings and the attempt data, feedback and questions
|
||||
* @since Moodle 3.1
|
||||
* @throws moodle_exception
|
||||
* @throws moodle_quiz_exception
|
||||
*/
|
||||
public static function get_attempt_review($attemptid, $page = -1) {
|
||||
global $PAGE;
|
||||
|
||||
$warnings = array();
|
||||
|
||||
$params = array(
|
||||
'attemptid' => $attemptid,
|
||||
'page' => $page,
|
||||
);
|
||||
$params = self::validate_parameters(self::get_attempt_review_parameters(), $params);
|
||||
|
||||
list($attemptobj, $displayoptions) = self::validate_attempt_review($params);
|
||||
|
||||
if ($params['page'] !== -1) {
|
||||
$page = $attemptobj->force_page_number_into_range($params['page']);
|
||||
} else {
|
||||
$page = 'all';
|
||||
}
|
||||
|
||||
// Prepare the output.
|
||||
$result = array();
|
||||
$result['attempt'] = $attemptobj->get_attempt();
|
||||
$result['questions'] = self::get_attempt_questions_data($attemptobj, true, $page, true);
|
||||
|
||||
$result['additionaldata'] = array();
|
||||
// Summary data (from behaviours).
|
||||
$summarydata = $attemptobj->get_additional_summary_data($displayoptions);
|
||||
foreach ($summarydata as $key => $data) {
|
||||
// This text does not need formatting (no need for external_format_[string|text]).
|
||||
$result['additionaldata'][] = array(
|
||||
'id' => $key,
|
||||
'title' => $data['title'], $attemptobj->get_quizobj()->get_context()->id,
|
||||
'content' => $data['content'],
|
||||
);
|
||||
}
|
||||
|
||||
// Feedback if there is any, and the user is allowed to see it now.
|
||||
$grade = quiz_rescale_grade($attemptobj->get_attempt()->sumgrades, $attemptobj->get_quiz(), false);
|
||||
|
||||
$feedback = $attemptobj->get_overall_feedback($grade);
|
||||
if ($displayoptions->overallfeedback && $feedback) {
|
||||
$result['additionaldata'][] = array(
|
||||
'id' => 'feedback',
|
||||
'title' => get_string('feedback', 'quiz'),
|
||||
'content' => $feedback,
|
||||
);
|
||||
}
|
||||
|
||||
$result['grade'] = $grade;
|
||||
$result['warnings'] = $warnings;
|
||||
return $result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Describes the get_attempt_review return value.
|
||||
*
|
||||
* @return external_single_structure
|
||||
* @since Moodle 3.1
|
||||
*/
|
||||
public static function get_attempt_review_returns() {
|
||||
return new external_single_structure(
|
||||
array(
|
||||
'grade' => new external_value(PARAM_RAW, 'grade for the quiz (or empty or "notyetgraded")'),
|
||||
'attempt' => self::attempt_structure(),
|
||||
'additionaldata' => new external_multiple_structure(
|
||||
new external_single_structure(
|
||||
array(
|
||||
'id' => new external_value(PARAM_ALPHANUMEXT, 'id of the data'),
|
||||
'title' => new external_value(PARAM_TEXT, 'data title'),
|
||||
'content' => new external_value(PARAM_RAW, 'data content'),
|
||||
)
|
||||
)
|
||||
),
|
||||
'questions' => new external_multiple_structure(self::question_structure()),
|
||||
'warnings' => new external_warnings(),
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -119,4 +119,13 @@ $functions = array(
|
|||
'capabilities' => 'mod/quiz:attempt',
|
||||
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
|
||||
),
|
||||
|
||||
'mod_quiz_get_attempt_review' => array(
|
||||
'classname' => 'mod_quiz_external',
|
||||
'methodname' => 'get_attempt_review',
|
||||
'description' => 'Returns review information for the given finished attempt, can be used by users or teachers.',
|
||||
'type' => 'read',
|
||||
'capabilities' => 'mod/quiz:reviewmyattempts',
|
||||
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
|
||||
),
|
||||
);
|
||||
|
|
|
@ -51,6 +51,16 @@ class testable_mod_quiz_external extends mod_quiz_external {
|
|||
public static function validate_attempt($params, $checkaccessrules = true, $failifoverdue = true) {
|
||||
return parent::validate_attempt($params, $checkaccessrules, $failifoverdue);
|
||||
}
|
||||
|
||||
/**
|
||||
* Public accessor.
|
||||
*
|
||||
* @param array $params Array of parameters including the attemptid
|
||||
* @return array containing the attempt object and display options
|
||||
*/
|
||||
public static function validate_attempt_review($params) {
|
||||
return parent::validate_attempt_review($params);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -101,7 +111,7 @@ class mod_quiz_external_testcase extends externallib_advanced_testcase {
|
|||
// Create a new quiz with attempts.
|
||||
$quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
|
||||
$data = array('course' => $this->course->id,
|
||||
'sumgrades' => 1);
|
||||
'sumgrades' => 2);
|
||||
$quiz = $quizgenerator->create_instance($data);
|
||||
$context = context_module::instance($quiz->cmid);
|
||||
|
||||
|
@ -1121,4 +1131,120 @@ class mod_quiz_external_testcase extends externallib_advanced_testcase {
|
|||
|
||||
}
|
||||
|
||||
/**
|
||||
* Test validate_attempt_review
|
||||
*/
|
||||
public function test_validate_attempt_review() {
|
||||
global $DB;
|
||||
|
||||
// Create a new quiz with one attempt started.
|
||||
list($quiz, $context, $quizobj, $attempt, $attemptobj) = $this->create_quiz_with_questions(true);
|
||||
|
||||
$this->setUser($this->student);
|
||||
|
||||
// Invalid attempt, invalid id.
|
||||
try {
|
||||
$params = array('attemptid' => -1);
|
||||
testable_mod_quiz_external::validate_attempt_review($params);
|
||||
$this->fail('Exception expected due invalid id.');
|
||||
} catch (dml_missing_record_exception $e) {
|
||||
$this->assertEquals('invalidrecord', $e->errorcode);
|
||||
}
|
||||
|
||||
// Invalid attempt, not closed.
|
||||
try {
|
||||
$params = array('attemptid' => $attempt->id);
|
||||
testable_mod_quiz_external::validate_attempt_review($params);
|
||||
$this->fail('Exception expected due not closed attempt.');
|
||||
} catch (moodle_quiz_exception $e) {
|
||||
$this->assertEquals('attemptclosed', $e->errorcode);
|
||||
}
|
||||
|
||||
// Test ok case (finished attempt).
|
||||
list($quiz, $context, $quizobj, $attempt, $attemptobj) = $this->create_quiz_with_questions(true, true);
|
||||
|
||||
$params = array('attemptid' => $attempt->id);
|
||||
testable_mod_quiz_external::validate_attempt_review($params);
|
||||
|
||||
// Teacher should be able to view the review of one student's attempt.
|
||||
$this->setUser($this->teacher);
|
||||
testable_mod_quiz_external::validate_attempt_review($params);
|
||||
|
||||
// We should not see other students attempts.
|
||||
$anotherstudent = self::getDataGenerator()->create_user();
|
||||
$this->getDataGenerator()->enrol_user($anotherstudent->id, $this->course->id, $this->studentrole->id, 'manual');
|
||||
|
||||
$this->setUser($anotherstudent);
|
||||
try {
|
||||
$params = array('attemptid' => $attempt->id);
|
||||
testable_mod_quiz_external::validate_attempt_review($params);
|
||||
$this->fail('Exception expected due missing permissions.');
|
||||
} catch (moodle_quiz_exception $e) {
|
||||
$this->assertEquals('noreviewattempt', $e->errorcode);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Test get_attempt_review
|
||||
*/
|
||||
public function test_get_attempt_review() {
|
||||
global $DB;
|
||||
|
||||
// Create a new quiz with two questions and one attempt finished.
|
||||
list($quiz, $context, $quizobj, $attempt, $attemptobj, $quba) = $this->create_quiz_with_questions(true, true);
|
||||
|
||||
// Add feedback to the quiz.
|
||||
$feedback = new stdClass();
|
||||
$feedback->quizid = $quiz->id;
|
||||
$feedback->feedbacktext = 'Feedback text 1';
|
||||
$feedback->feedbacktextformat = 1;
|
||||
$feedback->mingrade = 49;
|
||||
$feedback->maxgrade = 100;
|
||||
$feedback->id = $DB->insert_record('quiz_feedback', $feedback);
|
||||
|
||||
$feedback->feedbacktext = 'Feedback text 2';
|
||||
$feedback->feedbacktextformat = 1;
|
||||
$feedback->mingrade = 30;
|
||||
$feedback->maxgrade = 48;
|
||||
$feedback->id = $DB->insert_record('quiz_feedback', $feedback);
|
||||
|
||||
$result = mod_quiz_external::get_attempt_review($attempt->id);
|
||||
$result = external_api::clean_returnvalue(mod_quiz_external::get_attempt_review_returns(), $result);
|
||||
|
||||
// Two questions, one completed and correct, the other gave up.
|
||||
$this->assertEquals(50, $result['grade']);
|
||||
$this->assertEquals(1, $result['attempt']['attempt']);
|
||||
$this->assertEquals('finished', $result['attempt']['state']);
|
||||
$this->assertEquals(1, $result['attempt']['sumgrades']);
|
||||
$this->assertCount(2, $result['questions']);
|
||||
$this->assertEquals('gradedright', $result['questions'][0]['state']);
|
||||
$this->assertEquals(1, $result['questions'][0]['slot']);
|
||||
$this->assertEquals('gaveup', $result['questions'][1]['state']);
|
||||
$this->assertEquals(2, $result['questions'][1]['slot']);
|
||||
|
||||
$this->assertCount(1, $result['additionaldata']);
|
||||
$this->assertEquals('feedback', $result['additionaldata'][0]['id']);
|
||||
$this->assertEquals('Feedback', $result['additionaldata'][0]['title']);
|
||||
$this->assertEquals('Feedback text 1', $result['additionaldata'][0]['content']);
|
||||
|
||||
// Only first page.
|
||||
$result = mod_quiz_external::get_attempt_review($attempt->id, 0);
|
||||
$result = external_api::clean_returnvalue(mod_quiz_external::get_attempt_review_returns(), $result);
|
||||
|
||||
$this->assertEquals(50, $result['grade']);
|
||||
$this->assertEquals(1, $result['attempt']['attempt']);
|
||||
$this->assertEquals('finished', $result['attempt']['state']);
|
||||
$this->assertEquals(1, $result['attempt']['sumgrades']);
|
||||
$this->assertCount(1, $result['questions']);
|
||||
$this->assertEquals('gradedright', $result['questions'][0]['state']);
|
||||
$this->assertEquals(1, $result['questions'][0]['slot']);
|
||||
|
||||
$this->assertCount(1, $result['additionaldata']);
|
||||
$this->assertEquals('feedback', $result['additionaldata'][0]['id']);
|
||||
$this->assertEquals('Feedback', $result['additionaldata'][0]['title']);
|
||||
$this->assertEquals('Feedback text 1', $result['additionaldata'][0]['content']);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -24,7 +24,7 @@
|
|||
|
||||
defined('MOODLE_INTERNAL') || die();
|
||||
|
||||
$plugin->version = 2016032103;
|
||||
$plugin->version = 2016032104;
|
||||
$plugin->requires = 2015111000;
|
||||
$plugin->component = 'mod_quiz';
|
||||
$plugin->cron = 60;
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue