diff --git a/grade/classes/local/penalty/manager.php b/grade/classes/local/penalty/manager.php index 550b077cd4bc0..6fc380fed6399 100644 --- a/grade/classes/local/penalty/manager.php +++ b/grade/classes/local/penalty/manager.php @@ -114,7 +114,8 @@ public static function apply_penalty(int $userid, grade_item $gradeitem, // Apply the penalty to the grade. if (!$previewonly) { // Update the final grade after the penalty is applied. - $gradeitem->update_raw_grade($userid, $beforepenaltyhook->get_grade_after_penalty()); + $gradeitem->update_raw_grade($userid, $beforepenaltyhook->get_grade_after_penalty(), 'gradepenalty'); + $gradeitem->update_deducted_mark($userid, $beforepenaltyhook->get_deducted_grade()); // Hook for plugins to process further after the penalty is applied to the grade. $afterpenaltyhook = new after_penalty_applied($userid, $gradeitem, $submissiondate, $duedate, diff --git a/grade/report/grader/lib.php b/grade/report/grader/lib.php index 87be16c4684b8..356d7eb8d144a 100644 --- a/grade/report/grader/lib.php +++ b/grade/report/grader/lib.php @@ -1191,7 +1191,7 @@ public function get_right_rows(bool $displayaverages): array { $context->extraclasses = 'gradevalue ' . $hidden . $gradepass; $context->text = grade_format_gradevalue($gradeval, $item, true, - $gradedisplaytype, null); + $gradedisplaytype, null, $grade->deductedmark); } } diff --git a/grade/report/user/classes/report/user.php b/grade/report/user/classes/report/user.php index 48e65c52d72f2..50a9ba404c7cd 100644 --- a/grade/report/user/classes/report/user.php +++ b/grade/report/user/classes/report/user.php @@ -700,8 +700,8 @@ private function fill_table_recursive(array &$element) { if ($this->canviewhidden) { $gradeitemdata['graderaw'] = $gradeval; $data['grade']['content'] = grade_format_gradevalue($gradeval, - $gradegrade->grade_item, - true) . $gradestatus; + $gradegrade->grade_item, true, null, null, $gradegrade->deductedmark + ) . $gradestatus; } } else { $gradestatusclass = ''; @@ -728,7 +728,8 @@ private function fill_table_recursive(array &$element) { $data['grade']['class'] = "{$class} {$gradestatusclass}"; $data['grade']['content'] = $gradepassicon . grade_format_gradevalue($gradeval, - $gradegrade->grade_item, true) . $gradestatus; + $gradegrade->grade_item, true, null, null, $gradegrade->deductedmark + ) . $gradestatus; $gradeitemdata['graderaw'] = $gradeval; } $data['grade']['headers'] = "$headercat $headerrow grade$userid"; diff --git a/lib/db/install.xml b/lib/db/install.xml index dd261b090441b..e23296f45842e 100644 --- a/lib/db/install.xml +++ b/lib/db/install.xml @@ -2059,6 +2059,7 @@ + diff --git a/lib/db/upgrade.php b/lib/db/upgrade.php index b9890fbd08ec1..435a35fa3036b 100644 --- a/lib/db/upgrade.php +++ b/lib/db/upgrade.php @@ -1236,5 +1236,21 @@ function xmldb_main_upgrade($oldversion) { upgrade_main_savepoint(true, 2024111500.04); } + if ($oldversion < 2024111100.00) { + + // Define field penalty to be added to grade_grades. + $table = new xmldb_table('grade_grades'); + $field = new xmldb_field('deductedmark', XMLDB_TYPE_NUMBER, '10, 5', null, + XMLDB_NOTNULL, null, '0', 'aggregationweight'); + + // Conditionally launch add field penalty. + if (!$dbman->field_exists($table, $field)) { + $dbman->add_field($table, $field); + } + + // Main savepoint reached. + upgrade_main_savepoint(true, 2024111100.00); + } + return true; } diff --git a/lib/grade/grade_grade.php b/lib/grade/grade_grade.php index 46adfbfc972d1..1ac340ba0aaae 100644 --- a/lib/grade/grade_grade.php +++ b/lib/grade/grade_grade.php @@ -50,7 +50,7 @@ class grade_grade extends grade_object { public $required_fields = array('id', 'itemid', 'userid', 'rawgrade', 'rawgrademax', 'rawgrademin', 'rawscaleid', 'usermodified', 'finalgrade', 'hidden', 'locked', 'locktime', 'exported', 'overridden', 'excluded', 'timecreated', - 'timemodified', 'aggregationstatus', 'aggregationweight'); + 'timemodified', 'aggregationstatus', 'aggregationweight', 'deductedmark'); /** * Array of optional fields with default values (these should match db defaults) @@ -218,6 +218,9 @@ class grade_grade extends grade_object { */ public $label; + /** @var float $deductedmark mark deducted from final grade */ + public $deductedmark = 0; + /** * Returns array of grades for given grade_item+users * diff --git a/lib/grade/grade_item.php b/lib/grade/grade_item.php index 606608584267d..a57d577d0816a 100644 --- a/lib/grade/grade_item.php +++ b/lib/grade/grade_item.php @@ -2146,6 +2146,21 @@ public function update_raw_grade($userid, $rawgrade = false, $source = null, $fe return $result; } + /** + * Update penalty value for given user + * + * @param int $userid The graded user + * @param float $deductedmark The mark deducted from final grade + */ + public function update_deducted_mark(int $userid, float $deductedmark): void { + $grade = new grade_grade([ + 'itemid' => $this->id, + 'userid' => $userid, + ]); + $grade->deductedmark = $deductedmark; + $grade->update(); + } + /** * Calculates final grade values using the formula in the calculation property. * The parameters are taken from final grades of grade items in current course only. diff --git a/lib/gradelib.php b/lib/gradelib.php index 35389e5381737..966ee4cce72b2 100644 --- a/lib/gradelib.php +++ b/lib/gradelib.php @@ -534,6 +534,7 @@ function grade_get_grades($courseid, $itemtype, $itemmodule, $iteminstance, $use $grade->usermodified = $grade_grades[$userid]->usermodified; $grade->datesubmitted = $grade_grades[$userid]->get_datesubmitted(); $grade->dategraded = $grade_grades[$userid]->get_dategraded(); + $grade->deductedmark = $grade_grades[$userid]->deductedmark; // create text representation of grade if ($grade_item->gradetype == GRADE_TYPE_TEXT or $grade_item->gradetype == GRADE_TYPE_NONE) { @@ -785,9 +786,12 @@ function grade_set_setting($courseid, $name, $value) { * @param bool $localized use localised decimal separator * @param int $displaytype type of display. For example GRADE_DISPLAY_TYPE_REAL, GRADE_DISPLAY_TYPE_PERCENTAGE, GRADE_DISPLAY_TYPE_LETTER * @param int $decimals The number of decimal places when displaying float values + * @param float $penalty The penalty value * @return string */ -function grade_format_gradevalue(?float $value, &$grade_item, $localized=true, $displaytype=null, $decimals=null) { +function grade_format_gradevalue(?float $value, &$grade_item, $localized=true, $displaytype=null, $decimals=null, $penalty = 0.0) { + global $PAGE; + if ($grade_item->gradetype == GRADE_TYPE_NONE or $grade_item->gradetype == GRADE_TYPE_TEXT) { return ''; } @@ -812,40 +816,59 @@ function grade_format_gradevalue(?float $value, &$grade_item, $localized=true, $ switch ($displaytype) { case GRADE_DISPLAY_TYPE_REAL: - return grade_format_gradevalue_real($value, $grade_item, $decimals, $localized); + $gradetext = grade_format_gradevalue_real($value, $grade_item, $decimals, $localized); + break; case GRADE_DISPLAY_TYPE_PERCENTAGE: - return grade_format_gradevalue_percentage($value, $grade_item, $decimals, $localized); + $gradetext = grade_format_gradevalue_percentage($value, $grade_item, $decimals, $localized); + break; case GRADE_DISPLAY_TYPE_LETTER: - return grade_format_gradevalue_letter($value, $grade_item); + $gradetext = grade_format_gradevalue_letter($value, $grade_item); + break; case GRADE_DISPLAY_TYPE_REAL_PERCENTAGE: - return grade_format_gradevalue_real($value, $grade_item, $decimals, $localized) . ' (' . + $gradetext = grade_format_gradevalue_real($value, $grade_item, $decimals, $localized) . ' (' . grade_format_gradevalue_percentage($value, $grade_item, $decimals, $localized) . ')'; + break; case GRADE_DISPLAY_TYPE_REAL_LETTER: - return grade_format_gradevalue_real($value, $grade_item, $decimals, $localized) . ' (' . + $gradetext = grade_format_gradevalue_real($value, $grade_item, $decimals, $localized) . ' (' . grade_format_gradevalue_letter($value, $grade_item) . ')'; + break; case GRADE_DISPLAY_TYPE_PERCENTAGE_REAL: - return grade_format_gradevalue_percentage($value, $grade_item, $decimals, $localized) . ' (' . + $gradetext = grade_format_gradevalue_percentage($value, $grade_item, $decimals, $localized) . ' (' . grade_format_gradevalue_real($value, $grade_item, $decimals, $localized) . ')'; + break; case GRADE_DISPLAY_TYPE_LETTER_REAL: - return grade_format_gradevalue_letter($value, $grade_item) . ' (' . + $gradetext = grade_format_gradevalue_letter($value, $grade_item) . ' (' . grade_format_gradevalue_real($value, $grade_item, $decimals, $localized) . ')'; + break; case GRADE_DISPLAY_TYPE_LETTER_PERCENTAGE: - return grade_format_gradevalue_letter($value, $grade_item) . ' (' . + $gradetext = grade_format_gradevalue_letter($value, $grade_item) . ' (' . grade_format_gradevalue_percentage($value, $grade_item, $decimals, $localized) . ')'; + break; case GRADE_DISPLAY_TYPE_PERCENTAGE_LETTER: - return grade_format_gradevalue_percentage($value, $grade_item, $decimals, $localized) . ' (' . + $gradetext = grade_format_gradevalue_percentage($value, $grade_item, $decimals, $localized) . ' (' . grade_format_gradevalue_letter($value, $grade_item) . ')'; + break; + default: - return ''; + $gradetext = ''; } + + // Show penalty indicator if penalty is greater than 0. + if ($penalty > 0.0) { + $indicator = new \core_grades\output\penalty_indicator(2, $penalty); + $renderer = $PAGE->get_renderer('core_grades'); + $gradetext .= $renderer->render_penalty_indicator($indicator); + } + + return $gradetext; } /**