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;
}
/**