mirror of
https://github.com/moodle/moodle.git
synced 2025-01-19 14:27:22 +01:00
Merge branch 'MDL-52670-master' of git://github.com/jleyva/moodle
Conflicts: lib/db/services.php
This commit is contained in:
commit
9b701a37e4
@ -331,4 +331,120 @@ class mod_quiz_external extends external_api {
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the parameters for get_user_attempts.
|
||||||
|
*
|
||||||
|
* @return external_external_function_parameters
|
||||||
|
* @since Moodle 3.1
|
||||||
|
*/
|
||||||
|
public static function get_user_attempts_parameters() {
|
||||||
|
return new external_function_parameters (
|
||||||
|
array(
|
||||||
|
'quizid' => new external_value(PARAM_INT, 'quiz instance id'),
|
||||||
|
'userid' => new external_value(PARAM_INT, 'user id, empty for current user', VALUE_DEFAULT, 0),
|
||||||
|
'status' => new external_value(PARAM_ALPHA, 'quiz status: all, finished or unfinished', VALUE_DEFAULT, 'finished'),
|
||||||
|
'includepreviews' => new external_value(PARAM_BOOL, 'whether to include previews or not', VALUE_DEFAULT, false),
|
||||||
|
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Return a list of attempts for the given quiz and user.
|
||||||
|
*
|
||||||
|
* @param int $quizid quiz instance id
|
||||||
|
* @param int $userid user id
|
||||||
|
* @param string $status quiz status: all, finished or unfinished
|
||||||
|
* @param bool $includepreviews whether to include previews or not
|
||||||
|
* @return array of warnings and the list of attempts
|
||||||
|
* @since Moodle 3.1
|
||||||
|
* @throws invalid_parameter_exception
|
||||||
|
*/
|
||||||
|
public static function get_user_attempts($quizid, $userid = 0, $status = 'finished', $includepreviews = false) {
|
||||||
|
global $DB, $USER;
|
||||||
|
|
||||||
|
$warnings = array();
|
||||||
|
|
||||||
|
$params = array(
|
||||||
|
'quizid' => $quizid,
|
||||||
|
'userid' => $userid,
|
||||||
|
'status' => $status,
|
||||||
|
'includepreviews' => $includepreviews,
|
||||||
|
);
|
||||||
|
$params = self::validate_parameters(self::get_user_attempts_parameters(), $params);
|
||||||
|
|
||||||
|
// Request and permission validation.
|
||||||
|
$quiz = $DB->get_record('quiz', array('id' => $params['quizid']), '*', MUST_EXIST);
|
||||||
|
list($course, $cm) = get_course_and_cm_from_instance($quiz, 'quiz');
|
||||||
|
|
||||||
|
$context = context_module::instance($cm->id);
|
||||||
|
self::validate_context($context);
|
||||||
|
|
||||||
|
if (!in_array($params['status'], array('all', 'finished', 'unfinished'))) {
|
||||||
|
throw new invalid_parameter_exception('Invalid status value');
|
||||||
|
}
|
||||||
|
|
||||||
|
// Default value for userid.
|
||||||
|
if (empty($params['userid'])) {
|
||||||
|
$params['userid'] = $USER->id;
|
||||||
|
}
|
||||||
|
|
||||||
|
$user = core_user::get_user($params['userid'], '*', MUST_EXIST);
|
||||||
|
core_user::require_active_user($user);
|
||||||
|
|
||||||
|
// Extra checks so only users with permissions can view other users attempts.
|
||||||
|
if ($USER->id != $user->id) {
|
||||||
|
require_capability('mod/quiz:viewreports', $context);
|
||||||
|
}
|
||||||
|
|
||||||
|
$attempts = quiz_get_user_attempts($quiz->id, $user->id, $params['status'], $params['includepreviews']);
|
||||||
|
|
||||||
|
$result = array();
|
||||||
|
$result['attempts'] = $attempts;
|
||||||
|
$result['warnings'] = $warnings;
|
||||||
|
return $result;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Describes the get_user_attempts return value.
|
||||||
|
*
|
||||||
|
* @return external_single_structure
|
||||||
|
* @since Moodle 3.1
|
||||||
|
*/
|
||||||
|
public static function get_user_attempts_returns() {
|
||||||
|
return new external_single_structure(
|
||||||
|
array(
|
||||||
|
'attempts' => new external_multiple_structure(
|
||||||
|
new external_single_structure(
|
||||||
|
array(
|
||||||
|
'id' => new external_value(PARAM_INT, 'Attempt id.', VALUE_OPTIONAL),
|
||||||
|
'quiz' => new external_value(PARAM_INT, 'Foreign key reference to the quiz that was attempted.',
|
||||||
|
VALUE_OPTIONAL),
|
||||||
|
'userid' => new external_value(PARAM_INT, 'Foreign key reference to the user whose attempt this is.',
|
||||||
|
VALUE_OPTIONAL),
|
||||||
|
'attempt' => new external_value(PARAM_INT, 'Sequentially numbers this students attempts at this quiz.',
|
||||||
|
VALUE_OPTIONAL),
|
||||||
|
'uniqueid' => new external_value(PARAM_INT, 'Foreign key reference to the question_usage that holds the
|
||||||
|
details of the the question_attempts that make up this quiz
|
||||||
|
attempt.', VALUE_OPTIONAL),
|
||||||
|
'layout' => new external_value(PARAM_RAW, 'Attempt layout.', VALUE_OPTIONAL),
|
||||||
|
'currentpage' => new external_value(PARAM_INT, 'Attempt current page.', VALUE_OPTIONAL),
|
||||||
|
'preview' => new external_value(PARAM_INT, 'Whether is a preview attempt or not.', VALUE_OPTIONAL),
|
||||||
|
'state' => new external_value(PARAM_ALPHA, 'The current state of the attempts. \'inprogress\',
|
||||||
|
\'overdue\', \'finished\' or \'abandoned\'.', VALUE_OPTIONAL),
|
||||||
|
'timestart' => new external_value(PARAM_INT, 'Time when the attempt was started.', VALUE_OPTIONAL),
|
||||||
|
'timefinish' => new external_value(PARAM_INT, 'Time when the attempt was submitted.
|
||||||
|
0 if the attempt has not been submitted yet.', VALUE_OPTIONAL),
|
||||||
|
'timemodified' => new external_value(PARAM_INT, 'Last modified time.', VALUE_OPTIONAL),
|
||||||
|
'timecheckstate' => new external_value(PARAM_INT, 'Next time quiz cron should check attempt for
|
||||||
|
state changes. NULL means never check.', VALUE_OPTIONAL),
|
||||||
|
'sumgrades' => new external_value(PARAM_FLOAT, 'Total marks for this attempt.', VALUE_OPTIONAL),
|
||||||
|
)
|
||||||
|
)
|
||||||
|
),
|
||||||
|
'warnings' => new external_warnings(),
|
||||||
|
)
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -46,4 +46,12 @@ $functions = array(
|
|||||||
'capabilities' => 'mod/quiz:view',
|
'capabilities' => 'mod/quiz:view',
|
||||||
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
|
'services' => array(MOODLE_OFFICIAL_MOBILE_SERVICE)
|
||||||
),
|
),
|
||||||
|
|
||||||
|
'mod_quiz_get_user_attempts' => array(
|
||||||
|
'classname' => 'mod_quiz_external',
|
||||||
|
'methodname' => 'get_user_attempts',
|
||||||
|
'description' => 'Return a list of attempts for the given quiz and user.',
|
||||||
|
'type' => 'read',
|
||||||
|
'capabilities' => 'mod/quiz:view'
|
||||||
|
),
|
||||||
);
|
);
|
||||||
|
@ -268,4 +268,122 @@ class mod_quiz_external_testcase extends externallib_advanced_testcase {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Test get_user_attempts
|
||||||
|
*/
|
||||||
|
public function test_get_user_attempts() {
|
||||||
|
|
||||||
|
// Create a new quiz with attempts.
|
||||||
|
$quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz');
|
||||||
|
$data = array('course' => $this->course->id,
|
||||||
|
'sumgrades' => 1);
|
||||||
|
$quiz = $quizgenerator->create_instance($data);
|
||||||
|
|
||||||
|
// Create a couple of questions.
|
||||||
|
$questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question');
|
||||||
|
|
||||||
|
$cat = $questiongenerator->create_question_category();
|
||||||
|
$question = $questiongenerator->create_question('numerical', null, array('category' => $cat->id));
|
||||||
|
quiz_add_quiz_question($question->id, $quiz);
|
||||||
|
|
||||||
|
$quizobj = quiz::create($quiz->id, $this->student->id);
|
||||||
|
|
||||||
|
// Set grade to pass.
|
||||||
|
$item = grade_item::fetch(array('courseid' => $this->course->id, 'itemtype' => 'mod',
|
||||||
|
'itemmodule' => 'quiz', 'iteminstance' => $quiz->id, 'outcomeid' => null));
|
||||||
|
$item->gradepass = 80;
|
||||||
|
$item->update();
|
||||||
|
|
||||||
|
// Start the passing attempt.
|
||||||
|
$quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
|
||||||
|
$quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
|
||||||
|
|
||||||
|
$timenow = time();
|
||||||
|
$attempt = quiz_create_attempt($quizobj, 1, false, $timenow, false, $this->student->id);
|
||||||
|
quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow);
|
||||||
|
quiz_attempt_save_started($quizobj, $quba, $attempt);
|
||||||
|
|
||||||
|
// Process some responses from the student.
|
||||||
|
$attemptobj = quiz_attempt::create($attempt->id);
|
||||||
|
$tosubmit = array(1 => array('answer' => '3.14'));
|
||||||
|
$attemptobj->process_submitted_actions($timenow, false, $tosubmit);
|
||||||
|
|
||||||
|
// Finish the attempt.
|
||||||
|
$attemptobj = quiz_attempt::create($attempt->id);
|
||||||
|
$this->assertTrue($attemptobj->has_response_to_at_least_one_graded_question());
|
||||||
|
$attemptobj->process_finish($timenow, false);
|
||||||
|
|
||||||
|
$this->setUser($this->student);
|
||||||
|
$result = mod_quiz_external::get_user_attempts($quiz->id);
|
||||||
|
$result = external_api::clean_returnvalue(mod_quiz_external::get_user_attempts_returns(), $result);
|
||||||
|
|
||||||
|
$this->assertCount(1, $result['attempts']);
|
||||||
|
$this->assertEquals($attempt->id, $result['attempts'][0]['id']);
|
||||||
|
$this->assertEquals($quiz->id, $result['attempts'][0]['quiz']);
|
||||||
|
$this->assertEquals($this->student->id, $result['attempts'][0]['userid']);
|
||||||
|
$this->assertEquals(1, $result['attempts'][0]['attempt']);
|
||||||
|
|
||||||
|
// Test filters. Only finished.
|
||||||
|
$result = mod_quiz_external::get_user_attempts($quiz->id, 0, 'finished', false);
|
||||||
|
$result = external_api::clean_returnvalue(mod_quiz_external::get_user_attempts_returns(), $result);
|
||||||
|
|
||||||
|
$this->assertCount(1, $result['attempts']);
|
||||||
|
$this->assertEquals($attempt->id, $result['attempts'][0]['id']);
|
||||||
|
|
||||||
|
// Test filters. All attempts.
|
||||||
|
$result = mod_quiz_external::get_user_attempts($quiz->id, 0, 'all', false);
|
||||||
|
$result = external_api::clean_returnvalue(mod_quiz_external::get_user_attempts_returns(), $result);
|
||||||
|
|
||||||
|
$this->assertCount(1, $result['attempts']);
|
||||||
|
$this->assertEquals($attempt->id, $result['attempts'][0]['id']);
|
||||||
|
|
||||||
|
// Test filters. Unfinished.
|
||||||
|
$result = mod_quiz_external::get_user_attempts($quiz->id, 0, 'unfinished', false);
|
||||||
|
$result = external_api::clean_returnvalue(mod_quiz_external::get_user_attempts_returns(), $result);
|
||||||
|
|
||||||
|
$this->assertCount(0, $result['attempts']);
|
||||||
|
|
||||||
|
// Start a new attempt, but not finish it.
|
||||||
|
$timenow = time();
|
||||||
|
$attempt = quiz_create_attempt($quizobj, 2, false, $timenow, false, $this->student->id);
|
||||||
|
$quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context());
|
||||||
|
$quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour);
|
||||||
|
|
||||||
|
quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow);
|
||||||
|
quiz_attempt_save_started($quizobj, $quba, $attempt);
|
||||||
|
|
||||||
|
// Test filters. All attempts.
|
||||||
|
$result = mod_quiz_external::get_user_attempts($quiz->id, 0, 'all', false);
|
||||||
|
$result = external_api::clean_returnvalue(mod_quiz_external::get_user_attempts_returns(), $result);
|
||||||
|
|
||||||
|
$this->assertCount(2, $result['attempts']);
|
||||||
|
|
||||||
|
// Test filters. Unfinished.
|
||||||
|
$result = mod_quiz_external::get_user_attempts($quiz->id, 0, 'unfinished', false);
|
||||||
|
$result = external_api::clean_returnvalue(mod_quiz_external::get_user_attempts_returns(), $result);
|
||||||
|
|
||||||
|
$this->assertCount(1, $result['attempts']);
|
||||||
|
|
||||||
|
// Test manager can see user attempts.
|
||||||
|
$this->setUser($this->teacher);
|
||||||
|
$result = mod_quiz_external::get_user_attempts($quiz->id, $this->student->id);
|
||||||
|
$result = external_api::clean_returnvalue(mod_quiz_external::get_user_attempts_returns(), $result);
|
||||||
|
|
||||||
|
$this->assertCount(1, $result['attempts']);
|
||||||
|
$this->assertEquals($this->student->id, $result['attempts'][0]['userid']);
|
||||||
|
|
||||||
|
$result = mod_quiz_external::get_user_attempts($quiz->id, $this->student->id, 'all');
|
||||||
|
$result = external_api::clean_returnvalue(mod_quiz_external::get_user_attempts_returns(), $result);
|
||||||
|
|
||||||
|
$this->assertCount(2, $result['attempts']);
|
||||||
|
$this->assertEquals($this->student->id, $result['attempts'][0]['userid']);
|
||||||
|
|
||||||
|
// Invalid parameters.
|
||||||
|
try {
|
||||||
|
mod_quiz_external::get_user_attempts($quiz->id, $this->student->id, 'INVALID_PARAMETER');
|
||||||
|
$this->fail('Exception expected due to missing capability.');
|
||||||
|
} catch (invalid_parameter_exception $e) {
|
||||||
|
$this->assertEquals('invalidparameter', $e->errorcode);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user