diff --git a/admin/settings/grades.php b/admin/settings/grades.php index 2d6e86874a71b..c99121cc5f04c 100644 --- a/admin/settings/grades.php +++ b/admin/settings/grades.php @@ -246,6 +246,11 @@ new lang_string('gradepenalty_supportedplugins', 'grades'), new lang_string('gradepenalty_supportedplugins_help', 'grades'), [], $options)); + // Option to apply penalty to overridden grades. + $temp->add(new admin_setting_configcheckbox('gradepenalty_overriddengrade', + new lang_string('gradepenalty_overriddengrade', 'grades'), + new lang_string('gradepenalty_overriddengrade_help', 'grades'), 0)); + $ADMIN->add('gradepenalty', $temp); } diff --git a/grade/report/grader/lang/en/gradereport_grader.php b/grade/report/grader/lang/en/gradereport_grader.php index bf27242c63cd8..75d80e9c884b4 100644 --- a/grade/report/grader/lang/en/gradereport_grader.php +++ b/grade/report/grader/lang/en/gradereport_grader.php @@ -23,6 +23,8 @@ * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later */ +$string['applypenaltytext'] = 'Deduct {$a}'; +$string['applypenaltytooltip'] = 'Apply a deduction to the grade. This value is retrieved from the current overridden grade.'; $string['aria:dropdowncolumns'] = 'Collapsed columns found'; $string['clearsearch'] = 'Clear searched users'; $string['collapsedcolumns'] = 'Collapsed columns {$a}'; diff --git a/grade/report/grader/lib.php b/grade/report/grader/lib.php index 356d7eb8d144a..a82ca0c15e0f8 100644 --- a/grade/report/grader/lib.php +++ b/grade/report/grader/lib.php @@ -241,18 +241,21 @@ public function process_data($data) { continue; } - // If the grade item uses a custom scale - if (!empty($oldvalue->grade_item->scaleid)) { + // Detect if there is any mark deduction. + if (!isset($data->deduction[$userid][$itemid])) { + // If the grade item uses a custom scale. + if (!empty($oldvalue->grade_item->scaleid)) { - if ((int)$oldvalue->finalgrade === (int)$postedvalue) { - continue; - } - } else { - // The grade item uses a numeric scale + if ((int)$oldvalue->finalgrade === (int)$postedvalue) { + continue; + } + } else { + // The grade item uses a numeric scale. - // Format the finalgrade from the DB so that it matches the grade from the client - if ($postedvalue === format_float($oldvalue->finalgrade, $oldvalue->grade_item->get_decimals())) { - continue; + // Format the finalgrade from the DB so that it matches the grade from the client. + if ($postedvalue === format_float($oldvalue->finalgrade, $oldvalue->grade_item->get_decimals())) { + continue; + } } } @@ -328,6 +331,13 @@ public function process_data($data) { $gradeitem->update_final_grade($userid, $finalgrade, 'gradebook', false, FORMAT_MOODLE, null, null, true); + + // Apply penalty. + if (isset($data->deduction[$userid][$itemid])) { + $deductedmark = $data->deduction[$userid][$itemid]; + $gradeitem->update_final_grade($userid, $finalgrade - $deductedmark, 'gradepenalty', false, + FORMAT_MOODLE, null, null, true); + } } } } @@ -1146,6 +1156,14 @@ public function get_right_rows(bool $displayaverages): array { if ($context->statusicons) { $context->extraclasses .= ' statusicons'; } + + // If option to reapply deduction is enabled, add the option to the context. + if (get_config('core', 'gradepenalty_overriddengrade')) { + $context->deductedmark = format_float($grade->deductedmark, $decimalpoints); + $context->reapplydeduction = $grade->deductedmark > 0; + $context->deductionid = 'deduction_' . $userid . '_' . $item->id; + $context->deductionname = 'deduction[' . $userid . '][' . $item->id .']'; + } } else { $context->extraclasses = 'gradevalue' . $hidden . $gradepass; $context->text = format_float($gradeval, $decimalpoints); diff --git a/grade/report/grader/templates/cell.mustache b/grade/report/grader/templates/cell.mustache index 52b8910a844b2..2115462b64fa1 100644 --- a/grade/report/grader/templates/cell.mustache +++ b/grade/report/grader/templates/cell.mustache @@ -41,7 +41,19 @@ {{>core_grades/grades/grader/scale}} {{/scale}} {{^scale}} - {{>core_grades/grades/grader/input}} +
+
+ {{>core_grades/grades/grader/input}} +
+ {{#reapplydeduction}} +
+ + +
+ {{/reapplydeduction}} +
{{/scale}} {{/iseditable}} {{^iseditable}} diff --git a/grade/report/grader/tests/behat/behat_gradereport_grader.php b/grade/report/grader/tests/behat/behat_gradereport_grader.php index 46acb8c3fc1f7..24132ed6e5a02 100644 --- a/grade/report/grader/tests/behat/behat_gradereport_grader.php +++ b/grade/report/grader/tests/behat/behat_gradereport_grader.php @@ -144,4 +144,14 @@ public static function get_partial_named_selectors(): array { ), ]; } + + /** + * Enable penalty for overridden grade. + * + * @Given I enable penalty for overridden grade + */ + public function i_enable_penalty_for_overridden_grade(): void { + set_config('gradepenalty_enabled', 1); + set_config('gradepenalty_overriddengrade', 1); + } } diff --git a/grade/report/grader/tests/behat/grade_override_with_deduction.feature b/grade/report/grader/tests/behat/grade_override_with_deduction.feature new file mode 100644 index 0000000000000..55f7fd0cbcbf3 --- /dev/null +++ b/grade/report/grader/tests/behat/grade_override_with_deduction.feature @@ -0,0 +1,48 @@ +@gradereport @gradereport_grader @gradereport_grader_deduction +Feature: As a teacher, I want to override a grade with a deduction and check the gradebook. + + Background: + Given the following "courses" exist: + | fullname | shortname | format | + | Course 1 | C1 | topics | + And I enable penalty for overridden grade + And the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | 1 | teacher1@example.com | + | student1 | Student | 1 | student1@example.com | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + | student1 | C1 | student | + And the following "grade items" exist: + | itemname | grademin | grademax | course | + | Manual grade 01 | 0 | 100 | C1 | + | Manual grade 02 | 0 | 100 | C1 | + And the following "grade grades" exist: + | gradeitem | user | grade | deductedmark | + | Manual grade 01 | student1 | 60 | 10 | + | Manual grade 02 | student1 | 80 | 20 | + When I log in as "teacher1" + + @javascript + Scenario: Override a grade with a deduction and check the gradebook + And I am on "Course 1" course homepage + And I navigate to "View > Grader report" in the course gradebook + And the following should exist in the "user-grades" table: + | -1- | -2- | -3- | -4- | -5- | + | Student 1 | student1@example.com | 60 | 80 | 140 | + And I turn editing mode on + And I set the field "Student 1 Manual grade 01 grade" to "80" + And I click on "Deduct 10.00" "checkbox" + And I click on "Save changes" "button" + And I turn editing mode off + And the following should exist in the "user-grades" table: + | -1- | -2- | -3- | -4- | -5- | + | Student 1 | student1@example.com | 70 | 80 | 150 | + And I turn editing mode on + And I set the field "Student 1 Manual grade 02 grade" to "100" + And I click on "Save changes" "button" + And I turn editing mode off + And the following should exist in the "user-grades" table: + | -1- | -2- | -3- | -4- | -5- | + | Student 1 | student1@example.com | 70 | 100 | 170 | diff --git a/lang/en/grades.php b/lang/en/grades.php index a886c4a7d36d5..7bed40640f2de 100644 --- a/lang/en/grades.php +++ b/lang/en/grades.php @@ -334,6 +334,8 @@ $string['gradepenalty_enabled'] = 'Grade penalty'; $string['gradepenalty_enabled_help'] = 'If enabled, the penalty will be applied to the grades of supported modules.'; $string['gradepenalty_indicator_info'] = 'Late penalty applied -{$a} marks'; +$string['gradepenalty_overriddengrade'] = 'Apply penalty to overridden grades'; +$string['gradepenalty_overriddengrade_help'] = 'If enabled, the penalty will be applied to overridden grades.'; $string['gradepenalty_supportedplugins'] = 'Supported modules'; $string['gradepenalty_supportedplugins_help'] = 'Enable the grade penalty for the selected modules.'; $string['gradepointdefault'] = 'Grade point default';