first commit

This commit is contained in:
CHIEFSOFT\ameye
2024-09-30 18:11:26 -04:00
commit e592ca6823
27270 changed files with 5002257 additions and 0 deletions
@@ -0,0 +1,120 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Question behaviour for questions that can only be graded manually.
*
* @package qbehaviour
* @subpackage manualgraded
* @copyright 2009 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Question behaviour for questions that can only be graded manually.
*
* The student enters their response during the attempt, and it is saved. Later,
* when the whole attempt is finished, the attempt goes into the NEEDS_GRADING
* state, and the teacher must grade it manually.
*
* @copyright 2009 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qbehaviour_manualgraded extends question_behaviour_with_save {
public function is_compatible_question(question_definition $question) {
return $question instanceof question_with_responses;
}
public function adjust_display_options(question_display_options $options) {
parent::adjust_display_options($options);
if ($this->qa->get_state()->is_finished()) {
// Hide all feedback except genfeedback and manualcomment.
$save = clone($options);
$options->hide_all_feedback();
$options->generalfeedback = $save->generalfeedback;
$options->manualcomment = $save->manualcomment;
}
}
public function process_action(question_attempt_pending_step $pendingstep) {
if ($pendingstep->has_behaviour_var('comment')) {
return $this->process_comment($pendingstep);
} else if ($pendingstep->has_behaviour_var('finish')) {
return $this->process_finish($pendingstep);
} else {
return $this->process_save($pendingstep);
}
}
/**
* Like the parent method, except that when a response is gradable, but not
* completely, we move it to the invalid state.
* @param question_attempt_pending_step $pendingstep a partially initialised step
* containing all the information about the action that is being performed.
* @return bool either {@link question_attempt::KEEP} or {@link question_attempt::DISCARD}
*/
public function process_save(question_attempt_pending_step $pendingstep) {
if ($this->qa->get_state()->is_finished()) {
return question_attempt::DISCARD;
} else if (!$this->qa->get_state()->is_active()) {
throw new coding_exception('Question is not active, cannot process_actions.');
}
if ($this->is_same_response($pendingstep)) {
return question_attempt::DISCARD;
}
if ($this->is_complete_response($pendingstep)) {
$pendingstep->set_state(question_state::$complete);
} else if ($this->question->is_gradable_response($pendingstep->get_qt_data())) {
$pendingstep->set_state(question_state::$invalid);
} else {
$pendingstep->set_state(question_state::$todo);
}
return question_attempt::KEEP;
}
public function summarise_action(question_attempt_step $step) {
if ($step->has_behaviour_var('comment')) {
return $this->summarise_manual_comment($step);
} else if ($step->has_behaviour_var('finish')) {
return $this->summarise_finish($step);
} else {
return $this->summarise_save($step);
}
}
public function process_finish(question_attempt_pending_step $pendingstep) {
if ($this->qa->get_state()->is_finished()) {
return question_attempt::DISCARD;
}
$response = $this->qa->get_last_step()->get_qt_data();
if (!$this->question->is_gradable_response($response)) {
$pendingstep->set_state(question_state::$gaveup);
} else {
$pendingstep->set_state(question_state::$needsgrading);
}
$pendingstep->set_new_response_summary($this->question->summarise_response($response));
return question_attempt::KEEP;
}
}
@@ -0,0 +1,44 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Question behaviour type for manually graded behaviour.
*
* @package qbehaviour_manualgraded
* @copyright 2012 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Question behaviour type information for manually graded behaviour.
*
* @copyright 2012 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qbehaviour_manualgraded_type extends question_behaviour_type {
public function is_archetypal() {
return true;
}
public function get_unused_display_options() {
return array('correctness', 'marks', 'specificfeedback', 'generalfeedback',
'rightanswer');
}
}
@@ -0,0 +1,46 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Privacy Subsystem implementation for qbehaviour_manualgraded.
*
* @package qbehaviour_manualgraded
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
namespace qbehaviour_manualgraded\privacy;
defined('MOODLE_INTERNAL') || die();
/**
* Privacy Subsystem for qbehaviour_manualgraded implementing null_provider.
*
* @copyright 2018 Andrew Nicols <andrew@nicols.co.uk>
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class provider implements \core_privacy\local\metadata\null_provider {
/**
* Get the language string identifier with the component's language
* file to explain why this plugin stores no data.
*
* @return string
*/
public static function get_reason(): string {
return 'privacy:metadata';
}
}
@@ -0,0 +1,49 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Post-install script for manual graded question behaviour.
* @package qbehaviour_manualgraded
* @copyright 2013 The Open Universtiy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Post-install script
*/
function xmldb_qbehaviour_manualgraded_install() {
// Hide the manualgraded behaviour from the list of behaviours that users
// can select in the user-interface. If a user accidentally chooses manual
// graded behaviour for a quiz, there is no way to get the questions automatically
// graded after the student has answered them. If teachers really want to do
// this they can ask their admin to enable it on the manage behaviours
// screen in the UI.
$disabledbehaviours = get_config('question', 'disabledbehaviours');
if (!empty($disabledbehaviours)) {
$disabledbehaviours = explode(',', $disabledbehaviours);
} else {
$disabledbehaviours = array();
}
if (array_search('manualgraded', $disabledbehaviours) === false) {
$disabledbehaviours[] = 'manualgraded';
set_config('disabledbehaviours', implode(',', $disabledbehaviours), 'question');
}
}
@@ -0,0 +1,42 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Post-install script for the manual graded question behaviour.
*
* @package qbehaviour_manualgraded
* @copyright 2013 The Open Universtiy
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
/**
* Manual graded question behaviour upgrade code.
*/
function xmldb_qbehaviour_manualgraded_upgrade($oldversion) {
// Automatically generated Moodle v4.1.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.2.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.3.0 release upgrade line.
// Put any upgrade step following this.
// Automatically generated Moodle v4.4.0 release upgrade line.
// Put any upgrade step following this.
return true;
}
@@ -0,0 +1,27 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Strings for component 'qbehaviour_manualgraded', language 'en'.
*
* @package qbehaviour
* @subpackage manualgraded
* @copyright 2009 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
$string['pluginname'] = 'Manually graded';
$string['privacy:metadata'] = 'The Manually graded question behaviour plugin does not store any personal data.';
@@ -0,0 +1,38 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Defines the renderer for the manual graded behaviour.
*
* @package qbehaviour
* @subpackage manualgraded
* @copyright 2009 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
/**
* Renderer for outputting parts of a question belonging to the manual
* graded behaviour.
*
* @copyright 2009 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class qbehaviour_manualgraded_renderer extends qbehaviour_renderer {
}
@@ -0,0 +1,63 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace qbehaviour_manualgraded;
use question_engine;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once(__DIR__ . '/../../../engine/lib.php');
require_once(__DIR__ . '/../../../engine/tests/helpers.php');
/**
* Unit tests for the manually graded behaviour type class.
*
* @package qbehaviour_manualgraded
* @category test
* @copyright 2015 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class behaviour_type_test extends \basic_testcase {
/** @var qbehaviour_manualgraded_type */
protected $behaviourtype;
public function setUp(): void {
parent::setUp();
$this->behaviourtype = question_engine::get_behaviour_type('manualgraded');
}
public function test_is_archetypal(): void {
$this->assertTrue($this->behaviourtype->is_archetypal());
}
public function test_get_unused_display_options(): void {
$this->assertEquals(array('correctness', 'marks', 'specificfeedback', 'generalfeedback', 'rightanswer'),
$this->behaviourtype->get_unused_display_options());
}
public function test_can_questions_finish_during_the_attempt(): void {
$this->assertFalse($this->behaviourtype->can_questions_finish_during_the_attempt());
}
public function test_adjust_random_guess_score(): void {
$this->assertEquals(0, $this->behaviourtype->adjust_random_guess_score(0));
$this->assertEquals(1, $this->behaviourtype->adjust_random_guess_score(1));
}
}
@@ -0,0 +1,717 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
namespace qbehaviour_manualgraded;
use question_display_options;
use question_state;
defined('MOODLE_INTERNAL') || die();
global $CFG;
require_once(__DIR__ . '/../../../engine/lib.php');
require_once(__DIR__ . '/../../../engine/tests/helpers.php');
/**
* Unit tests for the manual graded behaviour.
*
* @package qbehaviour_manualgraded
* @copyright 2009 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
class walkthrough_test extends \qbehaviour_walkthrough_test_base {
public function test_manual_graded_essay(): void {
global $PAGE;
// The current text editor depends on the users profile setting - so it needs a valid user.
$this->setAdminUser();
// Required to init a text editor.
$PAGE->set_url('/');
// Create an essay question.
$essay = \test_question_maker::make_an_essay_question();
$this->start_attempt_at_question($essay, 'deferredfeedback', 10);
// Check the right model is being used.
$this->assertEquals('manualgraded', $this->quba->get_question_attempt(
$this->slot)->get_behaviour_name());
// Check the initial state.
$this->check_current_state(question_state::$todo);
$this->check_current_mark(null);
$this->check_current_output($this->get_contains_question_text_expectation($essay),
$this->get_does_not_contain_feedback_expectation());
// Simulate some data submitted by the student.
$this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
// Verify.
$this->check_current_state(question_state::$complete);
$this->check_current_mark(null);
$this->check_current_output(
new \question_contains_tag_with_attribute('textarea', 'name',
$this->quba->get_question_attempt($this->slot)->get_qt_field_name('answer')),
$this->get_does_not_contain_feedback_expectation());
// Process the same data again, check it does not create a new step.
$numsteps = $this->get_step_count();
$this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
$this->check_step_count($numsteps);
// Process different data, check it creates a new step.
$this->process_submission(array('answer' => '', 'answerformat' => FORMAT_HTML));
$this->check_step_count($numsteps + 1);
$this->check_current_state(question_state::$todo);
// Change back, check it creates a new step.
$this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
$this->check_step_count($numsteps + 2);
// Finish the attempt.
$this->quba->finish_all_questions();
// Verify.
$this->check_current_state(question_state::$needsgrading);
$this->check_current_mark(null);
$this->assertEquals('This is my wonderful essay!',
$this->quba->get_response_summary($this->slot));
// Process a manual comment.
$this->manual_grade('Not good enough!', 10, FORMAT_HTML);
// Verify.
$this->check_current_state(question_state::$mangrright);
$this->check_current_mark(10);
$this->check_current_output(
new \question_pattern_expectation('/' . preg_quote('Not good enough!', '/') . '/'));
// Now change the max mark for the question and regrade.
$this->quba->regrade_question($this->slot, true, 1);
// Verify.
$this->check_current_state(question_state::$mangrright);
$this->check_current_mark(1);
}
public function test_manual_graded_essay_not_answered(): void {
global $PAGE;
// The current text editor depends on the users profile setting - so it needs a valid user.
$this->setAdminUser();
// Required to init a text editor.
$PAGE->set_url('/');
// Create an essay question.
$essay = \test_question_maker::make_an_essay_question();
$this->start_attempt_at_question($essay, 'deferredfeedback', 10);
// Check the right model is being used.
$this->assertEquals('manualgraded', $this->quba->get_question_attempt(
$this->slot)->get_behaviour_name());
// Check the initial state.
$this->check_current_state(question_state::$todo);
$this->check_current_mark(null);
$this->check_current_output($this->get_contains_question_text_expectation($essay),
$this->get_does_not_contain_feedback_expectation());
// Finish the attempt.
$this->quba->finish_all_questions();
// Verify.
$this->check_current_state(question_state::$gaveup);
$this->check_current_mark(null);
$this->assertEquals('',
$this->quba->get_response_summary($this->slot));
// Process a manual comment.
$this->manual_grade('Not good enough!', 1, FORMAT_HTML);
// Verify.
$this->check_current_state(question_state::$mangrpartial);
$this->check_current_mark(1);
$this->check_current_output(
new \question_pattern_expectation('/' . preg_quote('Not good enough!') . '/'));
// Now change the max mark for the question and regrade.
$this->quba->regrade_question($this->slot, true, 1);
// Verify.
$this->check_current_state(question_state::$mangrpartial);
$this->check_current_mark(0.1);
}
public function test_manual_graded_truefalse(): void {
// Create a true-false question with correct answer true.
$tf = \test_question_maker::make_question('truefalse', 'true');
$this->start_attempt_at_question($tf, 'manualgraded', 2);
// Check the initial state.
$this->check_current_state(question_state::$todo);
$this->check_current_mark(null);
$this->check_current_output(
$this->get_contains_question_text_expectation($tf),
$this->get_does_not_contain_feedback_expectation());
// Process a true answer and check the expected result.
$this->process_submission(array('answer' => 1));
$this->check_current_state(question_state::$complete);
$this->check_current_mark(null);
$this->check_current_output(
$this->get_contains_tf_true_radio_expectation(true, true),
$this->get_does_not_contain_correctness_expectation(),
$this->get_does_not_contain_feedback_expectation());
// Finish the attempt.
$this->quba->finish_all_questions();
// Verify.
$this->check_current_state(question_state::$needsgrading);
$this->check_current_mark(null);
$this->check_current_output(
$this->get_does_not_contain_correctness_expectation(),
$this->get_does_not_contain_specific_feedback_expectation());
// Process a manual comment.
$this->manual_grade('Not good enough!', 1, FORMAT_HTML);
$this->check_current_state(question_state::$mangrpartial);
$this->check_current_mark(1);
$this->check_current_output(
$this->get_does_not_contain_correctness_expectation(),
$this->get_does_not_contain_specific_feedback_expectation(),
new \question_pattern_expectation('/' . preg_quote('Not good enough!', '/') . '/'));
}
public function test_manual_grade_ungraded_question(): void {
global $PAGE;
// The current text editor depends on the users profile setting - so it needs a valid user.
$this->setAdminUser();
// Required to init a text editor.
$PAGE->set_url('/');
// Create an essay question.
$essay = \test_question_maker::make_an_essay_question();
$this->start_attempt_at_question($essay, 'deferredfeedback', 0);
// Check the right model is being used.
$this->assertEquals('manualgraded', $this->quba->get_question_attempt(
$this->slot)->get_behaviour_name());
// Check the initial state.
$this->check_current_state(question_state::$todo);
$this->check_current_mark(null);
$this->check_current_output($this->get_contains_question_text_expectation($essay),
$this->get_does_not_contain_feedback_expectation());
// Simulate some data submitted by the student.
$this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
// Verify.
$this->check_current_state(question_state::$complete);
$this->check_current_mark(null);
$this->check_current_output(
new \question_contains_tag_with_attribute('textarea', 'name',
$this->quba->get_question_attempt($this->slot)->get_qt_field_name('answer')),
$this->get_does_not_contain_feedback_expectation());
// Finish the attempt.
$this->quba->finish_all_questions();
// Verify.
$this->check_current_state(question_state::$needsgrading);
$this->check_current_mark(null);
$this->assertEquals('This is my wonderful essay!',
$this->quba->get_response_summary($this->slot));
// Process a manual comment. Note: null mark is the whole point here.
$this->manual_grade('Not good enough!', null, FORMAT_HTML);
// Verify.
// I am pretty sure this next assertion is incorrect. We should change
// the question state to indicate that this quetion has now been commented
// on. However, that is tricky, because what if, after that, the mam mark
// for the qusetions is changed. So, for now, this assertion verifies
// the current behaviour.
$this->check_current_state(question_state::$needsgrading);
$this->check_current_mark(null);
$this->check_current_output(
new \question_pattern_expectation('/' . preg_quote('Not good enough!', '/') . '/'));
}
public function test_manual_graded_ignore_repeat_sumbission(): void {
// Create an essay question.
$essay = \test_question_maker::make_an_essay_question();
$this->start_attempt_at_question($essay, 'deferredfeedback', 10);
// Check the right model is being used.
$this->assertEquals('manualgraded', $this->quba->get_question_attempt(
$this->slot)->get_behaviour_name());
// Check the initial state.
$this->check_current_state(question_state::$todo);
$this->check_current_mark(null);
// Simulate some data submitted by the student.
$this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
// Verify.
$this->check_current_state(question_state::$complete);
$this->check_current_mark(null);
// Finish the attempt.
$this->quba->finish_all_questions();
// Verify.
$this->check_current_state(question_state::$needsgrading);
$this->check_current_mark(null);
$this->assertEquals('This is my wonderful essay!',
$this->quba->get_response_summary($this->slot));
// Process a blank manual comment. Ensure it does not change the state.
$numsteps = $this->get_step_count();
$this->manual_grade('', '', FORMAT_HTML);
$this->check_step_count($numsteps);
$this->check_current_state(question_state::$needsgrading);
$this->check_current_mark(null);
// Process a comment, but with the mark blank. Should be recorded, but
// not change the mark.
$this->manual_grade('I am not sure what grade to award.', '', FORMAT_HTML);
$this->check_step_count($numsteps + 1);
$this->check_current_state(question_state::$needsgrading);
$this->check_current_mark(null);
$this->check_current_output(
new \question_pattern_expectation('/' .
preg_quote('I am not sure what grade to award.', '/') . '/'));
// Now grade it.
$this->manual_grade('Pretty good!', '9.00000', FORMAT_HTML);
$this->check_step_count($numsteps + 2);
$this->check_current_state(question_state::$mangrpartial);
$this->check_current_mark(9);
$this->check_current_output(
new \question_pattern_expectation('/' . preg_quote('Pretty good!', '/') . '/'));
// Process the same data again, and make sure it does not add a step.
$this->manual_grade('Pretty good!', '9.00000', FORMAT_HTML);
$this->check_step_count($numsteps + 2);
$this->check_current_state(question_state::$mangrpartial);
$this->check_current_mark(9);
// Now set the mark back to blank.
$this->manual_grade('Actually, I am not sure any more.', '', FORMAT_HTML);
$this->check_step_count($numsteps + 3);
$this->check_current_state(question_state::$needsgrading);
$this->check_current_mark(null);
$this->check_current_output(
new \question_pattern_expectation('/' .
preg_quote('Actually, I am not sure any more.', '/') . '/'));
$qa = $this->quba->get_question_attempt($this->slot);
$this->assertEquals('Commented: Actually, I am not sure any more.',
$qa->summarise_action($qa->get_last_step()));
}
public function test_manual_graded_ignore_repeat_sumbission_commas(): void {
// Create an essay question.
$essay = \test_question_maker::make_an_essay_question();
$this->start_attempt_at_question($essay, 'deferredfeedback', 10);
// Check the right model is being used.
$this->assertEquals('manualgraded', $this->quba->get_question_attempt(
$this->slot)->get_behaviour_name());
// Check the initial state.
$this->check_current_state(question_state::$todo);
$this->check_current_mark(null);
// Simulate some data submitted by the student.
$this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
// Verify.
$this->check_current_state(question_state::$complete);
$this->check_current_mark(null);
// Finish the attempt.
$this->quba->finish_all_questions();
// Verify.
$this->check_current_state(question_state::$needsgrading);
$this->check_current_mark(null);
$this->assertEquals('This is my wonderful essay!',
$this->quba->get_response_summary($this->slot));
// Now grade it with a mark with a comma.
$numsteps = $this->get_step_count();
$this->manual_grade('Pretty good!', '9,00000', FORMAT_HTML);
$this->check_step_count($numsteps + 1);
$this->check_current_state(question_state::$mangrpartial);
$this->check_current_mark(9);
$qa = $this->get_question_attempt();
$this->assertEquals('Manually graded 9 with comment: Pretty good!',
$qa->summarise_action($qa->get_last_step()));
$this->check_current_output(
new \question_pattern_expectation('/' . preg_quote('Pretty good!', '/') . '/'));
// Process the same mark with a dot. Verify it does not add a new step.
$this->manual_grade('Pretty good!', '9.00000', FORMAT_HTML);
$this->check_step_count($numsteps + 1);
$this->check_current_state(question_state::$mangrpartial);
$this->check_current_mark(9);
}
public function test_manual_graded_essay_can_grade_0(): void {
global $PAGE;
// The current text editor depends on the users profile setting - so it needs a valid user.
$this->setAdminUser();
// Required to init a text editor.
$PAGE->set_url('/');
// Create an essay question.
$essay = \test_question_maker::make_an_essay_question();
$this->start_attempt_at_question($essay, 'deferredfeedback', 10);
// Check the right model is being used.
$this->assertEquals('manualgraded', $this->quba->get_question_attempt(
$this->slot)->get_behaviour_name());
// Check the initial state.
$this->check_current_state(question_state::$todo);
$this->check_current_mark(null);
$this->check_current_output($this->get_contains_question_text_expectation($essay),
$this->get_does_not_contain_feedback_expectation());
// Simulate some data submitted by the student.
$this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
// Verify.
$this->check_current_state(question_state::$complete);
$this->check_current_mark(null);
$this->check_current_output(
new \question_contains_tag_with_attribute('textarea', 'name',
$this->quba->get_question_attempt($this->slot)->get_qt_field_name('answer')),
$this->get_does_not_contain_feedback_expectation());
// Finish the attempt.
$this->quba->finish_all_questions();
// Verify.
$this->check_current_state(question_state::$needsgrading);
$this->check_current_mark(null);
$this->assertEquals('This is my wonderful essay!',
$this->quba->get_response_summary($this->slot));
// Process a blank comment and a grade of 0.
$this->manual_grade('', 0, FORMAT_HTML);
// Verify.
$this->check_current_state(question_state::$mangrwrong);
$this->check_current_mark(0);
}
public function test_manual_graded_change_comment_format(): void {
global $PAGE;
// The current text editor depends on the users profile setting - so it needs a valid user.
$this->setAdminUser();
// Required to init a text editor.
$PAGE->set_url('/');
// Create an essay question.
$essay = \test_question_maker::make_an_essay_question();
$this->start_attempt_at_question($essay, 'deferredfeedback', 10);
// Simulate some data submitted by the student.
$this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
// Finish the attempt.
$this->quba->finish_all_questions();
// Process an example comment and a grade of 0.
$this->manual_grade('example', 0, FORMAT_HTML);
// Verify the format is FORMAT_HTML.
$this->check_comment('example', FORMAT_HTML);
// Process the same grade and comment with different format.
$this->manual_grade('example', 0, FORMAT_MARKDOWN);
// Verify the format is FORMAT_MARKDOWN.
$this->check_comment('example', FORMAT_MARKDOWN);
}
public function test_manual_graded_respects_display_options(): void {
// This test is for MDL-43874. Manual comments were not respecting the
// Display options for feedback.
global $PAGE;
// The current text editor depends on the users profile setting - so it needs a valid user.
$this->setAdminUser();
// Required to init a text editor.
$PAGE->set_url('/');
// Create an essay question.
$essay = \test_question_maker::make_an_essay_question();
$this->start_attempt_at_question($essay, 'deferredfeedback', 10);
// Check the right model is being used.
$this->assertEquals('manualgraded', $this->quba->get_question_attempt(
$this->slot)->get_behaviour_name());
// Check the initial state.
$this->check_current_state(question_state::$todo);
$this->check_current_mark(null);
$this->check_current_output($this->get_contains_question_text_expectation($essay),
$this->get_does_not_contain_feedback_expectation());
// Simulate some data submitted by the student.
$this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
// Verify.
$this->check_current_state(question_state::$complete);
$this->check_current_mark(null);
$this->check_current_output(
new \question_contains_tag_with_attribute('textarea', 'name',
$this->quba->get_question_attempt($this->slot)->get_qt_field_name('answer')),
$this->get_does_not_contain_feedback_expectation());
// Finish the attempt.
$this->quba->finish_all_questions();
// Verify.
$this->check_current_state(question_state::$needsgrading);
$this->check_current_mark(null);
$this->assertEquals('This is my wonderful essay!',
$this->quba->get_response_summary($this->slot));
// Process a comment and a grade.
$this->manual_grade('This should only appear if the displya options allow it', 5, FORMAT_HTML);
// Verify.
$this->check_current_state(question_state::$mangrpartial);
$this->check_current_mark(5);
$this->displayoptions->manualcomment = question_display_options::HIDDEN;
$this->check_output_does_not_contain('This should only appear if the displya options allow it');
$this->displayoptions->manualcomment = question_display_options::VISIBLE;
$this->check_output_contains('This should only appear if the displya options allow it');
}
public function test_manual_graded_invalid_value_throws_exception(): void {
global $PAGE;
// The current text editor depends on the users profile setting - so it needs a valid user.
$this->setAdminUser();
// Required to init a text editor.
$PAGE->set_url('/');
// Create an essay question.
$essay = \test_question_maker::make_an_essay_question();
$this->start_attempt_at_question($essay, 'deferredfeedback', 10);
// Check the right model is being used.
$this->assertEquals('manualgraded', $this->quba->get_question_attempt(
$this->slot)->get_behaviour_name());
// Check the initial state.
$this->check_current_state(question_state::$todo);
$this->check_current_mark(null);
$this->check_current_output($this->get_contains_question_text_expectation($essay),
$this->get_does_not_contain_feedback_expectation());
// Simulate some data submitted by the student.
$this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
// Verify.
$this->check_current_state(question_state::$complete);
$this->check_current_mark(null);
$this->check_current_output(
new \question_contains_tag_with_attribute('textarea', 'name',
$this->quba->get_question_attempt($this->slot)->get_qt_field_name('answer')),
$this->get_does_not_contain_feedback_expectation());
// Finish the attempt.
$this->quba->finish_all_questions();
// Verify.
$this->check_current_state(question_state::$needsgrading);
$this->check_current_mark(null);
$this->assertEquals('This is my wonderful essay!',
$this->quba->get_response_summary($this->slot));
// Try to process a an invalid grade.
$this->expectException('coding_exception');
$this->manual_grade('Comment', 'frog', FORMAT_HTML);
}
public function test_manual_graded_out_of_range_throws_exception(): void {
global $PAGE;
// The current text editor depends on the users profile setting - so it needs a valid user.
$this->setAdminUser();
// Required to init a text editor.
$PAGE->set_url('/');
// Create an essay question.
$essay = \test_question_maker::make_an_essay_question();
$this->start_attempt_at_question($essay, 'deferredfeedback', 10);
// Check the right model is being used.
$this->assertEquals('manualgraded', $this->quba->get_question_attempt(
$this->slot)->get_behaviour_name());
// Check the initial state.
$this->check_current_state(question_state::$todo);
$this->check_current_mark(null);
$this->check_current_output($this->get_contains_question_text_expectation($essay),
$this->get_does_not_contain_feedback_expectation());
// Simulate some data submitted by the student.
$this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
// Verify.
$this->check_current_state(question_state::$complete);
$this->check_current_mark(null);
$this->check_current_output(
new \question_contains_tag_with_attribute('textarea', 'name',
$this->quba->get_question_attempt($this->slot)->get_qt_field_name('answer')),
$this->get_does_not_contain_feedback_expectation());
// Finish the attempt.
$this->quba->finish_all_questions();
// Verify.
$this->check_current_state(question_state::$needsgrading);
$this->check_current_mark(null);
$this->assertEquals('This is my wonderful essay!',
$this->quba->get_response_summary($this->slot));
// Try to process a an invalid grade.
$this->expectException('coding_exception');
$this->manual_grade('Comment', '10.1', FORMAT_HTML);
}
public function test_manual_graded_displays_proper_comment_format(): void {
global $PAGE;
// The current text editor depends on the users profile setting - so it needs a valid user.
$this->setAdminUser();
// Required to init a text editor.
$PAGE->set_url('/');
// Create an essay question.
$essay = \test_question_maker::make_an_essay_question();
$this->start_attempt_at_question($essay, 'deferredfeedback', 10);
// Check the right model is being used.
$this->assertEquals('manualgraded', $this->quba->get_question_attempt(
$this->slot)->get_behaviour_name());
// Simulate some data submitted by the student.
$this->process_submission(
array(
'answer' => "A submission!",
'answerformat' => FORMAT_PLAIN
)
);
// Finish the attempt.
$this->quba->finish_all_questions();
// Write a manual comment in markdown.
$this->manual_grade("*one\n*two\n*three\n", 10, FORMAT_MARKDOWN);
// Check that feedback contains the original markdown format.
$preg = '/<textarea [^>]+name="[^"]+-comment"[^>]+>\*one\n\*two\n\*three\n/';
$this->displayoptions->manualcomment = question_display_options::EDITABLE;
$this->check_current_output(
new \question_pattern_expectation($preg)
);
}
public function test_manual_grading_reshows_exactly_the_mark_input(): void {
global $PAGE;
// The current text editor depends on the users profile setting - so it needs a valid user.
$this->setAdminUser();
// Required to init a text editor.
$PAGE->set_url('/');
// Create an essay question graded out of 15 and attempt it.
$essay = \test_question_maker::make_an_essay_question();
$this->start_attempt_at_question($essay, 'deferredfeedback', 15);
$this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
$this->quba->finish_all_questions();
// Verify.
$this->check_current_state(question_state::$needsgrading);
$this->check_current_mark(null);
$this->assertEquals('This is my wonderful essay!',
$this->quba->get_response_summary($this->slot));
// Try to process a grade where the score will be stored rounded.
$this->manual_grade('Comment', '5.0', FORMAT_HTML);
// Verify.
$this->check_current_state(question_state::$mangrpartial);
$this->check_current_mark(5);
$this->displayoptions->manualcomment = question_display_options::EDITABLE;
$this->render();
$this->check_output_contains_text_input('-mark', '5.0');
// Rescale what the question is worth, and verify the display.
$this->get_question_attempt()->set_max_mark(1);
$this->render();
$this->check_output_contains_text_input('-mark', '0.3333333');
}
public function test_manual_grading_history_display(): void {
global $PAGE;
// The current text editor depends on the users profile setting - so it needs a valid user.
$this->setAdminUser();
// Required to init a text editor.
$PAGE->set_url('/');
// Create an essay question graded out of 15 and attempt it.
$essay = \test_question_maker::make_an_essay_question();
$this->start_attempt_at_question($essay, 'deferredfeedback', 10);
$this->process_submission(array('answer' => 'This is my wonderful essay!', 'answerformat' => FORMAT_HTML));
$this->quba->finish_all_questions();
// Verify.
$this->check_current_state(question_state::$needsgrading);
// Process an initial grade and comment.
$this->manual_grade('First comment', '5.0', FORMAT_HTML);
// Process a second grade and comment.
$this->manual_grade('Second comment', '7.0', FORMAT_HTML);
// Verify.
$this->check_current_state(question_state::$mangrpartial);
$this->check_current_mark(7);
$this->displayoptions->history = question_display_options::VISIBLE;
$this->render();
$this->check_output_contains('Manually graded 5 with comment: First comment');
$this->check_output_contains('Manually graded 7 with comment: Second comment');
}
}
@@ -0,0 +1,33 @@
<?php
// This file is part of Moodle - http://moodle.org/
//
// Moodle is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// Moodle is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with Moodle. If not, see <http://www.gnu.org/licenses/>.
/**
* Version information for the calculated question type.
*
* @package qbehaviour
* @subpackage manualgraded
* @copyright 2011 The Open University
* @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
*/
defined('MOODLE_INTERNAL') || die();
$plugin->component = 'qbehaviour_manualgraded';
$plugin->version = 2024042200;
$plugin->requires = 2024041600;
$plugin->maturity = MATURITY_STABLE;