From 710ee11ad27323b1f2780c417b42a93ae0b5a5d0 Mon Sep 17 00:00:00 2001 From: Nathan Nguyen Date: Wed, 21 Aug 2024 15:01:09 +1000 Subject: [PATCH] MDL-82125 mod_assign: show penalty indicator --- mod/assign/gradingtable.php | 8 ++- mod/assign/locallib.php | 63 +++++++++++++++----- mod/assign/tests/behat/behat_mod_assign.php | 16 +++++ mod/assign/tests/behat/display_grade.feature | 45 +++++++++++++- 4 files changed, 114 insertions(+), 18 deletions(-) diff --git a/mod/assign/gradingtable.php b/mod/assign/gradingtable.php index cc69aa0691a1d..c3a3e884c553f 100644 --- a/mod/assign/gradingtable.php +++ b/mod/assign/gradingtable.php @@ -753,9 +753,10 @@ public function col_scale($row) { * @param boolean $editable * @param int $userid The user id of the user this grade belongs to * @param int $modified Timestamp showing when the grade was last modified + * @param float $deductedmark The deducted mark if penalty is applied * @return string The formatted grade */ - public function display_grade($grade, $editable, $userid, $modified) { + public function display_grade($grade, $editable, $userid, $modified, $deductedmark = 0) { if ($this->is_downloading()) { if ($this->assignment->get_instance()->grade >= 0) { if ($grade == -1 || $grade === null) { @@ -772,7 +773,7 @@ public function display_grade($grade, $editable, $userid, $modified) { return $scale; } } - return $this->assignment->display_grade($grade, $editable, $userid, $modified); + return $this->assignment->display_grade($grade, $editable, $userid, $modified, $deductedmark); } /** @@ -1031,11 +1032,12 @@ public function col_grade(stdClass $row): string { * @return string */ public function col_finalgrade(stdClass $row) { + global $PAGE; $o = ''; $grade = $this->get_gradebook_data_for_user($row->userid); if ($grade) { - $o = $this->display_grade($grade->grade, false, $row->userid, $row->timemarked); + $o = $this->display_grade($grade->grade, false, $row->userid, $row->timemarked, $grade->deductedmark); } return $o; diff --git a/mod/assign/locallib.php b/mod/assign/locallib.php index dfcb8915f3837..cbf37992a8ad9 100644 --- a/mod/assign/locallib.php +++ b/mod/assign/locallib.php @@ -2010,22 +2010,17 @@ public function should_provide_intro_attachments(int $userid): bool { * @param boolean $editing Are we allowing changes to this grade? * @param int $userid The user id the grade belongs to * @param int $modified Timestamp from when the grade was last modified - * @param float $penalty The penalty to apply to the grade + * @param float $deductedmark The deducted mark if penalty is applied * @return string User-friendly representation of grade */ - public function display_grade($grade, $editing, $userid=0, $modified=0, $penalty = 0) { - global $DB; + public function display_grade($grade, $editing, $userid=0, $modified=0, $deductedmark = 0) { + global $DB, $PAGE; static $scalegrades = array(); $o = ''; if ($this->get_instance()->grade >= 0) { - // Apply penalty percentage to the grade. - if (!is_null($grade) && $grade >= 0) { - $grade *= 1 - $penalty / 100; - } - // Normal number. if ($editing && $this->get_instance()->grade > 0) { if ($grade < 0) { @@ -2044,7 +2039,6 @@ public function display_grade($grade, $editing, $userid=0, $modified=0, $penalty maxlength="10" class="quickgrade"/>'; $o .= ' / ' . format_float($this->get_instance()->grade, $this->get_grade_item()->get_decimals()); - return $o; } else { if ($grade == -1 || $grade === null) { $o .= '-'; @@ -2056,9 +2050,17 @@ class="quickgrade"/>'; $o .= ' / ' . format_float($this->get_instance()->grade, $item->get_decimals()); } } - return $o; } + // Add penalty indicator. + $penaltyindicator = ''; + if ($deductedmark > 0) { + $indicator = new \core_grades\output\penalty_indicator(2, $deductedmark); + $renderer = $PAGE->get_renderer('core_grades'); + $penaltyindicator = $renderer->render_penalty_indicator($indicator); + } + + return $penaltyindicator . $o; } else { // Scale. if (empty($this->cache['scale'])) { @@ -5489,7 +5491,7 @@ public function get_assign_feedback_status_renderable($user) { ); $gradefordisplay = $gradebookgrade->str_long_grade; } else { - $gradefordisplay = $this->display_grade($gradebookgrade->grade, false); + $gradefordisplay = $this->display_grade($gradebookgrade->grade, false, 0, 0, $gradebookgrade->deductedmark); } $gradeddate = $gradebookgrade->dategraded; @@ -5696,16 +5698,18 @@ protected function get_all_grades($userid) { } } + [$penalisedgrade, $deductedmark] = $this->calculate_penalised_grade($grade); + // Now get the gradefordisplay. if ($controller) { $controller->set_grade_range(make_grades_menu($this->get_instance()->grade), $this->get_instance()->grade > 0); $grade->gradefordisplay = $controller->render_grade($PAGE, $grade->id, $gradingitem, - $grade->grade, + $penalisedgrade, $cangrade); } else { - $grade->gradefordisplay = $this->display_grade($grade->grade, false, 0, 0, $grade->penalty); + $grade->gradefordisplay = $this->display_grade($penalisedgrade, false, 0, 0, $deductedmark); } } @@ -5713,6 +5717,28 @@ protected function get_all_grades($userid) { return $grades; } + /** + * Calculate penalised grade and deducted mark. + * + * @param stdClass $grade The grade object + * @return array [$penalisedgrade, $deductedmark] the penalised grade and the deducted mark + */ + public function calculate_penalised_grade(stdClass $grade): array { + $penalisedgrade = $grade->grade; + $deductedmark = 0; + + // No calculation needed if the grade is null or negative. + if (is_null($penalisedgrade) || $penalisedgrade < 0) { + return [$penalisedgrade, $deductedmark]; + } + + if ($grade->penalty > 0) { + $deductedmark = $grade->grade * $grade->penalty / 100; + $penalisedgrade = $grade->grade - $deductedmark; + } + return [$penalisedgrade, $deductedmark]; + } + /** * Get the submissions for all previous attempts. * @@ -7787,7 +7813,7 @@ protected function get_grading_instance($userid, $grade, $gradingdisabled) { * @return void */ public function add_grade_form_elements(MoodleQuickForm $mform, stdClass $data, $params) { - global $USER, $CFG, $SESSION; + global $USER, $CFG, $SESSION, $PAGE; $settings = $this->get_instance(); $rownum = isset($params['rownum']) ? $params['rownum'] : 0; @@ -7909,6 +7935,15 @@ public function add_grade_form_elements(MoodleQuickForm $mform, stdClass $data, $gradestring = $usergrade; } + // Penalty indicator. + $usergrade = $gradinginfo->items[0]->grades[$userid]; + if (isset($usergrade->grade) && $usergrade->deductedmark > 0) { + $indicator = new \core_grades\output\penalty_indicator(2, $usergrade->deductedmark); + $renderer = $PAGE->get_renderer('core_grades'); + $penaltyindicator = $renderer->render_penalty_indicator($indicator); + $gradestring = $penaltyindicator . $gradestring; + } + if ($this->get_instance()->markingworkflow) { $states = $this->get_marking_workflow_states_for_current_user(); $options = array('' => get_string('markingworkflowstatenotmarked', 'assign')) + $states; diff --git a/mod/assign/tests/behat/behat_mod_assign.php b/mod/assign/tests/behat/behat_mod_assign.php index 7d26105f46663..2a96b76cbf8c0 100644 --- a/mod/assign/tests/behat/behat_mod_assign.php +++ b/mod/assign/tests/behat/behat_mod_assign.php @@ -71,4 +71,20 @@ public function i_should_see_marking_guide_information(TableNode $table) { $criteriacheck++; } } + + /** + * Enable grade penalty. + * + * @Given I enable grade penalty for assignment + */ + public function i_enable_grade_penalty_for_assignment(): void { + global $DB; + + set_config('gradepenalty_enabled', 1); + set_config('gradepenalty_supportedplugins', 'assign'); + \core\plugininfo\gradepenalty::enable_plugin('duedate', true); + + $rule = ['contextid' => 1, 'overdueby' => DAYSECS, 'penalty' => 10, 'sortorder' => 0]; + $DB->insert_record('gradepenalty_duedate_rule', (object) $rule); + } } diff --git a/mod/assign/tests/behat/display_grade.feature b/mod/assign/tests/behat/display_grade.feature index ad6c265a4000f..ce8a5da901ee2 100644 --- a/mod/assign/tests/behat/display_grade.feature +++ b/mod/assign/tests/behat/display_grade.feature @@ -1,4 +1,4 @@ -@mod @mod_assign +@mod @mod_assign @assign_grade Feature: Check that the assignment grade can be updated correctly In order to ensure that the grade is shown correctly in the grading table As a teacher @@ -70,3 +70,46 @@ Feature: Check that the assignment grade can be updated correctly And I press "Save changes" And I follow "View all submissions" Then "Student 1" row "Grade" column of "generaltable" table should contain "50.00" + + @javascript + Scenario: Update the grade for an assignment with penalty + Given the following "courses" exist: + | fullname | shortname | category | groupmode | + | Course 1 | C1 | 0 | 1 | + And the following "users" exist: + | username | firstname | lastname | email | + | teacher1 | Teacher | 1 | teacher1@example.com | + | student1 | Student | 1 | student10@example.com | + And the following "course enrolments" exist: + | user | course | role | + | teacher1 | C1 | editingteacher | + | student1 | C1 | student | + And the following "groups" exist: + | name | course | idnumber | + | Group 1 | C1 | G1 | + And I enable grade penalty for assignment + And the following "activity" exists: + | activity | assign | + | course | C1 | + | name | Test assignment name | + | intro | Test assignment description | + | grade | 100 | + | duedate | ##yesterday## | + | gradepenalty | 1 | + | assignsubmission_onlinetext_enabled | 1 | + | submissiondrafts | 0 | + # Add a submission. + And the following "mod_assign > submissions" exist: + | assign | user | onlinetext | + | Test assignment name | student1 | I'm the student first submission | + And I am on the "Test assignment name" Activity page logged in as admin + Then I follow "View all submissions" + And I change window size to "large" + And I click on "Grade" "link" in the "Student 1" "table_row" + And I set the field "Grade out of 100" to "100" + And I set the field "Notify student" to "0" + And I press "Save changes" + And I follow "View all submissions" + And "Student 1" row "Grade" column of "generaltable" table should contain "100.00" + And "Student 1" row "Final grade" column of "generaltable" table should contain "90.00" + And the "title" attribute of ".penalty-indicator-icon" "css_element" should contain "Late penalty applied -10.00 marks"