Skip to content

Commit 2af5ddf

Browse files
authored
Merge pull request #4319 from crazyserver/MOBILE-3671
Mobile 3671 Assignment previous attempts
2 parents c77faf2 + d0173a3 commit 2af5ddf

File tree

68 files changed

+1921
-1562
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

68 files changed

+1921
-1562
lines changed

scripts/langindex.json

+6
Original file line numberDiff line numberDiff line change
@@ -357,8 +357,11 @@
357357
"addon.mod_assign.applytoteam": "assign",
358358
"addon.mod_assign.assignmentisdue": "assign",
359359
"addon.mod_assign.assigntimeleft": "assign",
360+
"addon.mod_assign.attempt": "quiz",
361+
"addon.mod_assign.attempthistory": "assign",
360362
"addon.mod_assign.attemptnumber": "assign",
361363
"addon.mod_assign.attemptreopenmethod": "assign",
364+
"addon.mod_assign.attemptreopenmethod_automatic": "assign",
362365
"addon.mod_assign.attemptreopenmethod_manual": "assign",
363366
"addon.mod_assign.attemptreopenmethod_untilpass": "assign",
364367
"addon.mod_assign.attemptsettings": "assign",
@@ -383,6 +386,7 @@
383386
"addon.mod_assign.erroreditpluginsnotsupported": "local_moodlemobileapp",
384387
"addon.mod_assign.errorshowinginformation": "local_moodlemobileapp",
385388
"addon.mod_assign.extensionduedate": "assign",
389+
"addon.mod_assign.feedback": "assign",
386390
"addon.mod_assign.feedbacknotsupported": "local_moodlemobileapp",
387391
"addon.mod_assign.grade": "grades",
388392
"addon.mod_assign.graded": "assign",
@@ -1878,6 +1882,7 @@
18781882
"core.fullnameandsitename": "local_moodlemobileapp",
18791883
"core.fullscreen": "h5p",
18801884
"core.goto": "local_moodlemobileapp",
1885+
"core.gradenoun": "moodle",
18811886
"core.grades.aggregatemean": "grades",
18821887
"core.grades.aggregatesum": "grades",
18831888
"core.grades.average": "grades",
@@ -1904,6 +1909,7 @@
19041909
"core.grades.range": "grades",
19051910
"core.grades.rank": "grades",
19061911
"core.grades.weight": "grades",
1912+
"core.gradeverb": "moodle",
19071913
"core.group": "moodle",
19081914
"core.groupsseparate": "moodle",
19091915
"core.groupsvisible": "moodle",

src/addons/coursecompletion/services/coursecompletion.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -136,13 +136,13 @@ export class AddonCourseCompletionProvider {
136136
* @param siteId Site ID. If not defined, use current site.
137137
* @returns Promise to be resolved when the completion is retrieved.
138138
*/
139-
getCompletion(
139+
async getCompletion(
140140
courseId: number,
141141
userId?: number,
142142
preSets: CoreSiteWSPreSets = {},
143143
siteId?: string,
144144
): Promise<AddonCourseCompletionCourseCompletionStatus> {
145-
return firstValueFrom(this.getCompletionObservable(courseId, {
145+
return await firstValueFrom(this.getCompletionObservable(courseId, {
146146
userId,
147147
preSets,
148148
siteId,

src/addons/mod/assign/assign.module.ts

+4-6
Original file line numberDiff line numberDiff line change
@@ -23,15 +23,15 @@ import { CorePushNotificationsDelegate } from '@features/pushnotifications/servi
2323
import { CoreCronDelegate } from '@services/cron';
2424
import { CORE_SITE_SCHEMAS } from '@services/sites';
2525
import { AddonModAssignFeedbackModule } from './feedback/feedback.module';
26-
import { OFFLINE_SITE_SCHEMA } from './services/database/assign';
26+
import { ADDON_MOD_ASSIGN_OFFLINE_SITE_SCHEMA } from './services/database/assign';
2727
import { AddonModAssignIndexLinkHandler } from './services/handlers/index-link';
2828
import { AddonModAssignListLinkHandler } from './services/handlers/list-link';
2929
import { AddonModAssignModuleHandler } from './services/handlers/module';
3030
import { AddonModAssignPrefetchHandler } from './services/handlers/prefetch';
3131
import { AddonModAssignPushClickHandler } from './services/handlers/push-click';
3232
import { AddonModAssignSyncCronHandler } from './services/handlers/sync-cron';
3333
import { AddonModAssignSubmissionModule } from './submission/submission.module';
34-
import { ADDON_MOD_ASSIGN_COMPONENT, ADDON_MOD_ASSIGN_PAGE_NAME } from './constants';
34+
import { ADDON_MOD_ASSIGN_COMPONENT_LEGACY, ADDON_MOD_ASSIGN_PAGE_NAME } from './constants';
3535
import { conditionalRoutes } from '@/app/app-routing.module';
3636
import { canLeaveGuard } from '@guards/can-leave';
3737
import { CoreScreen } from '@services/screen';
@@ -97,7 +97,6 @@ const mobileRoutes: Routes = [
9797
{
9898
path: ':courseId/:cmId/submission/:submitId',
9999
loadComponent: () => import('./pages/submission-review/submission-review'),
100-
canDeactivate: [canLeaveGuard],
101100
},
102101
];
103102

@@ -110,7 +109,6 @@ const tabletRoutes: Routes = [
110109
{
111110
path: ':submitId',
112111
loadComponent: () => import('./pages/submission-review/submission-review'),
113-
canDeactivate: [canLeaveGuard],
114112
},
115113
],
116114
},
@@ -135,7 +133,7 @@ const routes: Routes = [
135133
providers: [
136134
{
137135
provide: CORE_SITE_SCHEMAS,
138-
useValue: [OFFLINE_SITE_SCHEMA],
136+
useValue: [ADDON_MOD_ASSIGN_OFFLINE_SITE_SCHEMA],
139137
multi: true,
140138
},
141139
{
@@ -149,7 +147,7 @@ const routes: Routes = [
149147
CoreCronDelegate.register(AddonModAssignSyncCronHandler.instance);
150148
CorePushNotificationsDelegate.registerClickHandler(AddonModAssignPushClickHandler.instance);
151149

152-
CoreCourseHelper.registerModuleReminderClick(ADDON_MOD_ASSIGN_COMPONENT);
150+
CoreCourseHelper.registerModuleReminderClick(ADDON_MOD_ASSIGN_COMPONENT_LEGACY);
153151
},
154152
},
155153
],

src/addons/mod/assign/classes/base-feedback-plugin-component.ts

+5-3
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,20 @@ export class AddonModAssignFeedbackPluginBaseComponent implements IAddonModAssig
4040
* Open a modal to edit the feedback plugin.
4141
*
4242
* @returns Promise resolved with the input data, rejected if cancelled.
43+
* @deprecated since 5.0.0. Use inline forms instead.
4344
*/
4445
async editFeedback(): Promise<AddonModAssignFeedbackCommentsTextData> {
4546
if (!this.canEdit) {
4647
throw new CoreError('Cannot edit feedback');
4748
}
4849

49-
const { AddonModAssignEditFeedbackModalComponent } =
50-
await import('@addons/mod/assign/components/edit-feedback-modal/edit-feedback-modal');
50+
// eslint-disable-next-line deprecation/deprecation
51+
const { AddonModAssignEditPluginFeedbackModalComponent } =
52+
await import('@addons/mod/assign/components/edit-feedback-plugin-modal/edit-feedback-plugin-modal');
5153

5254
// Create the navigation modal.
5355
const modalData = await CoreModals.openModal<AddonModAssignFeedbackCommentsTextData>({
54-
component: AddonModAssignEditFeedbackModalComponent,
56+
component: AddonModAssignEditPluginFeedbackModalComponent,
5557
componentProps: {
5658
assign: this.assign,
5759
submission: this.submission,

src/addons/mod/assign/classes/submissions-source.ts

+6-13
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,17 @@ import { CoreEvents } from '@singletons/events';
2323
import {
2424
AddonModAssign,
2525
AddonModAssignAssign,
26-
AddonModAssignGradingStates,
2726
AddonModAssignSubmission,
28-
AddonModAssignSubmissionStatusValues,
2927
} from '../services/assign';
3028
import { AddonModAssignHelper, AddonModAssignSubmissionFormatted } from '../services/assign-helper';
3129
import { AddonModAssignOffline } from '../services/assign-offline';
3230
import { AddonModAssignSync } from '../services/assign-sync';
33-
import { ADDON_MOD_ASSIGN_MANUAL_SYNCED } from '../constants';
31+
import {
32+
ADDON_MOD_ASSIGN_MANUAL_SYNCED,
33+
AddonModAssignGradingStates,
34+
AddonModAssignListFilterName,
35+
AddonModAssignSubmissionStatusValues,
36+
} from '../constants';
3437

3538
/**
3639
* Provides a collection of assignment submissions.
@@ -256,13 +259,3 @@ export type AddonModAssignSubmissionForList = AddonModAssignSubmissionFormatted
256259
gradingStatusTranslationId?: string; // Calculated in the app. Key of the text of the submission grading status.
257260
needsGrading?: boolean; // Calculated in the app. If submission and grading status means that it needs grading.
258261
};
259-
260-
/**
261-
* List filter by status name.
262-
*/
263-
export enum AddonModAssignListFilterName {
264-
ALL = '',
265-
NEED_GRADING = 'needgrading',
266-
DRAFT = 'draft',
267-
SUBMITTED = 'submitted',
268-
}
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
<ion-header>
22
<ion-toolbar>
33
<ion-title>
4-
<h1>{{ plugin.name }}</h1>
4+
<h1>{{'addon.mod_assign.grade' | translate}}</h1>
55
</ion-title>
66
<ion-buttons slot="end">
77
<ion-button fill="clear" (click)="closeModal()" [ariaLabel]="'core.close' | translate">
@@ -11,8 +11,104 @@ <h1>{{ plugin.name }}</h1>
1111
</ion-toolbar>
1212
</ion-header>
1313
<ion-content>
14-
<form name="addon-mod_assign-edit-feedback-form" *ngIf="userId && plugin" #editFeedbackForm>
15-
<addon-mod-assign-feedback-plugin [assign]="assign" [submission]="submission" [userId]="userId" [plugin]="plugin" [edit]="true" />
16-
<ion-button expand="block" (click)="done($event)">{{ 'core.done' | translate }}</ion-button>
17-
</form>
14+
@if (gradeInfo) {
15+
<form name="addon-mod_assign-edit-feedback-form" #editFeedbackForm>
16+
<!-- Numeric grade.
17+
Use a text input because otherwise we cannot read the value if it has an invalid character. -->
18+
<ion-item class="ion-text-wrap" *ngIf="grade.method === 'simple' && !grade.scale">
19+
<ion-input name="grade" *ngIf="!grade.disabled" type="text" [(ngModel)]="grade.grade" min="0" [max]="gradeInfo.grade"
20+
[lang]="grade.lang" [label]="'addon.mod_assign.gradeoutof' | translate: {$a: gradeInfo.grade}" labelPlacement="stacked"
21+
[helperText]="grade.disabled ? ('addon.mod_assign.gradelocked' | translate) : null" />
22+
</ion-item>
23+
24+
<!-- Grade using a scale. -->
25+
<ion-item class="ion-text-wrap" *ngIf="grade.method === 'simple' && grade.scale">
26+
<ion-select name="grade" [(ngModel)]="grade.grade" interface="action-sheet" [disabled]="grade.disabled"
27+
[cancelText]="'core.cancel' | translate" [interfaceOptions]="{header: 'addon.mod_assign.grade' | translate}">
28+
<p class="item-heading" slot="label">{{ 'addon.mod_assign.grade' | translate }}</p>
29+
<ion-select-option *ngFor="let grade of grade.scale" [value]="grade.value">
30+
{{grade.label}}
31+
</ion-select-option>
32+
</ion-select>
33+
</ion-item>
34+
35+
<!-- Outcomes. -->
36+
<ion-item class="ion-text-wrap" *ngFor="let outcome of gradeInfo.outcomes">
37+
<ion-select name="outcome" *ngIf="outcome.itemNumber" [(ngModel)]="outcome.selectedId" interface="action-sheet"
38+
[disabled]="!!gradeInfo.disabled" [cancelText]="'core.cancel' | translate" [interfaceOptions]="{header: outcome.name }">
39+
<p class="item-heading" slot="label">{{ outcome.name }}</p>
40+
<ion-select-option *ngFor="let grade of outcome.options" [value]="grade.value">
41+
{{grade.label}}
42+
</ion-select-option>
43+
</ion-select>
44+
<p *ngIf="!outcome.itemNumber">{{ outcome.selected }}</p>
45+
</ion-item>
46+
47+
<!-- Assign grade if it wasn't released to gradebook. -->
48+
<ion-item class="ion-text-wrap" *ngIf="gradeInfo && grade.unreleasedGrade !== undefined">
49+
<ion-label>
50+
<p class="item-heading">{{ 'addon.mod_assign.currentassigngrade' | translate }}</p>
51+
<p *ngIf="grade.method !== 'simple' || !grade.scale">{{ grade.unreleasedGrade}} / {{ gradeInfo.grade }}</p>
52+
<p *ngIf="grade.method === 'simple' && grade.scale">{{ grade.unreleasedGrade }}</p>
53+
</ion-label>
54+
</ion-item>
55+
56+
<!-- Gradebook grade for simple grading. -->
57+
<ion-item class="ion-text-wrap" *ngIf="grade.method === 'simple'">
58+
<ion-label>
59+
<p class="item-heading">{{ 'addon.mod_assign.currentgrade' | translate }}</p>
60+
<p *ngIf="grade.gradebookGrade && !grade.scale">
61+
{{ grade.gradebookGrade }}
62+
</p>
63+
<p *ngIf="grade.gradebookGrade && grade.scale">
64+
{{ grade.scale[grade.gradebookGrade].label }}
65+
</p>
66+
<p *ngIf="!grade.gradebookGrade">-</p>
67+
</ion-label>
68+
</ion-item>
69+
70+
@if (feedback) {
71+
<addon-mod-assign-feedback-plugin *ngFor="let plugin of feedback.plugins" [assign]="assign" [submission]="userSubmission"
72+
[userId]="submitId" [plugin]="plugin" [canEdit]="true" [edit]="true" />
73+
}
74+
75+
<!--- Apply grade to all team members. -->
76+
<ion-item class="ion-text-wrap" *ngIf="assign.teamsubmission">
77+
<ion-toggle name="applyToAll" [(ngModel)]="grade.applyToAll">
78+
<p class="item-heading">{{ 'addon.mod_assign.groupsubmissionsettings' | translate }}</p>
79+
<p>{{ 'addon.mod_assign.applytoteam' | translate }}</p>
80+
</ion-toggle>
81+
</ion-item>
82+
83+
<!-- Attempt status. -->
84+
@if (assign.maxattempts !== 1) {
85+
<ion-item class="ion-text-wrap">
86+
<ion-label>
87+
<p class="item-heading">{{ 'addon.mod_assign.attemptsettings' | translate }}</p>
88+
<p *ngIf="assign.maxattempts === unlimitedAttempts">
89+
{{ 'addon.mod_assign.outof' | translate :
90+
{'$a': {'current': currentAttemptNumber, 'total': maxAttemptsText} } }}
91+
</p>
92+
<p *ngIf="assign.maxattempts !== unlimitedAttempts">
93+
{{ 'addon.mod_assign.outof' | translate :
94+
{'$a': {'current': currentAttemptNumber, 'total': assign.maxattempts} } }}
95+
</p>
96+
<p>
97+
{{ 'addon.mod_assign.attemptreopenmethod' | translate }}:
98+
{{ 'addon.mod_assign.attemptreopenmethod_' + assign.attemptreopenmethod | translate }}
99+
</p>
100+
</ion-label>
101+
</ion-item>
102+
<ion-item *ngIf="allowAddAttempt">
103+
<ion-toggle name="addAttempt" [(ngModel)]="grade.addAttempt">
104+
<p>{{ 'addon.mod_assign.addattempt' | translate }}</p>
105+
</ion-toggle>
106+
</ion-item>
107+
}
108+
109+
<ion-button expand="block" (click)="submitGrade()" type="submit">
110+
{{ 'core.gradeverb' | translate }}
111+
</ion-button>
112+
</form>
113+
}
18114
</ion-content>

0 commit comments

Comments
 (0)