Skip to content

Commit f41572a

Browse files
committed
MOBILE-4626 grades: Display grade penalties
1 parent 5b18cbf commit f41572a

File tree

13 files changed

+318
-26
lines changed

13 files changed

+318
-26
lines changed

src/addons/mod/assign/components/edit-feedback-modal/edit-feedback-modal.html

+20-6
Original file line numberDiff line numberDiff line change
@@ -59,13 +59,27 @@ <h1>{{'core.gradenoun' | translate}}</h1>
5959
<ion-item class="ion-text-wrap" *ngIf="grade.method === 'simple'">
6060
<ion-label>
6161
<p class="item-heading">{{ 'addon.mod_assign.currentgrade' | translate }}</p>
62-
<p *ngIf="grade.gradebookGrade && !grade.scale">
63-
{{ grade.gradebookGrade }}
62+
<p class="core-grading-summary-grade">
63+
@if (grade.gradebookGrade) {
64+
@if (grade.penalty) {
65+
<ion-icon name="fas-triangle-exclamation" color="danger" aria-hidden="true" />
66+
}
67+
<span>
68+
@if (grade.scale) {
69+
{{ grade.scale[grade.gradebookGrade].label }}
70+
} @else {
71+
{{ grade.gradebookGrade }}
72+
}
73+
</span>
74+
} @else {
75+
-
76+
}
6477
</p>
65-
<p *ngIf="grade.gradebookGrade && grade.scale">
66-
{{ grade.scale[grade.gradebookGrade].label }}
67-
</p>
68-
<p *ngIf="!grade.gradebookGrade">-</p>
78+
@if (grade.penalty) {
79+
<p class="core-grading-summary-penalty">
80+
{{ grade.penalty }}
81+
</p>
82+
}
6983
</ion-label>
7084
</ion-item>
7185

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
@use "theme/globals" as *;
2+
3+
4+
:host {
5+
6+
.core-grading-summary-grade {
7+
display: flex;
8+
align-items: center;
9+
gap: 0.25rem;
10+
11+
ion-icon {
12+
flex-shrink: 0;
13+
}
14+
15+
.penalty-indicator-icon {
16+
display: none;
17+
}
18+
}
19+
20+
.core-grading-summary-penalty {
21+
color: var(--danger);
22+
font-size: 0.75em;
23+
}
24+
}

src/addons/mod/assign/components/edit-feedback-modal/edit-feedback-modal.ts

+4
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ import { CoreFormFields, CoreForms } from '@singletons/form';
5353
@Component({
5454
selector: 'addon-mod-assign-edit-feedback-modal',
5555
templateUrl: 'edit-feedback-modal.html',
56+
styleUrl: 'edit-feedback-modal.scss',
5657
standalone: true,
5758
imports: [
5859
CoreSharedModule,
@@ -359,6 +360,8 @@ export class AddonModAssignEditFeedbackModalComponent implements OnDestroy, OnIn
359360
});
360361
gradeInfo.disabled = grade.gradeislocked || grade.gradeisoverridden;
361362
}
363+
364+
this.grade.penalty = CoreGradesHelper.getPenaltyFromGrade(grade.gradeformatted);
362365
});
363366

364367
gradeInfo.outcomes = outcomes;
@@ -599,6 +602,7 @@ type AddonModAssignSubmissionGrade = {
599602
lang: string;
600603
disabled: boolean;
601604
unreleasedGrade?: SafeNumber | string;
605+
penalty?: string; // Parsed from grade.
602606
};
603607

604608
type AddonModAssignGradeInfo = Omit<CoreCourseModuleGradeInfo, 'outcomes'> & {

src/addons/mod/assign/components/submission/addon-mod-assign-submission.html

+21-3
Original file line numberDiff line numberDiff line change
@@ -262,9 +262,19 @@ <h2 class="big">{{'addon.mod_assign.feedback' | translate}}</h2>
262262
<ion-item class="ion-text-wrap core-grading-summary">
263263
<ion-label>
264264
<p class="item-heading">{{ 'core.gradenoun' | translate }}</p>
265-
<p>
266-
<core-format-text [text]="feedback.gradefordisplay" [filter]="false" />
265+
<p class="core-grading-summary-grade">
266+
@if (feedback.penalty) {
267+
<ion-icon name="fas-triangle-exclamation" color="danger" aria-hidden="true" />
268+
}
269+
<span>
270+
<core-format-text [text]="feedback.gradefordisplay" [filter]="false" />
271+
</span>
267272
</p>
273+
@if (feedback.penalty) {
274+
<p class="core-grading-summary-penalty">
275+
{{ feedback.penalty }}
276+
</p>
277+
}
268278
</ion-label>
269279
@if (feedback.advancedgrade) {
270280
<ion-button slot="end" (click)="showAdvancedGrade(feedback.gradefordisplay)"
@@ -365,9 +375,17 @@ <h4 class="big">{{'core.gradenoun' | translate}}</h4>
365375
<ion-item class="ion-text-wrap core-grading-summary">
366376
<ion-label>
367377
<p class="item-heading">{{ 'core.gradenoun' | translate }}</p>
368-
<p>
378+
<p class="core-grading-summary-grade">
379+
@if (attempt.penalty) {
380+
<ion-icon name="fas-triangle-exclamation" color="danger" aria-hidden="true" />
381+
}
369382
<core-format-text [text]="attempt.grade.gradefordisplay" [filter]="false" />
370383
</p>
384+
@if (attempt.penalty) {
385+
<p class="core-grading-summary-penalty">
386+
{{ attempt.penalty }}
387+
</p>
388+
}
371389
</ion-label>
372390
@if (attempt.advancedgrade) {
373391
<ion-button slot="end" (click)="showAdvancedGrade(attempt.grade.gradefordisplay)"

src/addons/mod/assign/components/submission/submission.scss

+19
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,25 @@
2525
display: none;
2626
}
2727

28+
.core-grading-summary-grade {
29+
display: flex;
30+
align-items: center;
31+
gap: 0.25rem;
32+
33+
ion-icon {
34+
flex-shrink: 0;
35+
}
36+
37+
.penalty-indicator-icon {
38+
display: none;
39+
}
40+
}
41+
42+
.core-grading-summary-penalty {
43+
color: var(--danger);
44+
font-size: 0.75em;
45+
}
46+
2847
ion-badge {
2948
margin-left: 2px;
3049
margin-right: 2px;

src/addons/mod/assign/components/submission/submission.ts

+4
Original file line numberDiff line numberDiff line change
@@ -511,6 +511,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
511511
// If we have data about the grader, get its profile.
512512
attempt.grader = await this.getGrader(attempt.grade);
513513
attempt.advancedgrade = this.getAdvancedGrade(attempt.grade?.gradefordisplay);
514+
attempt.penalty = CoreGradesHelper.getPenaltyFromGrade(attempt.grade?.gradefordisplay);
514515
});
515516

516517
promises.push(...graderPromises);
@@ -596,6 +597,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy {
596597

597598
// Check if the grade uses advanced grading.
598599
this.feedback.advancedgrade = this.getAdvancedGrade(feedback.gradefordisplay);
600+
this.feedback.penalty = CoreGradesHelper.getPenaltyFromGrade(feedback.gradefordisplay);
599601
}
600602

601603
// Get the grade for the assign.
@@ -1042,12 +1044,14 @@ type AddonModAssignSubmissionAttemptFormatted = AddonModAssignSubmissionAttempt
10421044
*/
10431045
type AddonModAssignSubmissionFeedbackFormatted = AddonModAssignSubmissionFeedback & {
10441046
advancedgrade?: boolean; // Calculated in the app. Whether it uses advanced grading.
1047+
penalty?: string; // Parsed from gradefordisplay.
10451048
};
10461049

10471050
type AddonModAssignSubmissionPreviousAttemptFormatted = AddonModAssignSubmissionPreviousAttempt & {
10481051
submissionStatusBadge?: StatusBadge;
10491052
grader?: CoreUserProfile;
10501053
advancedgrade?: boolean;
1054+
penalty?: string; // Parsed from gradefordisplay.
10511055
};
10521056

10531057
type StatusBadge = {
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
@addon_mod_assign @app @mod @mod_assign @javascript @lms_from5.0
2+
Feature: Grade penalties in the assignment activity
3+
4+
Background:
5+
Given the following "courses" exist:
6+
| fullname | shortname | category |
7+
| Course 1 | C1 | 0 |
8+
And the following "users" exist:
9+
| username | firstname | lastname | email |
10+
| teacher1 | Teacher | 1 | teacher1@example.com |
11+
| student1 | Student | 1 | student10@example.com |
12+
And the following "course enrolments" exist:
13+
| user | course | role |
14+
| teacher1 | C1 | editingteacher |
15+
| student1 | C1 | student |
16+
And I enable grade penalties for assignment
17+
And the following "activity" exists:
18+
| activity | assign |
19+
| course | C1 |
20+
| name | Test assignment name |
21+
| intro | Test assignment description |
22+
| grade | 100 |
23+
| duedate | ##yesterday## |
24+
| gradepenalty | 1 |
25+
| assignsubmission_onlinetext_enabled | 1 |
26+
| submissiondrafts | 0 |
27+
| maxattempts | -1 |
28+
| attemptreopenmethod | manual |
29+
# Add a submission.
30+
And the following "mod_assign > submissions" exist:
31+
| assign | user | onlinetext |
32+
| Test assignment name | student1 | I'm the student first submission |
33+
And I am on the "Test assignment name" Activity page logged in as teacher1
34+
And I go to "Student 1" "Test assignment name" activity advanced grading page
35+
And I set the following fields to these values:
36+
| Grade out of 100 | 50 |
37+
| Notify student | 0 |
38+
| Allow another attempt | 1 |
39+
And I press "Save changes"
40+
And I log out
41+
42+
Scenario: View submission with grade penalty as student
43+
Given I entered the assign activity "Test assignment name" on course "Course 1" as "student1" in the app
44+
When I press "Attempt 1" in the app
45+
Then I should find "Late penalty applied -10.00 marks" within "Feedback" "ion-card" in the app
46+
And I should find "Late penalty applied -10.00 marks" within "Attempt 1" "ion-accordion" in the app
47+
48+
Scenario: View activity summary with grade penalty as student
49+
Given I entered the assign activity "Test assignment name" on course "Course 1" as "student1" in the app
50+
When I press "Information" "ion-button" in the app
51+
And I press "Grade" "ion-item" in the app
52+
Then I should find "Late penalty applied -10.00 marks" within "Gradebook" "ion-card" in the app
53+
54+
Scenario: View submission with grade penalty as teacher
55+
Given I entered the assign activity "Test assignment name" on course "Course 1" as "teacher1" in the app
56+
When I press "Participants" in the app
57+
And I press "Student 1" in the app
58+
And I press "Attempt 1" in the app
59+
Then I should find "Late penalty applied -10.00 marks" within "Feedback" "ion-card" in the app
60+
And I should find "Late penalty applied -10.00 marks" within "Attempt 1" "ion-accordion" in the app
61+
62+
Scenario: Edit feedback with grade penalty as teacher
63+
Given I entered the assign activity "Test assignment name" on course "Course 1" as "teacher1" in the app
64+
When I press "Participants" in the app
65+
And I press "Student 1" in the app
66+
And I press "Grade" "ion-button" in the app
67+
Then I should find "Late penalty applied -10.00 marks" in the app

src/core/features/course/components/module-summary/module-summary.html

+23-11
Original file line numberDiff line numberDiff line change
@@ -69,22 +69,34 @@ <h2>
6969
<ion-icon name="fas-chevron-right" flip-rtl slot="start" aria-hidden="true" class="expandable-status-icon"
7070
[class.expandable-status-icon-expanded]="grade.expanded" />
7171
<ion-label>
72-
<p class="item-heading" *ngIf="!grade.itemmodule">
73-
<core-format-text [text]="grade.gradeitem" contextLevel="course" [contextInstanceId]="courseId" />
72+
<p class="item-heading">
73+
@if (grade.itemmodule) {
74+
{{ 'core.gradenoun' | translate }}
75+
} @else {
76+
<core-format-text [text]="grade.gradeitem" contextLevel="course" [contextInstanceId]="courseId" />
77+
}
7478
</p>
75-
<p class="item-heading" *ngIf="grade.itemmodule">
76-
{{ 'core.gradenoun' | translate }}
77-
</p>
78-
<p *ngIf="grade.grade && grade.grade !== '-'" [innerHTML]="grade.grade"></p>
79-
<ion-badge *ngIf="!grade.grade || grade.grade === '-'" color="light">
80-
{{ 'core.grades.notgraded' | translate }}
81-
</ion-badge>
79+
@if (grade.grade && grade.grade !== '-') {
80+
<p class="core-grading-summary-grade">
81+
@if (grade.penalty) {
82+
<ion-icon name="fas-triangle-exclamation" color="danger" aria-hidden="true" />
83+
}
84+
<span [innerHTML]="grade.grade"></span>
85+
</p>
86+
} @else {
87+
<ion-badge color="light">
88+
{{ 'core.grades.notgraded' | translate }}
89+
</ion-badge>
90+
}
91+
@if (grade.penalty) {
92+
<p class="core-grading-summary-penalty" [class.sr-only]="!grade.expanded">
93+
{{ grade.penalty }}
94+
</p>
95+
}
8296
</ion-label>
8397
<ion-icon *ngIf="grade.icon" name="{{grade.icon}}" slot="end" [attr.aria-label]="grade.iconAlt" />
8498
<img *ngIf="grade.image && !grade.itemmodule" [url]="grade.image" slot="end" [alt]="grade.iconAlt"
8599
core-external-content />
86-
<ion-icon *ngIf="grade.image && grade.itemmodule" name="fas-chart-bar" slot="end"
87-
[attr.aria-label]="grade.iconAlt" />
88100
</ion-item>
89101
<div *ngIf="grade.expanded" [id]="'grade-'+grade.id">
90102
<ion-item class="ion-text-wrap" *ngIf="grade.weight?.length > 0 && grade.weight !== '-'">

src/core/features/course/components/module-summary/module-summary.scss

+20
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,25 @@
11
@use "theme/globals" as *;
22

3+
:host {
4+
.core-grading-summary-grade {
5+
display: flex;
6+
align-items: center;
7+
8+
ion-icon {
9+
flex-shrink: 0;
10+
}
11+
12+
::ng-deep .penalty-indicator-icon {
13+
display: none;
14+
}
15+
}
16+
17+
.core-grading-summary-penalty {
18+
color: var(--danger);
19+
font-size: 0.75em;
20+
}
21+
}
22+
323
:host ::ng-deep ion-item[collapsible] ion-label {
424
margin-top: 12px;
525
}

src/core/features/grades/pages/course/course.html

+26-4
Original file line numberDiff line numberDiff line change
@@ -79,10 +79,21 @@ <h1>{{ title }}</h1>
7979
} @else if (column.name === 'grade') {
8080
<td [class.ion-hide-md-down]="column.hiddenPhone"
8181
class="ion-text-start core-grades-table-grade {{row.gradeClass}}">
82-
@if (row.gradeIcon) {
83-
<ion-icon [name]="row.gradeIcon" [attr.aria-label]="row.gradeIconAlt" />
82+
<div>
83+
@if (row.gradeIcon) {
84+
<ion-icon [name]="row.gradeIcon" [attr.aria-label]="row.gradeIconAlt" />
85+
}
86+
@if (row.penalty) {
87+
<ion-icon name="fas-triangle-exclamation" color="danger"
88+
aria-hidden="true" />
89+
}
90+
<span [innerHTML]="row[column.name]"></span>
91+
</div>
92+
@if (row.penalty) {
93+
<div class="core-grades-expanded-grade-penalty" [class.sr-only]="showSummary">
94+
{{ row.penalty }}
95+
</div>
8496
}
85-
<span [innerHTML]="row[column.name]"></span>
8697
</td>
8798
} @else if (column.name !== 'gradeitem' && column.name !== 'feedback' &&
8899
column.name !== 'grade' && row[column.name] !== undefined) {
@@ -112,7 +123,18 @@ <h1>{{ title }}</h1>
112123
<ion-item class="ion-text-wrap">
113124
<ion-label>
114125
<p class="item-heading">{{ 'core.gradenoun' | translate}}</p>
115-
<p [innerHTML]="row.grade"></p>
126+
<p class="core-grades-expanded-grade">
127+
@if (row.penalty) {
128+
<ion-icon name="fas-triangle-exclamation" color="danger"
129+
aria-hidden="true" />
130+
}
131+
<span [innerHTML]="row.grade"></span>
132+
</p>
133+
@if (row.penalty) {
134+
<p class="core-grades-expanded-grade-penalty">
135+
{{ row.penalty }}
136+
</p>
137+
}
116138
</ion-label>
117139
</ion-item>
118140
}

0 commit comments

Comments
 (0)