public function test_quiz_report_overview_report_forcesubmit_specific_attempt() { global $DB; $this->resetAfterTest(); $generator = $this->getDataGenerator(); $quizgenerator = $generator->get_plugin_generator('mod_quiz'); $questiongenerator = $generator->get_plugin_generator('core_question'); // Make a user to do the quiz. $user1 = $generator->create_user(); $user2 = $generator->create_user(); $user3 = $generator->create_user(); // Create our course. $course = $generator->create_course(array('visible' => true)); // Create the quiz. $quiz = $quizgenerator->create_instance(array('course' => $course->id, 'visible' => true, 'questionsperpage' => 0, 'grade' => 100.0, 'sumgrades' => 2)); // Create two questions. $cat = $questiongenerator->create_question_category(); $saq = $questiongenerator->create_question('shortanswer', null, array('category' => $cat->id)); $numq = $questiongenerator->create_question('numerical', null, array('category' => $cat->id)); // Add the questions to the quiz. quiz_add_quiz_question($saq->id, $quiz); quiz_add_quiz_question($numq->id, $quiz); // Get a quiz object with user access overrides. $quizobj = quiz::create($quiz->id, $user1->id); $quizobj2 = quiz::create($quiz->id, $user2->id); $quizobj3 = quiz::create($quiz->id, $user3->id); // Start the attempt. $quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context()); $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour); $quba2 = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj2->get_context()); $quba2->set_preferred_behaviour($quizobj2->get_quiz()->preferredbehaviour); $quba3 = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj3->get_context()); $quba3->set_preferred_behaviour($quizobj3->get_quiz()->preferredbehaviour); // Create a quiz attempt. $timenow = time(); $attempt = quiz_create_attempt($quizobj, 1, false, $timenow, false, $user1->id); $attempt2 = quiz_create_attempt($quizobj2, 1, false, $timenow, false, $user2->id); $attempt3 = quiz_create_attempt($quizobj3, 1, false, $timenow, false, $user3->id); // Start the attempt. quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow); quiz_attempt_save_started($quizobj, $quba, $attempt); quiz_start_new_attempt($quizobj2, $quba2, $attempt2, 1, $timenow); quiz_attempt_save_started($quizobj2, $quba2, $attempt2); quiz_start_new_attempt($quizobj3, $quba3, $attempt3, 1, $timenow); quiz_attempt_save_started($quizobj3, $quba3, $attempt3); // Answer first question and set it overdue. $tosubmit = array(1 => array('answer' => 'frog')); $tosubmit2 = array(1 => array('answer' => 'tiger')); $tosubmit3 = array(1 => array('answer' => 'tiger')); $attemptobj = quiz_attempt::create($attempt->id); $attemptobj->process_submitted_actions($timenow, true, $tosubmit); $attemptobj2 = quiz_attempt::create($attempt2->id); $attemptobj2->process_submitted_actions($timenow, true, $tosubmit2); $attemptobj3 = quiz_attempt::create($attempt3->id); $attemptobj3->process_submitted_actions($timenow, true, $tosubmit3); // Finish the attempt. $attemptobj = quiz_attempt::create($attempt->id); $this->assertTrue($attemptobj->has_response_to_at_least_one_graded_question()); $attemptobj->process_abandon($timenow, false); // Re-load quiz attempt2 data. $attemptobj = quiz_attempt::create($attempt->id); $attemptobj2 = quiz_attempt::create($attempt2->id); $attemptobj3 = quiz_attempt::create($attempt3->id); // Check that the state of the attempt is as expected. $this->assertEquals(1, $attemptobj->get_attempt_number()); $this->assertEquals(quiz_attempt::ABANDONED, $attemptobj->get_state()); $this->assertEquals($user1->id, $attemptobj->get_userid()); $this->assertTrue($attemptobj->has_response_to_at_least_one_graded_question()); // Check that the state of the attempt2 is as expected. $this->assertEquals(1, $attemptobj2->get_attempt_number()); $this->assertEquals(quiz_attempt::OVERDUE, $attemptobj2->get_state()); $this->assertEquals($user2->id, $attemptobj2->get_userid()); $this->assertTrue($attemptobj2->has_response_to_at_least_one_graded_question()); // Check that the state of the attempt3 is as expected. $this->assertEquals(1, $attemptobj3->get_attempt_number()); $this->assertEquals(quiz_attempt::OVERDUE, $attemptobj3->get_state()); $this->assertEquals($user3->id, $attemptobj3->get_userid()); $this->assertTrue($attemptobj3->has_response_to_at_least_one_graded_question()); // Force submit the attempts. $overviewreport = new quiz_overview_report_testable(); $overviewreport->forcesubmit_attempts($quiz, false, array(), array($attempt->id, $attempt3->id)); // Check that it is now finished. $attemptobj = quiz_attempt::create($attempt->id); $this->assertEquals(quiz_attempt::FINISHED, $attemptobj->get_state()); $attemptobj2 = quiz_attempt::create($attempt2->id); $this->assertEquals(quiz_attempt::OVERDUE, $attemptobj2->get_state()); $attemptobj3 = quiz_attempt::create($attempt3->id); $this->assertEquals(quiz_attempt::FINISHED, $attemptobj3->get_state()); }
protected function prepare_quiz_data() { $this->resetAfterTest(true); // Create a course $course = $this->getDataGenerator()->create_course(); // Make a quiz. $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz'); $quiz = $quizgenerator->create_instance(array('course' => $course->id, 'questionsperpage' => 0, 'grade' => 100.0, 'sumgrades' => 2)); $cm = get_coursemodule_from_instance('quiz', $quiz->id, $course->id); // Create a couple of questions. $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); $cat = $questiongenerator->create_question_category(); $saq = $questiongenerator->create_question('shortanswer', null, array('category' => $cat->id)); $numq = $questiongenerator->create_question('numerical', null, array('category' => $cat->id)); // Add them to the quiz. quiz_add_quiz_question($saq->id, $quiz); quiz_add_quiz_question($numq->id, $quiz); // Make a user to do the quiz. $user1 = $this->getDataGenerator()->create_user(); $this->setUser($user1); $quizobj = quiz::create($quiz->id, $user1->id); // Start the 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); quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow); quiz_attempt_save_started($quizobj, $quba, $attempt); return array($quizobj, $quba, $attempt); }
/** * Test update question flag */ public function test_core_question_update_flag() { $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); // Create a question category. $cat = $questiongenerator->create_question_category(); $quba = question_engine::make_questions_usage_by_activity('core_question_update_flag', context_system::instance()); $quba->set_preferred_behaviour('deferredfeedback'); $questiondata = $questiongenerator->create_question('numerical', null, array('category' => $cat->id)); $question = question_bank::load_question($questiondata->id); $slot = $quba->add_question($question); $qa = $quba->get_question_attempt($slot); self::setUser($this->student); $quba->start_all_questions(); question_engine::save_questions_usage_by_activity($quba); $qubaid = $quba->get_id(); $questionid = $question->id; $qaid = $qa->get_database_id(); $checksum = md5($qubaid . "_" . $this->student->secret . "_" . $questionid . "_" . $qaid . "_" . $slot); $flag = core_question_external::update_flag($qubaid, $questionid, $qaid, $slot, $checksum, true); $this->assertTrue($flag['status']); // Test invalid checksum. try { // Using random_string to force failing. $checksum = md5($qubaid . "_" . random_string(11) . "_" . $questionid . "_" . $qaid . "_" . $slot); core_question_external::update_flag($qubaid, $questionid, $qaid, $slot, $checksum, true); $this->fail('Exception expected due to invalid checksum.'); } catch (moodle_exception $e) { $this->assertEquals('errorsavingflags', $e->errorcode); } }
/** * We create two usages, each with two questions, a short-answer marked * out of 5, and and essay marked out of 10. We just start these attempts. * * Then we change the max mark for the short-answer question in one of the * usages to 20, using a qubaid_list, and verify. * * Then we change the max mark for the essay question in the other * usage to 2, using a qubaid_join, and verify. */ public function test_set_max_mark_in_attempts() { // Set up some things the tests will need. $this->resetAfterTest(); $dm = new question_engine_data_mapper(); // Create the questions. $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); $cat = $generator->create_question_category(); $sa = $generator->create_question('shortanswer', null, array('category' => $cat->id)); $essay = $generator->create_question('essay', null, array('category' => $cat->id)); // Create the first usage. $q = question_bank::load_question($sa->id); $this->start_attempt_at_question($q, 'interactive', 5); $q = question_bank::load_question($essay->id); $this->start_attempt_at_question($q, 'interactive', 10); $this->finish(); $this->save_quba(); $usage1id = $this->quba->get_id(); // Create the second usage. $this->quba = question_engine::make_questions_usage_by_activity('unit_test', context_system::instance()); $q = question_bank::load_question($sa->id); $this->start_attempt_at_question($q, 'interactive', 5); $this->process_submission(array('answer' => 'fish')); $q = question_bank::load_question($essay->id); $this->start_attempt_at_question($q, 'interactive', 10); $this->finish(); $this->save_quba(); $usage2id = $this->quba->get_id(); // Test set_max_mark_in_attempts with a qubaid_list. $usagestoupdate = new qubaid_list(array($usage1id)); $dm->set_max_mark_in_attempts($usagestoupdate, 1, 20.0); $quba1 = question_engine::load_questions_usage_by_activity($usage1id); $quba2 = question_engine::load_questions_usage_by_activity($usage2id); $this->assertEquals(20, $quba1->get_question_max_mark(1)); $this->assertEquals(10, $quba1->get_question_max_mark(2)); $this->assertEquals(5, $quba2->get_question_max_mark(1)); $this->assertEquals(10, $quba2->get_question_max_mark(2)); // Test set_max_mark_in_attempts with a qubaid_join. $usagestoupdate = new qubaid_join('{question_usages} qu', 'qu.id', 'qu.id = :usageid', array('usageid' => $usage2id)); $dm->set_max_mark_in_attempts($usagestoupdate, 2, 2.0); $quba1 = question_engine::load_questions_usage_by_activity($usage1id); $quba2 = question_engine::load_questions_usage_by_activity($usage2id); $this->assertEquals(20, $quba1->get_question_max_mark(1)); $this->assertEquals(10, $quba1->get_question_max_mark(2)); $this->assertEquals(5, $quba2->get_question_max_mark(1)); $this->assertEquals(2, $quba2->get_question_max_mark(2)); // Test the nothing to do case. $usagestoupdate = new qubaid_join('{question_usages} qu', 'qu.id', 'qu.id = :usageid', array('usageid' => -1)); $dm->set_max_mark_in_attempts($usagestoupdate, 2, 2.0); $quba1 = question_engine::load_questions_usage_by_activity($usage1id); $quba2 = question_engine::load_questions_usage_by_activity($usage2id); $this->assertEquals(20, $quba1->get_question_max_mark(1)); $this->assertEquals(10, $quba1->get_question_max_mark(2)); $this->assertEquals(5, $quba2->get_question_max_mark(1)); $this->assertEquals(2, $quba2->get_question_max_mark(2)); }
protected function setUp() { $this->quba = question_engine::make_questions_usage_by_activity('unit_test', context_system::instance()); $this->quba->set_preferred_behaviour('deferredfeedback'); $slot = $this->quba->add_question(test_question_maker::make_question('description')); $this->qas[$slot] = $this->quba->get_question_attempt($slot); $slot = $this->quba->add_question(test_question_maker::make_question('description')); $this->qas[$slot] = $this->quba->get_question_attempt($slot); $this->iterator = $this->quba->get_attempt_iterator(); }
public function setUp() { $this->quba = question_engine::make_questions_usage_by_activity('unit_test', get_context_instance(CONTEXT_SYSTEM)); $this->quba->set_preferred_behaviour('deferredfeedback'); $slot = $this->quba->add_question(test_question_maker::make_a_description_question()); $this->qas[$slot] = $this->quba->get_question_attempt($slot); $slot = $this->quba->add_question(test_question_maker::make_a_description_question()); $this->qas[$slot] = $this->quba->get_question_attempt($slot); $this->iterator = $this->quba->get_attempt_iterator(); }
/** * Test the various methods that load data for reporting. * * Since these methods need an expensive set-up, and then only do read-only * operations on the data, we use a single method to do the set-up, which * calls diffents methods to test each query. */ public function test_reporting_queries() { // We create two usages, each with two questions, a short-answer marked // out of 5, and and essay marked out of 10. // // In the first usage, the student answers the short-answer // question correctly, and enters something in the essay. // // In the second useage, the student answers the short-answer question // wrongly, and leaves the essay blank. $this->resetAfterTest(); $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); $cat = $generator->create_question_category(); $this->sa = $generator->create_question('shortanswer', null, array('category' => $cat->id)); $this->essay = $generator->create_question('essay', null, array('category' => $cat->id)); $this->usageids = array(); // Create the first usage. $q = question_bank::load_question($this->sa->id); $this->start_attempt_at_question($q, 'interactive', 5); $this->allslots[] = $this->slot; $this->process_submission(array('answer' => 'cat')); $this->process_submission(array('answer' => 'frog', '-submit' => 1)); $q = question_bank::load_question($this->essay->id); $this->start_attempt_at_question($q, 'interactive', 10); $this->allslots[] = $this->slot; $this->process_submission(array('answer' => '<p>The cat sat on the mat.</p>', 'answerformat' => FORMAT_HTML)); $this->finish(); $this->save_quba(); $this->usageids[] = $this->quba->get_id(); // Create the second usage. $this->quba = question_engine::make_questions_usage_by_activity('unit_test', context_system::instance()); $q = question_bank::load_question($this->sa->id); $this->start_attempt_at_question($q, 'interactive', 5); $this->process_submission(array('answer' => 'fish')); $q = question_bank::load_question($this->essay->id); $this->start_attempt_at_question($q, 'interactive', 10); $this->finish(); $this->save_quba(); $this->usageids[] = $this->quba->get_id(); // Set up some things the tests will need. $this->dm = new question_engine_data_mapper(); $this->bothusages = new qubaid_list($this->usageids); // Now test the various queries. $this->dotest_load_questions_usages_latest_steps(); $this->dotest_load_questions_usages_question_state_summary(); $this->dotest_load_questions_usages_where_question_in_state(); $this->dotest_load_average_marks(); $this->dotest_sum_usage_marks_subquery(); $this->dotest_question_attempt_latest_state_view(); }
public function test_get_questions_from_categories_with_usage_counts() { $this->resetAfterTest(); $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); $cat = $generator->create_question_category(); $questiondata1 = $generator->create_question('shortanswer', null, array('category' => $cat->id)); $questiondata2 = $generator->create_question('shortanswer', null, array('category' => $cat->id)); $questiondata3 = $generator->create_question('shortanswer', null, array('category' => $cat->id)); $quba = question_engine::make_questions_usage_by_activity('test', context_system::instance()); $quba->set_preferred_behaviour('deferredfeedback'); $question1 = question_bank::load_question($questiondata1->id); $question3 = question_bank::load_question($questiondata3->id); $quba->add_question($question1); $quba->add_question($question1); $quba->add_question($question3); $quba->start_all_questions(); question_engine::save_questions_usage_by_activity($quba); $this->assertEquals(array($questiondata2->id => 0, $questiondata3->id => 1, $questiondata1->id => 2), question_bank::get_finder()->get_questions_from_categories_with_usage_counts(array($cat->id), new qubaid_list(array($quba->get_id())))); }
/** * Construct the class. if a dbattempt object is passed in set it, otherwise initialize empty class * * @param questionmanager $questionmanager * @param \stdClass * @param \context_module $context */ public function __construct($questionmanager, $dbattempt = null, $context = null) { $this->questionmanager = $questionmanager; $this->context = $context; // if empty create new attempt if (empty($dbattempt)) { $this->attempt = new \stdClass(); // create a new quba since we're creating a new attempt $this->quba = \question_engine::make_questions_usage_by_activity('mod_activequiz', $this->questionmanager->getRTQ()->getContext()); $this->quba->set_preferred_behaviour('immediatefeedback'); $attemptlayout = $this->questionmanager->add_questions_to_quba($this->quba); // add the attempt layout to this instance $this->attempt->qubalayout = implode(',', $attemptlayout); } else { // else load it up in this class instance $this->attempt = $dbattempt; $this->quba = \question_engine::load_questions_usage_by_activity($this->attempt->questionengid); } }
public function get_clone($qinstances) { // The new quba doesn't have to be cloned, so we can use the parent class. $newquba = question_engine::make_questions_usage_by_activity($this->owningcomponent, $this->context); $newquba->set_preferred_behaviour('immediatefeedback'); foreach ($this->get_slots() as $slot) { $slotquestion = $this->get_question($slot); $attempt = $this->get_question_attempt($slot); // We have to check for the type because we might have old migrated templates // that could contain description questions. if ($slotquestion->get_type_name() == 'multichoice' || $slotquestion->get_type_name() == 'multichoiceset') { $order = $slotquestion->get_order($attempt); // Order of the answers. $order = implode(',', $order); $newslot = $newquba->add_question($slotquestion, $qinstances[$slotquestion->id]->maxmark); $qa = $newquba->get_question_attempt($newslot); $qa->start('immediatefeedback', 1, array('_order' => $order)); } } question_engine::save_questions_usage_by_activity($newquba); return $newquba; }
public function test_load_with_unnecessary_autosaved_data() { // The point here is that the somehow (probably due to two things // happening concurrently, we have autosaved data in the database that // has already been superceded by real data, so it should be ignored. // There is also a second lot of redundant data to delete. $records = new question_test_recordset(array(array('questionattemptid', 'contextid', 'questionusageid', 'slot', 'behaviour', 'questionid', 'variant', 'maxmark', 'minfraction', 'maxfraction', 'flagged', 'questionsummary', 'rightanswer', 'responsesummary', 'timemodified', 'attemptstepid', 'sequencenumber', 'state', 'fraction', 'timecreated', 'userid', 'name', 'value'), array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0, 0.0, 1.0, 0, '', '', '', 1256233790, 5, -2, 'complete', null, 1256233715, 1, 'answer', '0'), array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0, 0.0, 1.0, 0, '', '', '', 1256233790, 4, -1, 'complete', null, 1256233715, 1, 'answer', '0'), array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0, 0.0, 1.0, 0, '', '', '', 1256233790, 1, 0, 'todo', null, 1256233700, 1, null, null), array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0, 0.0, 1.0, 0, '', '', '', 1256233790, 2, 1, 'complete', null, 1256233705, 1, 'answer', '1'), array(1, 123, 1, 1, 'deferredfeedback', -1, 1, 2.0, 0.0, 1.0, 1, '', '', '', 1256233790, 3, 2, 'complete', null, 1256233710, 1, 'answer', '0'))); $question = test_question_maker::make_question('truefalse', 'true'); $question->id = -1; question_bank::start_unit_test(); question_bank::load_test_question_data($question); $observer = new testable_question_engine_unit_of_work(question_engine::make_questions_usage_by_activity('unit_test', context_system::instance())); $qa = question_attempt::load_from_records($records, 1, $observer, 'deferredfeedback'); question_bank::end_unit_test(); $this->assertEquals($question->questiontext, $qa->get_question()->questiontext); $this->assertEquals(3, $qa->get_num_steps()); $this->assertFalse($qa->has_autosaved_step()); $step = $qa->get_step(0); $this->assertEquals(question_state::$todo, $step->get_state()); $this->assertNull($step->get_fraction()); $this->assertEquals(1256233700, $step->get_timecreated()); $this->assertEquals(1, $step->get_user_id()); $this->assertEquals(array(), $step->get_all_data()); $step = $qa->get_step(1); $this->assertEquals(question_state::$complete, $step->get_state()); $this->assertNull($step->get_fraction()); $this->assertEquals(1256233705, $step->get_timecreated()); $this->assertEquals(1, $step->get_user_id()); $this->assertEquals(array('answer' => '1'), $step->get_all_data()); $step = $qa->get_step(2); $this->assertEquals(question_state::$complete, $step->get_state()); $this->assertNull($step->get_fraction()); $this->assertEquals(1256233710, $step->get_timecreated()); $this->assertEquals(1, $step->get_user_id()); $this->assertEquals(array('answer' => '0'), $step->get_all_data()); $this->assertEquals(2, count($observer->get_steps_deleted())); }
/** * Whether it is possible for another question to depend on this one finishing. * Note that the answer is not exact, because of random questions, and sometimes * questions cannot be depended upon because of quiz options. * @param int $slotnumber the index of the slot in question. * @return bool can this question finish naturally during the attempt? */ public function can_finish_during_the_attempt($slotnumber) { if ($this->quizobj->get_quiz()->shufflequestions || $this->quizobj->get_navigation_method() == QUIZ_NAVMETHOD_SEQ) { return false; } if ($this->get_question_type_for_slot($slotnumber) == 'random') { return true; } if (isset($this->slotsinorder[$slotnumber]->canfinish)) { return $this->slotsinorder[$slotnumber]->canfinish; } $quba = \question_engine::make_questions_usage_by_activity('mod_quiz', $this->quizobj->get_context()); $tempslot = $quba->add_question(\question_bank::load_question($this->slotsinorder[$slotnumber]->questionid)); $quba->set_preferred_behaviour($this->quizobj->get_quiz()->preferredbehaviour); $quba->start_all_questions(); $this->slotsinorder[$slotnumber]->canfinish = $quba->can_question_finish_during_attempt($tempslot); return $this->slotsinorder[$slotnumber]->canfinish; }
/** * Test get_attempt_access_information */ public function test_get_attempt_access_information() { global $DB; // Create a new quiz with attempts. $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz'); $data = array('course' => $this->course->id, 'sumgrades' => 2); $quiz = $quizgenerator->create_instance($data); // Create some 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); $question = $questiongenerator->create_question('shortanswer', null, array('category' => $cat->id)); quiz_add_quiz_question($question->id, $quiz); // Add new question types in the category (for the random one). $question = $questiongenerator->create_question('truefalse', null, array('category' => $cat->id)); $question = $questiongenerator->create_question('essay', null, array('category' => $cat->id)); $question = $questiongenerator->create_question('random', 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(); $this->setUser($this->student); // Default restrictions (none). $result = mod_quiz_external::get_attempt_access_information($quiz->id); $result = external_api::clean_returnvalue(mod_quiz_external::get_attempt_access_information_returns(), $result); $expected = array('isfinished' => false, 'preventnewattemptreasons' => [], 'warnings' => []); $this->assertEquals($expected, $result); // Limited attempts. $quiz->attempts = 1; $DB->update_record('quiz', $quiz); // Now, do one 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); // Can we start a new attempt? We shall not! $result = mod_quiz_external::get_attempt_access_information($quiz->id, $attempt->id); $result = external_api::clean_returnvalue(mod_quiz_external::get_attempt_access_information_returns(), $result); // Now new attemps allowed. $this->assertCount(1, $result['preventnewattemptreasons']); $this->assertFalse($result['ispreflightcheckrequired']); $this->assertEquals(get_string('nomoreattempts', 'quiz'), $result['preventnewattemptreasons'][0]); }
protected function setUp() { parent::setUp(); $this->resetAfterTest(true); $this->displayoptions = new question_display_options(); $this->quba = question_engine::make_questions_usage_by_activity('unit_test', context_system::instance()); }
/** * Creates a single printable copy of the given quiz. * * @return The ID of the created printable question usage. * */ protected function create_printable_copy($shuffle_mode = quiz_papercopy_shuffle_modes::MODE_SHUFFLE_IGNORE_PAGES, $fix_descriptions = false, $fix_first = false, $fix_last = false) { //get a reference to the current user global $USER; //create a new usage object, which will allow us to create a psuedoquiz in the same context as the online quiz $usage = question_engine::make_questions_usage_by_activity('mod_quiz', $this->context); //and set the grading mode to "deferred feedback", the standard for paper quizzes //this makes sense, since our paradigm is duriven by the idea that feedback is only offered once a paper quiz has been uploaded/graded $usage->set_preferred_behaviour('deferredfeedback'); //get an array of questions in the current quiz $quiz_questions = $this->quizobj->get_questions(); //randomize the question order, as requested $quiz_questions = self::shuffle_questions($quiz_questions, $this->quiz->questions, $shuffle_mode, $fix_descriptions, $fix_first, $fix_last); //for each question in our online quiz foreach ($quiz_questions as $slot => $qdata) { $question = question_bank::make_question($qdata); //add the new question instance to our new printable copy, keeping the maximum grade from the quiz //TODO: respect maximum marks $usage->add_question($question); } //initialize each of the questions $usage->start_all_questions(); //save the usage to the database question_engine::save_questions_usage_by_activity($usage); //return the ID of the newly created questions usage return $usage->get_id(); }
/** * Prepare and start a new attempt deleting the previous preview attempts. * * @param quiz $quizobj quiz object * @param int $attemptnumber the attempt number * @param object $lastattempt last attempt object * @param bool $offlineattempt whether is an offline attempt or not * @return object the new attempt * @since Moodle 3.1 */ function quiz_prepare_and_start_new_attempt(quiz $quizobj, $attemptnumber, $lastattempt, $offlineattempt = false) { global $DB, $USER; // Delete any previous preview attempts belonging to this user. quiz_delete_previews($quizobj->get_quiz(), $USER->id); $quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context()); $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour); // Create the new attempt and initialize the question sessions $timenow = time(); // Update time now, in case the server is running really slowly. $attempt = quiz_create_attempt($quizobj, $attemptnumber, $lastattempt, $timenow, $quizobj->is_preview_user()); if (!($quizobj->get_quiz()->attemptonlast && $lastattempt)) { $attempt = quiz_start_new_attempt($quizobj, $quba, $attempt, $attemptnumber, $timenow); } else { $attempt = quiz_start_attempt_built_on_last($quba, $attempt, $lastattempt); } $transaction = $DB->start_delegated_transaction(); // Init the timemodifiedoffline for offline attempts. if ($offlineattempt) { $attempt->timemodifiedoffline = $attempt->timemodified; } $attempt = quiz_attempt_save_started($quizobj, $quba, $attempt); $transaction->allow_commit(); return $attempt; }
$quba = question_engine::load_questions_usage_by_activity($previewid); } catch (Exception $e) { print_error('submissionoutofsequencefriendlymessage', 'question', question_preview_url($question->id, $options->behaviour, $options->maxmark, $options), null, $e); } $slot = $quba->get_first_question_number(); $usedquestion = $quba->get_question($slot); if ($usedquestion->id != $question->id) { print_error('questionidmismatch', 'question'); } $question = $usedquestion; $options->variant = $quba->get_variant($slot); } else { $quba = question_engine::make_questions_usage_by_activity('core_question_preview', get_context_instance_by_id($category->contextid)); $quba->set_preferred_behaviour($options->behaviour); $slot = $quba->add_question($question, $options->maxmark); if ($options->variant) { $options->variant = min($maxvariant, max(1, $options->variant)); } else { $options->variant = rand(1, $maxvariant); } $quba->start_question($slot, $options->variant); $transaction = $DB->start_delegated_transaction(); question_engine::save_questions_usage_by_activity($quba); $transaction->allow_commit();
function emarking_add_user_attempt($cm, $user) { global $DB; // Get the quiz object $quizobj = quiz::create($cm->instance, $user->id); // TODO get to know what usage by activity means $quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context()); $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour); // Create the new attempt and initialize the question sessions $attemptnumber = 1; $lastattempt = null; $timenow = time(); // Update time now, in case the server is running really slowly. $attempt = quiz_create_attempt($quizobj, $attemptnumber, $lastattempt, $timenow, false, $user->id); $attempt = quiz_start_new_attempt($quizobj, $quba, $attempt, $attemptnumber, $timenow); $transaction = $DB->start_delegated_transaction(); $attempt = quiz_attempt_save_started($quizobj, $quba, $attempt); $DB->commit_delegated_transaction($transaction); }
public function test_deferred_feedback_plain_attempt_on_last() { global $CFG, $USER; $this->resetAfterTest(true); $this->setAdminUser(); $usercontextid = context_user::instance($USER->id)->id; // Create an essay question in the DB. $generator = $this->getDataGenerator()->get_plugin_generator('core_question'); $cat = $generator->create_question_category(); $question = $generator->create_question('essay', 'plain', array('category' => $cat->id)); // Start attempt at the question. $q = question_bank::load_question($question->id); $this->start_attempt_at_question($q, 'deferredfeedback', 1); $this->check_current_state(question_state::$todo); $this->check_current_mark(null); $this->check_step_count(1); // Process a response and check the expected result. $this->process_submission(array( 'answer' => 'Once upon a time there was a frog called Freddy. He lived happily ever after.', 'answerformat' => FORMAT_PLAIN, )); $this->check_current_state(question_state::$complete); $this->check_current_mark(null); $this->check_step_count(2); $this->save_quba(); // Now submit all and finish. $this->finish(); $this->check_current_state(question_state::$needsgrading); $this->check_current_mark(null); $this->check_step_count(3); $this->save_quba(); // Now start a new attempt based on the old one. $this->load_quba(); $oldqa = $this->get_question_attempt(); $q = question_bank::load_question($question->id); $this->quba = question_engine::make_questions_usage_by_activity('unit_test', context_system::instance()); $this->quba->set_preferred_behaviour('deferredfeedback'); $this->slot = $this->quba->add_question($q, 1); $this->quba->start_question_based_on($this->slot, $oldqa); $this->check_current_state(question_state::$complete); $this->check_current_mark(null); $this->check_step_count(1); $this->save_quba(); // Check the display. $this->load_quba(); $this->render(); // Test taht no HTML comment has been added to the response. $this->assertRegExp('/Once upon a time there was a frog called Freddy. He lived happily ever after.(?!<!--)/', $this->currentoutput); // Test for the hash of an empty file area. $this->assertNotContains('d41d8cd98f00b204e9800998ecf8427e', $this->currentoutput); }
if (empty($quizobj->get_quiz()->showblocks)) { $PAGE->blocks->show_only_fake_blocks(); } echo $output->start_attempt_page($quizobj, $mform); die; } } // Pre-flight check passed. $accessmanager->notify_preflight_check_passed($currentattemptid); } if ($currentattemptid) { redirect($quizobj->attempt_url($currentattemptid, $page)); } // Delete any previous preview attempts belonging to this user. quiz_delete_previews($quizobj->get_quiz(), $USER->id); $quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context()); $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour); // Create the new attempt and initialize the question sessions $timenow = time(); // Update time now, in case the server is running really slowly. $attempt = quiz_create_attempt($quizobj, $attemptnumber, $lastattempt, $timenow, $quizobj->is_preview_user()); if (!($quizobj->get_quiz()->attemptonlast && $lastattempt)) { // Starting a normal, new, quiz attempt. // Fully load all the questions in this quiz. $quizobj->preload_questions(); $quizobj->load_questions(); // Add them all to the $quba. $idstoslots = array(); $questionsinuse = array_keys($quizobj->get_questions()); foreach ($quizobj->get_questions() as $i => $questiondata) { if ($questiondata->qtype != 'random') {
public function test_access_out_of_sequence_throws_exception() { // Start a deferred feedback attempt with CBM and add the question to it. $tf = test_question_maker::make_question('truefalse', 'true'); $quba = question_engine::make_questions_usage_by_activity('unit_test', context_system::instance()); $quba->set_preferred_behaviour('deferredcbm'); $slot = $quba->add_question($tf); $quba->start_all_questions(); // Prepare data to be submitted $prefix = $quba->get_field_prefix($slot); $answername = $prefix . 'answer'; $certaintyname = $prefix . '-certainty'; $postdata = array( $answername => 1, $certaintyname => 3, $prefix . ':sequencecheck' => 1, 'irrelevant' => 'should be ignored', ); // Exercise SUT - no exception yet. $quba->process_all_actions($slot, $postdata); $postdata = array( $answername => 1, $certaintyname => 3, $prefix . ':sequencecheck' => 3, 'irrelevant' => 'should be ignored', ); // Exercise SUT - now it should fail. $this->setExpectedException('question_out_of_sequence_exception'); $quba->process_all_actions($slot, $postdata); }
/** * Utility method to submit an attempt on a quiz. * @param $quiz * @param $user * @param $answers * @return testable_assign */ private function submit_quiz_attempt($quiz, $user, $answers) { // Create a quiz attempt for the user. $quizobj = quiz::create($quiz->id, $user->id); // Set up and start an 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, $user->id); quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow); quiz_attempt_save_started($quizobj, $quba, $attempt); $attemptobj = quiz_attempt::create($attempt->id); $attemptobj->process_submitted_actions($timenow, false, $answers); $timefinish = time(); // Finish the attempt. $attemptobj->process_finish($timefinish, false); return $timefinish; }
/** * Starts an attempt and returns the object representing it. * * @param int $quizid * @param int $userid * @throws moodle_exception * @return \stdClass */ public function start_quiz_attempt($quizid, $userid) { global $DB; $quizobj = quiz::create($quizid, $userid); $timenow = time(); $attempt = quiz_create_attempt($quizobj->get_quiz(), 1, false, $timenow, false); // Taken from /mod/quiz/startattempt.php. $quba = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj->get_context()); $quba->set_preferred_behaviour($quizobj->get_quiz()->preferredbehaviour); // Starting a normal, new, quiz attempt. // Fully load all the questions in this quiz. $quizobj->preload_questions(); $quizobj->load_questions(); // Add them all to the $quba. $idstoslots = array(); $questionsinuse = array_keys($quizobj->get_questions()); foreach ($quizobj->get_questions() as $i => $questiondata) { if ($questiondata->qtype != 'random') { if (!$quizobj->get_quiz()->shuffleanswers) { $questiondata->options->shuffleanswers = false; } $question = question_bank::make_question($questiondata); } else { $question = question_bank::get_qtype('random')->choose_other_question($questiondata, $questionsinuse, $quizobj->get_quiz()->shuffleanswers); if (is_null($question)) { throw new moodle_exception('notenoughrandomquestions', 'quiz', $quizobj->view_url(), $questiondata); } } $idstoslots[$i] = $quba->add_question($question, $questiondata->maxmark); $questionsinuse[] = $question->id; } // Start all the questions. if ($attempt->preview) { $variantoffset = rand(1, 100); } else { $variantoffset = 1; } $quba->start_all_questions(new question_variant_pseudorandom_no_repeats_strategy($variantoffset), $timenow); // Update attempt layout. $newlayout = array(); foreach (explode(',', $attempt->layout) as $qid) { if ($qid != 0) { $newlayout[] = $idstoslots[$qid]; } else { $newlayout[] = 0; } } $attempt->layout = implode(',', $newlayout); question_engine::save_questions_usage_by_activity($quba); $attempt->uniqueid = $quba->get_id(); $attempt->id = $DB->insert_record('quiz_attempts', $attempt); return $attempt; }
// This may not seem like the right error message to display, but // actually from the user point of view, it makes sense. print_error('submissionoutofsequencefriendlymessage', 'question', question_preview_url($question->id, $options->behaviour, $options->maxmark, $options, $options->variant, $context), null, $e); } if ($quba->get_owning_context()->instanceid != $USER->id) { print_error('notyourpreview', 'question'); } $slot = $quba->get_first_question_number(); $usedquestion = $quba->get_question($slot); if ($usedquestion->id != $question->id) { print_error('questionidmismatch', 'question'); } $question = $usedquestion; $options->variant = $quba->get_variant($slot); } else { $quba = question_engine::make_questions_usage_by_activity('core_question_preview', context_user::instance($USER->id)); $quba->set_preferred_behaviour($options->behaviour); $slot = $quba->add_question($question, $options->maxmark); if ($options->variant) { $options->variant = min($maxvariant, max(1, $options->variant)); } else { $options->variant = rand(1, $maxvariant); } $quba->start_question($slot, $options->variant); $transaction = $DB->start_delegated_transaction(); question_engine::save_questions_usage_by_activity($quba); $transaction->allow_commit(); } $options->behaviour = $quba->get_preferred_behaviour(); $options->maxmark = $quba->get_question_max_mark($slot); // Create the settings form, and initialise the fields.
/** * Test checking the completion state of a quiz. */ public function test_quiz_get_completion_state() { global $CFG, $DB; $this->resetAfterTest(true); // Enable completion before creating modules, otherwise the completion data is not written in DB. $CFG->enablecompletion = true; // Create a course and student. $course = $this->getDataGenerator()->create_course(array('enablecompletion' => true)); $passstudent = $this->getDataGenerator()->create_user(); $failstudent = $this->getDataGenerator()->create_user(); $studentrole = $DB->get_record('role', array('shortname' => 'student')); $this->assertNotEmpty($studentrole); // Enrol students. $this->assertTrue($this->getDataGenerator()->enrol_user($passstudent->id, $course->id, $studentrole->id)); $this->assertTrue($this->getDataGenerator()->enrol_user($failstudent->id, $course->id, $studentrole->id)); // Make a scale and an outcome. $scale = $this->getDataGenerator()->create_scale(); $data = array('courseid' => $course->id, 'fullname' => 'Team work', 'shortname' => 'Team work', 'scaleid' => $scale->id); $outcome = $this->getDataGenerator()->create_grade_outcome($data); // Make a quiz with the outcome on. $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz'); $data = array('course' => $course->id, 'outcome_' . $outcome->id => 1, 'grade' => 100.0, 'questionsperpage' => 0, 'sumgrades' => 1, 'completion' => COMPLETION_TRACKING_AUTOMATIC, 'completionpass' => 1); $quiz = $quizgenerator->create_instance($data); $cm = get_coursemodule_from_id('quiz', $quiz->cmid); // 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, $passstudent->id); // Set grade to pass. $item = grade_item::fetch(array('courseid' => $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, $passstudent->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); // Start the failing 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, $failstudent->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' => '0')); $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); // Check the results. $this->assertTrue(quiz_get_completion_state($course, $cm, $passstudent->id, 'return')); $this->assertFalse(quiz_get_completion_state($course, $cm, $failstudent->id, 'return')); }
try { $quba = question_engine::load_questions_usage_by_activity($previewid); } catch (Exception $e) { // This may not seem like the right error message to display, but // actually from the user point of view, it makes sense. print_error('submissionoutofsequencefriendlymessage', 'question', question_preview_url($question->id, $options->behaviour, $options->maxmark, $options, $options->variant, $context), null, $e); } $slot = $quba->get_first_question_number(); $usedquestion = $quba->get_question($slot); if ($usedquestion->id != $question->id) { print_error('questionidmismatch', 'question'); } $question = $usedquestion; $options->variant = $quba->get_variant($slot); } else { $quba = question_engine::make_questions_usage_by_activity('core_question_preview', $context); $quba->set_preferred_behaviour($options->behaviour); $slot = $quba->add_question($question, $options->maxmark); if ($options->variant) { $options->variant = min($maxvariant, max(1, $options->variant)); } else { $options->variant = rand(1, $maxvariant); } $quba->start_question($slot, $options->variant); $transaction = $DB->start_delegated_transaction(); question_engine::save_questions_usage_by_activity($quba); $transaction->allow_commit(); $SESSION->question_previews[$quba->get_id()] = true; } $options->behaviour = $quba->get_preferred_behaviour(); $options->maxmark = $quba->get_question_max_mark($slot);
public function test_quiz_get_user_attempts() { global $DB; $this->resetAfterTest(); $dg = $this->getDataGenerator(); $quizgen = $dg->get_plugin_generator('mod_quiz'); $course = $dg->create_course(); $u1 = $dg->create_user(); $u2 = $dg->create_user(); $u3 = $dg->create_user(); $u4 = $dg->create_user(); $role = $DB->get_record('role', ['shortname' => 'student']); $dg->enrol_user($u1->id, $course->id, $role->id); $dg->enrol_user($u2->id, $course->id, $role->id); $dg->enrol_user($u3->id, $course->id, $role->id); $dg->enrol_user($u4->id, $course->id, $role->id); $quiz1 = $quizgen->create_instance(['course' => $course->id, 'sumgrades' => 2]); $quiz2 = $quizgen->create_instance(['course' => $course->id, 'sumgrades' => 2]); // Questions. $questgen = $dg->get_plugin_generator('core_question'); $quizcat = $questgen->create_question_category(); $question = $questgen->create_question('numerical', null, ['category' => $quizcat->id]); quiz_add_quiz_question($question->id, $quiz1); quiz_add_quiz_question($question->id, $quiz2); $quizobj1a = quiz::create($quiz1->id, $u1->id); $quizobj1b = quiz::create($quiz1->id, $u2->id); $quizobj1c = quiz::create($quiz1->id, $u3->id); $quizobj1d = quiz::create($quiz1->id, $u4->id); $quizobj2a = quiz::create($quiz2->id, $u1->id); // Set attempts. $quba1a = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj1a->get_context()); $quba1a->set_preferred_behaviour($quizobj1a->get_quiz()->preferredbehaviour); $quba1b = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj1b->get_context()); $quba1b->set_preferred_behaviour($quizobj1b->get_quiz()->preferredbehaviour); $quba1c = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj1c->get_context()); $quba1c->set_preferred_behaviour($quizobj1c->get_quiz()->preferredbehaviour); $quba1d = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj1d->get_context()); $quba1d->set_preferred_behaviour($quizobj1d->get_quiz()->preferredbehaviour); $quba2a = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj2a->get_context()); $quba2a->set_preferred_behaviour($quizobj2a->get_quiz()->preferredbehaviour); $timenow = time(); // User 1 passes quiz 1. $attempt = quiz_create_attempt($quizobj1a, 1, false, $timenow, false, $u1->id); quiz_start_new_attempt($quizobj1a, $quba1a, $attempt, 1, $timenow); quiz_attempt_save_started($quizobj1a, $quba1a, $attempt); $attemptobj = quiz_attempt::create($attempt->id); $attemptobj->process_submitted_actions($timenow, false, [1 => ['answer' => '3.14']]); $attemptobj->process_finish($timenow, false); // User 2 goes overdue in quiz 1. $attempt = quiz_create_attempt($quizobj1b, 1, false, $timenow, false, $u2->id); quiz_start_new_attempt($quizobj1b, $quba1b, $attempt, 1, $timenow); quiz_attempt_save_started($quizobj1b, $quba1b, $attempt); $attemptobj = quiz_attempt::create($attempt->id); $attemptobj->process_going_overdue($timenow, true); // User 3 does not finish quiz 1. $attempt = quiz_create_attempt($quizobj1c, 1, false, $timenow, false, $u3->id); quiz_start_new_attempt($quizobj1c, $quba1c, $attempt, 1, $timenow); quiz_attempt_save_started($quizobj1c, $quba1c, $attempt); // User 4 abandons the quiz 1. $attempt = quiz_create_attempt($quizobj1d, 1, false, $timenow, false, $u4->id); quiz_start_new_attempt($quizobj1d, $quba1d, $attempt, 1, $timenow); quiz_attempt_save_started($quizobj1d, $quba1d, $attempt); $attemptobj = quiz_attempt::create($attempt->id); $attemptobj->process_abandon($timenow, true); // User 1 attempts the quiz three times (abandon, finish, in progress). $quba2a = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj2a->get_context()); $quba2a->set_preferred_behaviour($quizobj2a->get_quiz()->preferredbehaviour); $attempt = quiz_create_attempt($quizobj2a, 1, false, $timenow, false, $u1->id); quiz_start_new_attempt($quizobj2a, $quba2a, $attempt, 1, $timenow); quiz_attempt_save_started($quizobj2a, $quba2a, $attempt); $attemptobj = quiz_attempt::create($attempt->id); $attemptobj->process_abandon($timenow, true); $quba2a = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj2a->get_context()); $quba2a->set_preferred_behaviour($quizobj2a->get_quiz()->preferredbehaviour); $attempt = quiz_create_attempt($quizobj2a, 2, false, $timenow, false, $u1->id); quiz_start_new_attempt($quizobj2a, $quba2a, $attempt, 2, $timenow); quiz_attempt_save_started($quizobj2a, $quba2a, $attempt); $attemptobj = quiz_attempt::create($attempt->id); $attemptobj->process_finish($timenow, false); $quba2a = question_engine::make_questions_usage_by_activity('mod_quiz', $quizobj2a->get_context()); $quba2a->set_preferred_behaviour($quizobj2a->get_quiz()->preferredbehaviour); $attempt = quiz_create_attempt($quizobj2a, 3, false, $timenow, false, $u1->id); quiz_start_new_attempt($quizobj2a, $quba2a, $attempt, 3, $timenow); quiz_attempt_save_started($quizobj2a, $quba2a, $attempt); // Check for user 1. $attempts = quiz_get_user_attempts($quiz1->id, $u1->id, 'all'); $this->assertCount(1, $attempts); $attempt = array_shift($attempts); $this->assertEquals(quiz_attempt::FINISHED, $attempt->state); $this->assertEquals($u1->id, $attempt->userid); $this->assertEquals($quiz1->id, $attempt->quiz); $attempts = quiz_get_user_attempts($quiz1->id, $u1->id, 'finished'); $this->assertCount(1, $attempts); $attempt = array_shift($attempts); $this->assertEquals(quiz_attempt::FINISHED, $attempt->state); $this->assertEquals($u1->id, $attempt->userid); $this->assertEquals($quiz1->id, $attempt->quiz); $attempts = quiz_get_user_attempts($quiz1->id, $u1->id, 'unfinished'); $this->assertCount(0, $attempts); // Check for user 2. $attempts = quiz_get_user_attempts($quiz1->id, $u2->id, 'all'); $this->assertCount(1, $attempts); $attempt = array_shift($attempts); $this->assertEquals(quiz_attempt::OVERDUE, $attempt->state); $this->assertEquals($u2->id, $attempt->userid); $this->assertEquals($quiz1->id, $attempt->quiz); $attempts = quiz_get_user_attempts($quiz1->id, $u2->id, 'finished'); $this->assertCount(0, $attempts); $attempts = quiz_get_user_attempts($quiz1->id, $u2->id, 'unfinished'); $this->assertCount(1, $attempts); $attempt = array_shift($attempts); $this->assertEquals(quiz_attempt::OVERDUE, $attempt->state); $this->assertEquals($u2->id, $attempt->userid); $this->assertEquals($quiz1->id, $attempt->quiz); // Check for user 3. $attempts = quiz_get_user_attempts($quiz1->id, $u3->id, 'all'); $this->assertCount(1, $attempts); $attempt = array_shift($attempts); $this->assertEquals(quiz_attempt::IN_PROGRESS, $attempt->state); $this->assertEquals($u3->id, $attempt->userid); $this->assertEquals($quiz1->id, $attempt->quiz); $attempts = quiz_get_user_attempts($quiz1->id, $u3->id, 'finished'); $this->assertCount(0, $attempts); $attempts = quiz_get_user_attempts($quiz1->id, $u3->id, 'unfinished'); $this->assertCount(1, $attempts); $attempt = array_shift($attempts); $this->assertEquals(quiz_attempt::IN_PROGRESS, $attempt->state); $this->assertEquals($u3->id, $attempt->userid); $this->assertEquals($quiz1->id, $attempt->quiz); // Check for user 4. $attempts = quiz_get_user_attempts($quiz1->id, $u4->id, 'all'); $this->assertCount(1, $attempts); $attempt = array_shift($attempts); $this->assertEquals(quiz_attempt::ABANDONED, $attempt->state); $this->assertEquals($u4->id, $attempt->userid); $this->assertEquals($quiz1->id, $attempt->quiz); $attempts = quiz_get_user_attempts($quiz1->id, $u4->id, 'finished'); $this->assertCount(1, $attempts); $attempt = array_shift($attempts); $this->assertEquals(quiz_attempt::ABANDONED, $attempt->state); $this->assertEquals($u4->id, $attempt->userid); $this->assertEquals($quiz1->id, $attempt->quiz); $attempts = quiz_get_user_attempts($quiz1->id, $u4->id, 'unfinished'); $this->assertCount(0, $attempts); // Multiple attempts for user 1 in quiz 2. $attempts = quiz_get_user_attempts($quiz2->id, $u1->id, 'all'); $this->assertCount(3, $attempts); $attempt = array_shift($attempts); $this->assertEquals(quiz_attempt::ABANDONED, $attempt->state); $this->assertEquals($u1->id, $attempt->userid); $this->assertEquals($quiz2->id, $attempt->quiz); $attempt = array_shift($attempts); $this->assertEquals(quiz_attempt::FINISHED, $attempt->state); $this->assertEquals($u1->id, $attempt->userid); $this->assertEquals($quiz2->id, $attempt->quiz); $attempt = array_shift($attempts); $this->assertEquals(quiz_attempt::IN_PROGRESS, $attempt->state); $this->assertEquals($u1->id, $attempt->userid); $this->assertEquals($quiz2->id, $attempt->quiz); $attempts = quiz_get_user_attempts($quiz2->id, $u1->id, 'finished'); $this->assertCount(2, $attempts); $attempt = array_shift($attempts); $this->assertEquals(quiz_attempt::ABANDONED, $attempt->state); $attempt = array_shift($attempts); $this->assertEquals(quiz_attempt::FINISHED, $attempt->state); $attempts = quiz_get_user_attempts($quiz2->id, $u1->id, 'unfinished'); $this->assertCount(1, $attempts); $attempt = array_shift($attempts); // Multiple quiz attempts fetched at once. $attempts = quiz_get_user_attempts([$quiz1->id, $quiz2->id], $u1->id, 'all'); $this->assertCount(4, $attempts); $attempt = array_shift($attempts); $this->assertEquals(quiz_attempt::FINISHED, $attempt->state); $this->assertEquals($u1->id, $attempt->userid); $this->assertEquals($quiz1->id, $attempt->quiz); $attempt = array_shift($attempts); $this->assertEquals(quiz_attempt::ABANDONED, $attempt->state); $this->assertEquals($u1->id, $attempt->userid); $this->assertEquals($quiz2->id, $attempt->quiz); $attempt = array_shift($attempts); $this->assertEquals(quiz_attempt::FINISHED, $attempt->state); $this->assertEquals($u1->id, $attempt->userid); $this->assertEquals($quiz2->id, $attempt->quiz); $attempt = array_shift($attempts); $this->assertEquals(quiz_attempt::IN_PROGRESS, $attempt->state); $this->assertEquals($u1->id, $attempt->userid); $this->assertEquals($quiz2->id, $attempt->quiz); }
/** * This function is a copy of code taken from attempt_walkthrough_test.php * that creates a sample quiz and has a student complete the quiz. * * If the test suddenly stops working, grab the new code from that file, then * adjust to required inputs/outputs. * * @param object $course The course object to create the quiz in * @param object $student The student object that takes the quiz * @return array {attempt ID (int), quiz ID (int)} * */ private function create_quiz_attempt($course, $student) { // Make a quiz. $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz'); $quiz = $quizgenerator->create_instance(array('course' => $course->id, 'questionsperpage' => 0, 'grade' => 100.0, 'sumgrades' => 2)); // Create a couple of questions. $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); $cat = $questiongenerator->create_question_category(); $saq = $questiongenerator->create_question('shortanswer', null, array('category' => $cat->id)); $numq = $questiongenerator->create_question('numerical', null, array('category' => $cat->id)); // Add them to the quiz. quiz_add_quiz_question($saq->id, $quiz); quiz_add_quiz_question($numq->id, $quiz); $quizobj = quiz::create($quiz->id, $student->id); // Start the 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, $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); $prefix1 = $quba->get_field_prefix(1); $prefix2 = $quba->get_field_prefix(2); $tosubmit = array(1 => array('answer' => 'frog'), 2 => array('answer' => '3.14')); $attemptobj->process_submitted_actions($timenow, false, $tosubmit); // Finish the attempt. $attemptobj = quiz_attempt::create($attempt->id); $attemptobj->process_finish($timenow, false); // Re-load quiz attempt data. $attemptobj = quiz_attempt::create($attempt->id); return array($attempt->id, $quiz->id); }
while ($failedattempts < $maxfailedattempts && $numberdeployed < $deploy) { // Genrate a new seed. $seed = mt_rand(); $variantdeployed = false; // Reload the question to ensure any new deployed version is included. $question = question_bank::load_question($questionid); $question->seed = (int) $seed; $quba = question_engine::make_questions_usage_by_activity('qtype_stack', $context); $quba->set_preferred_behaviour('adaptive'); $slot = $quba->add_question($question, $question->defaultmark); $quba->start_question($slot); foreach ($question->deployedseeds as $key => $deployedseed) { $qn = question_bank::load_question($questionid); $qn->seed = (int) $deployedseed; $cn = $qn->get_context(); $qunote = question_engine::make_questions_usage_by_activity('qtype_stack', $cn); $qunote->set_preferred_behaviour('adaptive'); $slotnote = $qunote->add_question($qn, $qn->defaultmark); $qunote->start_question($slotnote); // Check if the question note has already been deployed. if ($qn->get_question_summary() == $question->get_question_summary()) { $variantdeployed = true; $failedattempts++; } } if (!$variantdeployed) { // Load the list of test cases. $testscases = question_bank::get_qtype('stack')->load_question_tests($question->id); // Exectue the tests. $testresults = array(); $allpassed = true;
/** * Create a quiz with a single question with variants and walk through quiz attempts. * * @dataProvider get_correct_response_for_variants */ public function test_quiz_with_question_with_variants_attempt_walkthrough($variantno, $correctresponse, $done = false) { global $SITE; $this->resetAfterTest($done); $this->setAdminUser(); if ($this->quizwithvariants === null) { // Make a quiz. $quizgenerator = $this->getDataGenerator()->get_plugin_generator('mod_quiz'); $this->quizwithvariants = $quizgenerator->create_instance(array('course'=>$SITE->id, 'questionsperpage' => 0, 'grade' => 100.0, 'sumgrades' => 1)); $questiongenerator = $this->getDataGenerator()->get_plugin_generator('core_question'); $cat = $questiongenerator->create_question_category(); $calc = $questiongenerator->create_question('calculatedsimple', 'sumwithvariants', array('category' => $cat->id)); quiz_add_quiz_question($calc->id, $this->quizwithvariants, 0); } // Make a new user to do the quiz. $user1 = $this->getDataGenerator()->create_user(); $this->setUser($user1); $quizobj = quiz::create($this->quizwithvariants->id, $user1->id); // Start the 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); // Select variant. quiz_start_new_attempt($quizobj, $quba, $attempt, 1, $timenow, array(), array(1 => $variantno)); $this->assertEquals('1,0', $attempt->layout); quiz_attempt_save_started($quizobj, $quba, $attempt); // Process some responses from the student. $attemptobj = quiz_attempt::create($attempt->id); $tosubmit = array(1 => array('answer' => $correctresponse)); $attemptobj->process_submitted_actions($timenow, false, $tosubmit); // Finish the attempt. $attemptobj = quiz_attempt::create($attempt->id); $attemptobj->process_finish($timenow, false); // Re-load quiz attempt data. $attemptobj = quiz_attempt::create($attempt->id); // Check that results are stored as expected. $this->assertEquals(1, $attemptobj->get_attempt_number()); $this->assertEquals(1, $attemptobj->get_sum_marks()); $this->assertEquals(true, $attemptobj->is_finished()); $this->assertEquals($timenow, $attemptobj->get_submitted_date()); $this->assertEquals($user1->id, $attemptobj->get_userid()); // Check quiz grades. $grades = quiz_get_user_grades($this->quizwithvariants, $user1->id); $grade = array_shift($grades); $this->assertEquals(100.0, $grade->rawgrade); // Check grade book. $gradebookgrades = grade_get_grades($SITE->id, 'mod', 'quiz', $this->quizwithvariants->id, $user1->id); $gradebookitem = array_shift($gradebookgrades->items); $gradebookgrade = array_shift($gradebookitem->grades); $this->assertEquals(100, $gradebookgrade->grade); }