Skip to content

Commit

Permalink
MDL-79295 quiz: Add proper WS support for sequential nav.
Browse files Browse the repository at this point in the history
  • Loading branch information
jleyva committed Jan 26, 2024
1 parent 810554e commit 30598cf
Show file tree
Hide file tree
Showing 2 changed files with 46 additions and 30 deletions.
64 changes: 38 additions & 26 deletions mod/quiz/classes/external.php
Expand Up @@ -968,41 +968,18 @@ private static function get_attempt_questions_data(quiz_attempt $attemptobj, $re
$qattempt = $attemptobj->get_question_attempt($slot);
$questiondef = $qattempt->get_question(true);

// Get response files (for questions like essay that allows attachments).
$responsefileareas = [];
foreach (question_bank::get_qtype($qtype)->response_file_areas() as $area) {
if ($files = $attemptobj->get_question_attempt($slot)->get_last_qt_files($area, $contextid)) {
$responsefileareas[$area]['area'] = $area;
$responsefileareas[$area]['files'] = [];

foreach ($files as $file) {
$responsefileareas[$area]['files'][] = [
'filename' => $file->get_filename(),
'fileurl' => $qattempt->get_response_file_url($file),
'filesize' => $file->get_filesize(),
'filepath' => $file->get_filepath(),
'mimetype' => $file->get_mimetype(),
'timemodified' => $file->get_timemodified(),
];
}
}
}

// Check display settings for question.
$settings = $questiondef->get_question_definition_for_external_rendering($qattempt, $displayoptions);

// Navigation information.
$question = [
'slot' => $slot,
'type' => $qtype,
'page' => $attemptobj->get_question_page($slot),
'questionnumber' => $attemptobj->get_question_number($slot),
'flagged' => $attemptobj->is_question_flagged($slot),
'html' => $attemptobj->render_question($slot, $review, $renderer) . $PAGE->requires->get_end_code(),
'responsefileareas' => $responsefileareas,
'sequencecheck' => $qattempt->get_sequence_check_count(),
'lastactiontime' => $qattempt->get_last_step()->get_timecreated(),
'hasautosavedstep' => $qattempt->has_autosaved_step(),
'settings' => !empty($settings) ? json_encode($settings) : null,
];

if ($question['questionnumber'] === (string) (int) $question['questionnumber']) {
Expand All @@ -1023,9 +1000,44 @@ private static function get_attempt_questions_data(quiz_attempt $attemptobj, $re
if ($displayoptions->marks >= question_display_options::MARK_AND_MAX) {
$question['mark'] = $attemptobj->get_question_mark($slot);
}
if ($attemptobj->check_page_access($attemptobj->get_question_page($slot), false)) {
$questions[] = $question;

// Check access. This is needed especially when sequential navigation is enforced. To prevent the student see "future" questions.
$haveaccess = $attemptobj->check_page_access($attemptobj->get_question_page($slot), false);
if (!$haveaccess) {
$question['type'] = '';
$question['html'] = '';
}

// For visited pages/questions it is ok to keep data the user already saw.
$questionalreadyseen = $attemptobj->get_currentpage() >= $attemptobj->get_question_page($slot);

// Information when only the user has access to the question at any moment (free navigation) or already seen.
if ($haveaccess || $questionalreadyseen) {
// Get response files (for questions like essay that allows attachments).
$responsefileareas = [];
foreach (question_bank::get_qtype($qtype)->response_file_areas() as $area) {
if ($files = $attemptobj->get_question_attempt($slot)->get_last_qt_files($area, $contextid)) {
$responsefileareas[$area]['area'] = $area;
$responsefileareas[$area]['files'] = [];

foreach ($files as $file) {
$responsefileareas[$area]['files'][] = [
'filename' => $file->get_filename(),
'fileurl' => $qattempt->get_response_file_url($file),
'filesize' => $file->get_filesize(),
'filepath' => $file->get_filepath(),
'mimetype' => $file->get_mimetype(),
'timemodified' => $file->get_timemodified(),
];
}
}
}
$question['type'] = $qtype;
$question['html'] = $attemptobj->render_question($slot, $review, $renderer) . $PAGE->requires->get_end_code();
$question['responsefileareas'] = $responsefileareas;
$question['settings'] = !empty($settings) ? json_encode($settings) : null;
}
$questions[] = $question;
}
return $questions;
}
Expand Down
12 changes: 8 additions & 4 deletions mod/quiz/tests/external/external_test.php
Expand Up @@ -2074,17 +2074,20 @@ public function test_sequential_navigation_view_attempt() {
}

/**
* Test that a sequential navigation quiz is not allowing to see questions in advance for a student
* Test that a sequential navigation quiz is not allowing to see questions content in advance for a student.
*/
public function test_sequential_navigation_attempt_summary() {
// Test user with full capabilities.
$quiz = $this->prepare_sequential_quiz();
$attemptobj = $this->create_quiz_attempt_object($quiz);
$this->setUser($this->student);
// Check that we do not return other questions than the one currently viewed.
// Check that we do not return content from other questions except than the ones currently viewed.
$result = mod_quiz_external::get_attempt_summary($attemptobj->get_attemptid());
$this->assertCount(1, $result['questions']);
$this->assertStringContainsString('Question (1)', $result['questions'][0]['html']);
$this->assertStringContainsString('Question (1)', $result['questions'][0]['html']); // Current question.
$this->assertEmpty($result['questions'][1]['html']); // Next question.
$this->assertEmpty($result['questions'][2]['html']); // And more.
$this->assertEmpty($result['questions'][3]['html']); // And more.
$this->assertEmpty($result['questions'][4]['html']); // And more.
}

/**
Expand Down Expand Up @@ -2137,6 +2140,7 @@ private function prepare_sequential_quiz(): quiz_settings {
$data = [
'course' => $this->course->id,
'sumgrades' => 2,
'questionsperpage' => 1,
'preferredbehaviour' => 'deferredfeedback',
'navmethod' => QUIZ_NAVMETHOD_SEQ
];
Expand Down

0 comments on commit 30598cf

Please sign in to comment.