commit
7d0ae4375c
|
@ -18,13 +18,14 @@ import { CoreGroupInfo, CoreGroups } from '@services/groups';
|
||||||
import { CoreSites } from '@services/sites';
|
import { CoreSites } from '@services/sites';
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
import { Translate } from '@singletons';
|
import { Translate } from '@singletons';
|
||||||
|
import { CoreIonicColorNames } from '@singletons/colors';
|
||||||
import { CoreEvents } from '@singletons/events';
|
import { CoreEvents } from '@singletons/events';
|
||||||
import {
|
import {
|
||||||
AddonModAssign,
|
AddonModAssign,
|
||||||
AddonModAssignAssign,
|
AddonModAssignAssign,
|
||||||
AddonModAssignGrade,
|
AddonModAssignGradingStates,
|
||||||
AddonModAssignProvider,
|
|
||||||
AddonModAssignSubmission,
|
AddonModAssignSubmission,
|
||||||
|
AddonModAssignSubmissionStatusValues,
|
||||||
} from '../services/assign';
|
} from '../services/assign';
|
||||||
import { AddonModAssignHelper, AddonModAssignSubmissionFormatted } from '../services/assign-helper';
|
import { AddonModAssignHelper, AddonModAssignSubmissionFormatted } from '../services/assign-helper';
|
||||||
import { AddonModAssignOffline } from '../services/assign-offline';
|
import { AddonModAssignOffline } from '../services/assign-offline';
|
||||||
|
@ -38,15 +39,15 @@ export class AddonModAssignSubmissionsSource extends CoreRoutedItemsManagerSourc
|
||||||
/**
|
/**
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
static getSourceId(courseId: number, moduleId: number, selectedStatus?: string): string {
|
static getSourceId(courseId: number, moduleId: number, selectedStatus?: AddonModAssignListFilterName): string {
|
||||||
selectedStatus = selectedStatus ?? '__empty__';
|
const statusId = selectedStatus ?? '__empty__';
|
||||||
|
|
||||||
return `submissions-${courseId}-${moduleId}-${selectedStatus}`;
|
return `submissions-${courseId}-${moduleId}-${statusId}`;
|
||||||
}
|
}
|
||||||
|
|
||||||
readonly COURSE_ID: number;
|
readonly COURSE_ID: number;
|
||||||
readonly MODULE_ID: number;
|
readonly MODULE_ID: number;
|
||||||
readonly SELECTED_STATUS: string | undefined;
|
readonly SELECTED_STATUS: AddonModAssignListFilterName | undefined;
|
||||||
|
|
||||||
assign?: AddonModAssignAssign;
|
assign?: AddonModAssignAssign;
|
||||||
groupId = 0;
|
groupId = 0;
|
||||||
|
@ -62,7 +63,7 @@ export class AddonModAssignSubmissionsSource extends CoreRoutedItemsManagerSourc
|
||||||
canviewsubmissions: false,
|
canviewsubmissions: false,
|
||||||
};
|
};
|
||||||
|
|
||||||
constructor(courseId: number, moduleId: number, selectedStatus?: string) {
|
constructor(courseId: number, moduleId: number, selectedStatus?: AddonModAssignListFilterName) {
|
||||||
super();
|
super();
|
||||||
|
|
||||||
this.COURSE_ID = courseId;
|
this.COURSE_ID = courseId;
|
||||||
|
@ -157,90 +158,82 @@ export class AddonModAssignSubmissionsSource extends CoreRoutedItemsManagerSourc
|
||||||
}
|
}
|
||||||
|
|
||||||
// Fetch submissions and grades.
|
// Fetch submissions and grades.
|
||||||
const submissions =
|
let submissions =
|
||||||
await AddonModAssignHelper.getSubmissionsUserData(
|
await AddonModAssignHelper.getSubmissionsUserData(
|
||||||
assign,
|
assign,
|
||||||
this.submissionsData.submissions,
|
this.submissionsData.submissions,
|
||||||
this.groupId,
|
this.groupId,
|
||||||
);
|
);
|
||||||
|
|
||||||
// Get assignment grades only if workflow is not enabled to check grading date.
|
// Get assignment grades only if workflow is not enabled to check grading date.
|
||||||
const grades = !assign.markingworkflow
|
let grades = !assign.markingworkflow
|
||||||
? await AddonModAssign.getAssignmentGrades(assign.id, { cmId: assign.cmid })
|
? await AddonModAssign.getAssignmentGrades(assign.id, { cmId: assign.cmid })
|
||||||
: [];
|
: [];
|
||||||
|
|
||||||
|
// Remove grades (not graded) and sort by timemodified DESC to allow finding quicker.
|
||||||
|
grades = grades.filter((grade) => parseInt(grade.grade, 10) >= 0).sort((a, b) => b.timemodified - a.timemodified);
|
||||||
// Filter the submissions to get only the ones with the right status and add some extra data.
|
// Filter the submissions to get only the ones with the right status and add some extra data.
|
||||||
const getNeedGrading = this.SELECTED_STATUS == AddonModAssignProvider.NEED_GRADING;
|
if (this.SELECTED_STATUS == AddonModAssignListFilterName.NEED_GRADING) {
|
||||||
const searchStatus = getNeedGrading ? AddonModAssignProvider.SUBMISSION_STATUS_SUBMITTED : this.SELECTED_STATUS;
|
const promises: Promise<void>[] = submissions.map(async (submission: AddonModAssignSubmissionForList) => {
|
||||||
|
// Only show the submissions that need to be graded.
|
||||||
|
submission.needsGrading = await AddonModAssign.needsSubmissionToBeGraded(submission, assign.id);
|
||||||
|
});
|
||||||
|
|
||||||
const promises: Promise<void>[] = [];
|
await Promise.all(promises);
|
||||||
const showSubmissions: AddonModAssignSubmissionForList[] = [];
|
|
||||||
|
|
||||||
submissions.forEach((submission: AddonModAssignSubmissionForList) => {
|
submissions = submissions.filter((submission: AddonModAssignSubmissionForList) => submission.needsGrading);
|
||||||
if (!searchStatus || searchStatus == submission.status) {
|
} else if (this.SELECTED_STATUS) {
|
||||||
promises.push(
|
const searchStatus = this.SELECTED_STATUS == AddonModAssignListFilterName.DRAFT
|
||||||
CoreUtils.ignoreErrors(
|
? AddonModAssignSubmissionStatusValues.DRAFT
|
||||||
AddonModAssignOffline.getSubmissionGrade(assign.id, submission.userid),
|
: AddonModAssignSubmissionStatusValues.SUBMITTED;
|
||||||
).then(async (data) => {
|
|
||||||
if (getNeedGrading) {
|
|
||||||
// Only show the submissions that need to be graded.
|
|
||||||
const add = await AddonModAssign.needsSubmissionToBeGraded(submission, assign.id);
|
|
||||||
|
|
||||||
if (!add) {
|
submissions = submissions.filter((submission: AddonModAssignSubmissionForList) => submission.status == searchStatus);
|
||||||
return;
|
}
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Load offline grades.
|
const showSubmissions: AddonModAssignSubmissionForList[] = await Promise.all(
|
||||||
const notSynced = !!data && submission.timemodified < data.timemodified;
|
submissions.map(async (submission: AddonModAssignSubmissionForList) => {
|
||||||
|
const gradeData =
|
||||||
|
await CoreUtils.ignoreErrors(AddonModAssignOffline.getSubmissionGrade(assign.id, submission.userid));
|
||||||
|
|
||||||
if (submission.gradingstatus == 'graded' && !assign.markingworkflow) {
|
// Load offline grades.
|
||||||
// Get the last grade of the submission.
|
const notSynced = !!gradeData && submission.timemodified < gradeData.timemodified;
|
||||||
const grade = grades
|
|
||||||
.filter((grade) => grade.userid == submission.userid)
|
|
||||||
.reduce(
|
|
||||||
(a, b) => (a && a.timemodified > b.timemodified ? a : b),
|
|
||||||
<AddonModAssignGrade | undefined> undefined,
|
|
||||||
);
|
|
||||||
|
|
||||||
if (grade && grade.timemodified < submission.timemodified) {
|
if (!assign.markingworkflow) {
|
||||||
submission.gradingstatus = AddonModAssignProvider.GRADED_FOLLOWUP_SUBMIT;
|
// Get the last grade of the submission.
|
||||||
}
|
const grade = grades.find((grade) => grade.userid == submission.userid);
|
||||||
}
|
|
||||||
submission.statusColor = AddonModAssign.getSubmissionStatusColor(submission.status);
|
|
||||||
submission.gradingColor = AddonModAssign.getSubmissionGradingStatusColor(
|
|
||||||
submission.gradingstatus,
|
|
||||||
);
|
|
||||||
|
|
||||||
// Show submission status if not submitted for grading.
|
if (grade) {
|
||||||
if (submission.statusColor != 'success' || !submission.gradingstatus) {
|
// Override status if grade is found.
|
||||||
submission.statusTranslated = Translate.instant(
|
submission.gradingstatus = grade.timemodified < submission.timemodified
|
||||||
'addon.mod_assign.submissionstatus_' + submission.status,
|
? AddonModAssignGradingStates.GRADED_FOLLOWUP_SUBMIT
|
||||||
);
|
: AddonModAssignGradingStates.GRADED;
|
||||||
} else {
|
}
|
||||||
submission.statusTranslated = '';
|
}
|
||||||
}
|
submission.statusColor = AddonModAssign.getSubmissionStatusColor(submission.status);
|
||||||
|
submission.gradingColor = AddonModAssign.getSubmissionGradingStatusColor(
|
||||||
if (notSynced) {
|
submission.gradingstatus,
|
||||||
submission.gradingStatusTranslationId = 'addon.mod_assign.gradenotsynced';
|
|
||||||
submission.gradingColor = '';
|
|
||||||
} else if (submission.statusColor != 'danger' || submission.gradingColor != 'danger') {
|
|
||||||
// Show grading status if one of the statuses is not done.
|
|
||||||
submission.gradingStatusTranslationId = AddonModAssign.getSubmissionGradingStatusTranslationId(
|
|
||||||
submission.gradingstatus,
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
submission.gradingStatusTranslationId = '';
|
|
||||||
}
|
|
||||||
|
|
||||||
showSubmissions.push(submission);
|
|
||||||
|
|
||||||
return;
|
|
||||||
}),
|
|
||||||
);
|
);
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
await Promise.all(promises);
|
submission.statusTranslated = Translate.instant(
|
||||||
|
'addon.mod_assign.submissionstatus_' + submission.status,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (notSynced) {
|
||||||
|
submission.gradingStatusTranslationId = 'addon.mod_assign.gradenotsynced';
|
||||||
|
submission.gradingColor = '';
|
||||||
|
} else if (submission.statusColor != CoreIonicColorNames.DANGER ||
|
||||||
|
submission.gradingColor != CoreIonicColorNames.DANGER) {
|
||||||
|
// Show grading status if one of the statuses is not done.
|
||||||
|
submission.gradingStatusTranslationId = AddonModAssign.getSubmissionGradingStatusTranslationId(
|
||||||
|
submission.gradingstatus,
|
||||||
|
);
|
||||||
|
} else {
|
||||||
|
submission.gradingStatusTranslationId = '';
|
||||||
|
}
|
||||||
|
|
||||||
|
return submission;
|
||||||
|
}),
|
||||||
|
);
|
||||||
|
|
||||||
return { items: showSubmissions };
|
return { items: showSubmissions };
|
||||||
}
|
}
|
||||||
|
@ -255,4 +248,15 @@ export type AddonModAssignSubmissionForList = AddonModAssignSubmissionFormatted
|
||||||
gradingColor?: string; // Calculated in the app. Color of the submission grading status.
|
gradingColor?: string; // Calculated in the app. Color of the submission grading status.
|
||||||
statusTranslated?: string; // Calculated in the app. Translated text of the submission status.
|
statusTranslated?: string; // Calculated in the app. Translated text of the submission status.
|
||||||
gradingStatusTranslationId?: string; // Calculated in the app. Key of the text of the submission grading status.
|
gradingStatusTranslationId?: string; // Calculated in the app. Key of the text of the submission grading status.
|
||||||
|
needsGrading?: boolean; // Calculated in the app. If submission and grading status means that it needs grading.
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* List filter by status name.
|
||||||
|
*/
|
||||||
|
export enum AddonModAssignListFilterName {
|
||||||
|
ALL = '',
|
||||||
|
NEED_GRADING = 'needgrading',
|
||||||
|
DRAFT = 'draft',
|
||||||
|
SUBMITTED = 'submitted',
|
||||||
};
|
};
|
||||||
|
|
|
@ -28,6 +28,7 @@ import { CoreTimeUtils } from '@services/utils/time';
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
import { Translate } from '@singletons';
|
import { Translate } from '@singletons';
|
||||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
||||||
|
import { AddonModAssignListFilterName } from '../../classes/submissions-source';
|
||||||
import {
|
import {
|
||||||
AddonModAssign,
|
AddonModAssign,
|
||||||
AddonModAssignAssign,
|
AddonModAssignAssign,
|
||||||
|
@ -75,12 +76,12 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
||||||
};
|
};
|
||||||
|
|
||||||
// Status.
|
// Status.
|
||||||
submissionStatusSubmitted = AddonModAssignProvider.SUBMISSION_STATUS_SUBMITTED;
|
submissionStatusSubmitted = AddonModAssignListFilterName.SUBMITTED;
|
||||||
submissionStatusDraft = AddonModAssignProvider.SUBMISSION_STATUS_DRAFT;
|
submissionStatusDraft = AddonModAssignListFilterName.DRAFT;
|
||||||
needGrading = AddonModAssignProvider.NEED_GRADING;
|
needGrading = AddonModAssignListFilterName.NEED_GRADING;
|
||||||
|
|
||||||
protected currentUserId?: number; // Current user ID.
|
protected currentUserId!: number; // Current user ID.
|
||||||
protected currentSite?: CoreSite; // Current user ID.
|
protected currentSite!: CoreSite; // Current site.
|
||||||
protected syncEventName = AddonModAssignSyncProvider.AUTO_SYNCED;
|
protected syncEventName = AddonModAssignSyncProvider.AUTO_SYNCED;
|
||||||
|
|
||||||
// Observers.
|
// Observers.
|
||||||
|
@ -93,6 +94,9 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
||||||
@Optional() courseContentsPage?: CoreCourseContentsPage,
|
@Optional() courseContentsPage?: CoreCourseContentsPage,
|
||||||
) {
|
) {
|
||||||
super('AddonModLessonIndexComponent', content, courseContentsPage);
|
super('AddonModLessonIndexComponent', content, courseContentsPage);
|
||||||
|
|
||||||
|
this.currentSite = CoreSites.getRequiredCurrentSite();
|
||||||
|
this.currentUserId = this.currentSite.getUserId();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -101,9 +105,6 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
||||||
async ngOnInit(): Promise<void> {
|
async ngOnInit(): Promise<void> {
|
||||||
super.ngOnInit();
|
super.ngOnInit();
|
||||||
|
|
||||||
this.currentUserId = CoreSites.getCurrentSiteUserId();
|
|
||||||
this.currentSite = CoreSites.getCurrentSite();
|
|
||||||
|
|
||||||
// Listen to events.
|
// Listen to events.
|
||||||
this.savedObserver = CoreEvents.on(
|
this.savedObserver = CoreEvents.on(
|
||||||
AddonModAssignProvider.SUBMISSION_SAVED_EVENT,
|
AddonModAssignProvider.SUBMISSION_SAVED_EVENT,
|
||||||
|
@ -139,8 +140,12 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
||||||
|
|
||||||
await this.loadContent(false, true);
|
await this.loadContent(false, true);
|
||||||
|
|
||||||
|
if (!this.assign) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await AddonModAssign.logView(this.assign!.id, this.assign!.name);
|
await AddonModAssign.logView(this.assign.id, this.assign.name);
|
||||||
CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
|
CoreCourse.checkModuleCompletion(this.courseId, this.module.completiondata);
|
||||||
} catch {
|
} catch {
|
||||||
// Ignore errors. Just don't check Module completion.
|
// Ignore errors. Just don't check Module completion.
|
||||||
|
@ -148,10 +153,10 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
||||||
|
|
||||||
if (this.canViewAllSubmissions) {
|
if (this.canViewAllSubmissions) {
|
||||||
// User can see all submissions, log grading view.
|
// User can see all submissions, log grading view.
|
||||||
CoreUtils.ignoreErrors(AddonModAssign.logGradingView(this.assign!.id, this.assign!.name));
|
CoreUtils.ignoreErrors(AddonModAssign.logGradingView(this.assign.id, this.assign.name));
|
||||||
} else if (this.canViewOwnSubmission) {
|
} else if (this.canViewOwnSubmission) {
|
||||||
// User can only see their own submission, log view the user submission.
|
// User can only see their own submission, log view the user submission.
|
||||||
CoreUtils.ignoreErrors(AddonModAssign.logSubmissionView(this.assign!.id, this.assign!.name));
|
CoreUtils.ignoreErrors(AddonModAssign.logSubmissionView(this.assign.id, this.assign.name));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -266,7 +271,11 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
||||||
async setGroup(groupId = 0): Promise<void> {
|
async setGroup(groupId = 0): Promise<void> {
|
||||||
this.group = groupId;
|
this.group = groupId;
|
||||||
|
|
||||||
const submissionStatus = await AddonModAssign.getSubmissionStatus(this.assign!.id, {
|
if (!this.assign) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const submissionStatus = await AddonModAssign.getSubmissionStatus(this.assign.id, {
|
||||||
groupId: this.group,
|
groupId: this.group,
|
||||||
cmId: this.module.id,
|
cmId: this.module.id,
|
||||||
});
|
});
|
||||||
|
@ -278,10 +287,10 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.summary?.warnofungroupedusers === true) {
|
if (this.summary.warnofungroupedusers === true) {
|
||||||
this.summary.warnofungroupedusers = 'ungroupedusers';
|
this.summary.warnofungroupedusers = 'ungroupedusers';
|
||||||
} else {
|
} else {
|
||||||
switch (this.summary?.warnofungroupedusers) {
|
switch (this.summary.warnofungroupedusers) {
|
||||||
case AddonModAssignProvider.WARN_GROUPS_REQUIRED:
|
case AddonModAssignProvider.WARN_GROUPS_REQUIRED:
|
||||||
this.summary.warnofungroupedusers = 'ungroupedusers';
|
this.summary.warnofungroupedusers = 'ungroupedusers';
|
||||||
break;
|
break;
|
||||||
|
@ -294,7 +303,7 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
this.needsGradingAvailable = (submissionStatus.gradingsummary?.submissionsneedgradingcount || 0) > 0;
|
this.needsGradingAvailable = this.summary.submissionsneedgradingcount > 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -303,7 +312,7 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
||||||
* @param status Status to see.
|
* @param status Status to see.
|
||||||
* @param hasSubmissions If the status has any submission.
|
* @param hasSubmissions If the status has any submission.
|
||||||
*/
|
*/
|
||||||
goToSubmissionList(status?: string, hasSubmissions = false): void {
|
goToSubmissionList(status?: AddonModAssignListFilterName, hasSubmissions = false): void {
|
||||||
if (status !== undefined && !hasSubmissions) {
|
if (status !== undefined && !hasSubmissions) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
@ -386,16 +395,16 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
||||||
* @return True if refresh is needed, false otherwise.
|
* @return True if refresh is needed, false otherwise.
|
||||||
*/
|
*/
|
||||||
protected isRefreshSyncNeeded(syncEventData: AddonModAssignAutoSyncData): boolean {
|
protected isRefreshSyncNeeded(syncEventData: AddonModAssignAutoSyncData): boolean {
|
||||||
if (this.assign && syncEventData.assignId == this.assign.id) {
|
if (!this.assign || syncEventData.assignId != this.assign.id) {
|
||||||
if (syncEventData.warnings && syncEventData.warnings.length) {
|
return false;
|
||||||
// Show warnings.
|
|
||||||
CoreDomUtils.showErrorModal(syncEventData.warnings[0]);
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return false;
|
if (syncEventData.warnings && syncEventData.warnings.length) {
|
||||||
|
// Show warnings.
|
||||||
|
CoreDomUtils.showErrorModal(syncEventData.warnings[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -403,8 +412,12 @@ export class AddonModAssignIndexComponent extends CoreCourseModuleMainActivityCo
|
||||||
*
|
*
|
||||||
* @return Promise resolved when done.
|
* @return Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected sync(): Promise<AddonModAssignSyncResult> {
|
protected async sync(): Promise<AddonModAssignSyncResult | void> {
|
||||||
return AddonModAssignSync.syncAssign(this.assign!.id);
|
if (!this.assign) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
await AddonModAssignSync.syncAssign(this.assign.id);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -25,6 +25,9 @@ import {
|
||||||
AddonModAssign,
|
AddonModAssign,
|
||||||
AddonModAssignGetSubmissionStatusWSResponse,
|
AddonModAssignGetSubmissionStatusWSResponse,
|
||||||
AddonModAssignSavePluginData,
|
AddonModAssignSavePluginData,
|
||||||
|
AddonModAssignGradingStates,
|
||||||
|
AddonModAssignSubmissionStatusValues,
|
||||||
|
AddonModAssignAttemptReopenMethodValues,
|
||||||
} from '../../services/assign';
|
} from '../../services/assign';
|
||||||
import {
|
import {
|
||||||
AddonModAssignAutoSyncData,
|
AddonModAssignAutoSyncData,
|
||||||
|
@ -125,9 +128,9 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
|
||||||
showDates = false; // Whether to show some dates.
|
showDates = false; // Whether to show some dates.
|
||||||
|
|
||||||
// Some constants.
|
// Some constants.
|
||||||
statusNew = AddonModAssignProvider.SUBMISSION_STATUS_NEW;
|
statusNew = AddonModAssignSubmissionStatusValues.NEW;
|
||||||
statusReopened = AddonModAssignProvider.SUBMISSION_STATUS_REOPENED;
|
statusReopened = AddonModAssignSubmissionStatusValues.REOPENED;
|
||||||
attemptReopenMethodNone = AddonModAssignProvider.ATTEMPT_REOPEN_METHOD_NONE;
|
attemptReopenMethodNone = AddonModAssignAttemptReopenMethodValues.NONE;
|
||||||
unlimitedAttempts = AddonModAssignProvider.UNLIMITED_ATTEMPTS;
|
unlimitedAttempts = AddonModAssignProvider.UNLIMITED_ATTEMPTS;
|
||||||
|
|
||||||
protected siteId: string; // Current site ID.
|
protected siteId: string; // Current site ID.
|
||||||
|
@ -214,7 +217,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
|
||||||
}
|
}
|
||||||
|
|
||||||
// Not submitted.
|
// Not submitted.
|
||||||
if (!this.userSubmission || this.userSubmission.status != AddonModAssignProvider.SUBMISSION_STATUS_SUBMITTED) {
|
if (!this.userSubmission || this.userSubmission.status != AddonModAssignSubmissionStatusValues.SUBMITTED) {
|
||||||
|
|
||||||
if (response.lastattempt?.submissionsenabled || response.gradingsummary?.submissionsenabled) {
|
if (response.lastattempt?.submissionsenabled || response.gradingsummary?.submissionsenabled) {
|
||||||
this.timeRemaining = Translate.instant(
|
this.timeRemaining = Translate.instant(
|
||||||
|
@ -612,11 +615,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
|
||||||
|
|
||||||
// If we have data about the grader, get its profile.
|
// If we have data about the grader, get its profile.
|
||||||
if (feedback.grade && feedback.grade.grader > 0) {
|
if (feedback.grade && feedback.grade.grader > 0) {
|
||||||
try {
|
this.grader = await CoreUtils.ignoreErrors(CoreUser.getProfile(feedback.grade.grader, this.courseId));
|
||||||
this.grader = await CoreUser.getProfile(feedback.grade.grader, this.courseId);
|
|
||||||
} catch {
|
|
||||||
// Ignore errors.
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
delete this.grader;
|
delete this.grader;
|
||||||
}
|
}
|
||||||
|
@ -633,7 +632,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
|
||||||
if (feedback.grade && feedback.grade.grade && !this.grade.grade) {
|
if (feedback.grade && feedback.grade.grade && !this.grade.grade) {
|
||||||
const parsedGrade = parseFloat(feedback.grade.grade);
|
const parsedGrade = parseFloat(feedback.grade.grade);
|
||||||
|
|
||||||
this.grade!.grade = parsedGrade >= 0 ? parsedGrade : undefined;
|
this.grade.grade = parsedGrade >= 0 ? parsedGrade : undefined;
|
||||||
this.grade.gradebookGrade = CoreUtils.formatFloat(this.grade.grade);
|
this.grade.gradebookGrade = CoreUtils.formatFloat(this.grade.grade);
|
||||||
this.originalGrades.grade = this.grade.grade;
|
this.originalGrades.grade = this.grade.grade;
|
||||||
}
|
}
|
||||||
|
@ -655,7 +654,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
|
||||||
// Treat the grade info.
|
// Treat the grade info.
|
||||||
await this.treatGradeInfo();
|
await this.treatGradeInfo();
|
||||||
|
|
||||||
const isManual = this.assign!.attemptreopenmethod == AddonModAssignProvider.ATTEMPT_REOPEN_METHOD_MANUAL;
|
const isManual = this.assign!.attemptreopenmethod == AddonModAssignAttemptReopenMethodValues.MANUAL;
|
||||||
const isUnlimited = this.assign!.maxattempts == AddonModAssignProvider.UNLIMITED_ATTEMPTS;
|
const isUnlimited = this.assign!.maxattempts == AddonModAssignProvider.UNLIMITED_ATTEMPTS;
|
||||||
const isLessThanMaxAttempts = !!this.userSubmission && (this.userSubmission.attemptnumber < (this.assign!.maxattempts - 1));
|
const isLessThanMaxAttempts = !!this.userSubmission && (this.userSubmission.attemptnumber < (this.assign!.maxattempts - 1));
|
||||||
|
|
||||||
|
@ -672,7 +671,7 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
|
||||||
|
|
||||||
if (this.lastAttempt?.gradingstatus == 'graded' && !this.assign!.markingworkflow && this.userSubmission && feedback) {
|
if (this.lastAttempt?.gradingstatus == 'graded' && !this.assign!.markingworkflow && this.userSubmission && feedback) {
|
||||||
if (feedback.gradeddate < this.userSubmission.timemodified) {
|
if (feedback.gradeddate < this.userSubmission.timemodified) {
|
||||||
this.lastAttempt.gradingstatus = AddonModAssignProvider.GRADED_FOLLOWUP_SUBMIT;
|
this.lastAttempt.gradingstatus = AddonModAssignGradingStates.GRADED_FOLLOWUP_SUBMIT;
|
||||||
|
|
||||||
// Get grading text and color.
|
// Get grading text and color.
|
||||||
this.gradingStatusTranslationId = AddonModAssign.getSubmissionGradingStatusTranslationId(
|
this.gradingStatusTranslationId = AddonModAssign.getSubmissionGradingStatusTranslationId(
|
||||||
|
@ -753,43 +752,43 @@ export class AddonModAssignSubmissionComponent implements OnInit, OnDestroy, Can
|
||||||
* @param status Submission status.
|
* @param status Submission status.
|
||||||
*/
|
*/
|
||||||
protected setStatusNameAndClass(status: AddonModAssignGetSubmissionStatusWSResponse): void {
|
protected setStatusNameAndClass(status: AddonModAssignGetSubmissionStatusWSResponse): void {
|
||||||
const translateService = Translate.instance;
|
|
||||||
|
|
||||||
if (this.hasOffline || this.submittedOffline) {
|
if (this.hasOffline || this.submittedOffline) {
|
||||||
// Offline data.
|
// Offline data.
|
||||||
this.statusTranslated = translateService.instant('core.notsent');
|
this.statusTranslated = Translate.instant('core.notsent');
|
||||||
this.statusColor = 'warning';
|
this.statusColor = 'warning';
|
||||||
} else if (!this.assign!.teamsubmission) {
|
} else if (!this.assign!.teamsubmission) {
|
||||||
|
|
||||||
// Single submission.
|
// Single submission.
|
||||||
if (this.userSubmission && this.userSubmission.status != this.statusNew) {
|
if (this.userSubmission && this.userSubmission.status != this.statusNew) {
|
||||||
this.statusTranslated = translateService.instant('addon.mod_assign.submissionstatus_' + this.userSubmission.status);
|
this.statusTranslated = Translate.instant('addon.mod_assign.submissionstatus_' + this.userSubmission.status);
|
||||||
this.statusColor = AddonModAssign.getSubmissionStatusColor(this.userSubmission.status);
|
this.statusColor = AddonModAssign.getSubmissionStatusColor(this.userSubmission.status);
|
||||||
} else {
|
} else {
|
||||||
if (!status.lastattempt?.submissionsenabled) {
|
if (!status.lastattempt?.submissionsenabled) {
|
||||||
this.statusTranslated = translateService.instant('addon.mod_assign.noonlinesubmissions');
|
this.statusTranslated = Translate.instant('addon.mod_assign.noonlinesubmissions');
|
||||||
this.statusColor = AddonModAssign.getSubmissionStatusColor('noonlinesubmissions');
|
this.statusColor =
|
||||||
|
AddonModAssign.getSubmissionStatusColor(AddonModAssignSubmissionStatusValues.NO_ONLINE_SUBMISSIONS);
|
||||||
} else {
|
} else {
|
||||||
this.statusTranslated = translateService.instant('addon.mod_assign.noattempt');
|
this.statusTranslated = Translate.instant('addon.mod_assign.noattempt');
|
||||||
this.statusColor = AddonModAssign.getSubmissionStatusColor('noattempt');
|
this.statusColor = AddonModAssign.getSubmissionStatusColor(AddonModAssignSubmissionStatusValues.NO_ATTEMPT);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
// Team submission.
|
// Team submission.
|
||||||
if (!status.lastattempt?.submissiongroup && this.assign!.preventsubmissionnotingroup) {
|
if (!status.lastattempt?.submissiongroup && this.assign!.preventsubmissionnotingroup) {
|
||||||
this.statusTranslated = translateService.instant('addon.mod_assign.nosubmission');
|
this.statusTranslated = Translate.instant('addon.mod_assign.nosubmission');
|
||||||
this.statusColor = AddonModAssign.getSubmissionStatusColor('nosubmission');
|
this.statusColor = AddonModAssign.getSubmissionStatusColor(AddonModAssignSubmissionStatusValues.NO_SUBMISSION);
|
||||||
} else if (this.userSubmission && this.userSubmission.status != this.statusNew) {
|
} else if (this.userSubmission && this.userSubmission.status != this.statusNew) {
|
||||||
this.statusTranslated = translateService.instant('addon.mod_assign.submissionstatus_' + this.userSubmission.status);
|
this.statusTranslated = Translate.instant('addon.mod_assign.submissionstatus_' + this.userSubmission.status);
|
||||||
this.statusColor = AddonModAssign.getSubmissionStatusColor(this.userSubmission.status);
|
this.statusColor = AddonModAssign.getSubmissionStatusColor(this.userSubmission.status);
|
||||||
} else {
|
} else {
|
||||||
if (!status.lastattempt?.submissionsenabled) {
|
if (!status.lastattempt?.submissionsenabled) {
|
||||||
this.statusTranslated = translateService.instant('addon.mod_assign.noonlinesubmissions');
|
this.statusTranslated = Translate.instant('addon.mod_assign.noonlinesubmissions');
|
||||||
this.statusColor = AddonModAssign.getSubmissionStatusColor('noonlinesubmissions');
|
this.statusColor =
|
||||||
|
AddonModAssign.getSubmissionStatusColor(AddonModAssignSubmissionStatusValues.NO_ONLINE_SUBMISSIONS);
|
||||||
} else {
|
} else {
|
||||||
this.statusTranslated = translateService.instant('addon.mod_assign.nosubmission');
|
this.statusTranslated = Translate.instant('addon.mod_assign.nosubmission');
|
||||||
this.statusColor = AddonModAssign.getSubmissionStatusColor('nosubmission');
|
this.statusColor = AddonModAssign.getSubmissionStatusColor(AddonModAssignSubmissionStatusValues.NO_SUBMISSION);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1184,7 +1183,7 @@ type AddonModAssignSubmissionGrade = {
|
||||||
grade?: number | string;
|
grade?: number | string;
|
||||||
gradebookGrade?: string;
|
gradebookGrade?: string;
|
||||||
modified?: number;
|
modified?: number;
|
||||||
gradingStatus?: string;
|
gradingStatus?: AddonModAssignGradingStates;
|
||||||
addAttempt: boolean;
|
addAttempt: boolean;
|
||||||
applyToAll: boolean;
|
applyToAll: boolean;
|
||||||
scale?: CoreMenuItem<number>[];
|
scale?: CoreMenuItem<number>[];
|
||||||
|
|
|
@ -63,14 +63,16 @@
|
||||||
{{ 'addon.mod_assign.defaultteam' | translate }}
|
{{ 'addon.mod_assign.defaultteam' | translate }}
|
||||||
</span>
|
</span>
|
||||||
</p>
|
</p>
|
||||||
<ion-badge class="ion-text-center ion-text-wrap" [color]="submission.statusColor"
|
<p *ngIf="submission.statusTranslated">
|
||||||
*ngIf="submission.statusTranslated">
|
<ion-badge class="ion-text-center ion-text-wrap" [color]="submission.statusColor">
|
||||||
{{ submission.statusTranslated }}
|
{{ submission.statusTranslated }}
|
||||||
</ion-badge>
|
</ion-badge>
|
||||||
<ion-badge class="ion-text-center ion-text-wrap" [color]="submission.gradingColor"
|
</p>
|
||||||
*ngIf="submission.gradingStatusTranslationId">
|
<p *ngIf="submission.gradingStatusTranslationId">
|
||||||
{{ submission.gradingStatusTranslationId | translate }}
|
<ion-badge class="ion-text-center ion-text-wrap" [color]="submission.gradingColor">
|
||||||
</ion-badge>
|
{{ submission.gradingStatusTranslationId | translate }}
|
||||||
|
</ion-badge>
|
||||||
|
</p>
|
||||||
</ion-label>
|
</ion-label>
|
||||||
</ion-item>
|
</ion-item>
|
||||||
</ng-container>
|
</ng-container>
|
||||||
|
|
|
@ -23,7 +23,11 @@ import { CoreSites } from '@services/sites';
|
||||||
import { CoreDomUtils } from '@services/utils/dom';
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
import { Translate } from '@singletons';
|
import { Translate } from '@singletons';
|
||||||
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
import { CoreEventObserver, CoreEvents } from '@singletons/events';
|
||||||
import { AddonModAssignSubmissionForList, AddonModAssignSubmissionsSource } from '../../classes/submissions-source';
|
import {
|
||||||
|
AddonModAssignListFilterName,
|
||||||
|
AddonModAssignSubmissionForList,
|
||||||
|
AddonModAssignSubmissionsSource,
|
||||||
|
} from '../../classes/submissions-source';
|
||||||
import { AddonModAssignAssign, AddonModAssignProvider } from '../../services/assign';
|
import { AddonModAssignAssign, AddonModAssignProvider } from '../../services/assign';
|
||||||
import {
|
import {
|
||||||
AddonModAssignSyncProvider,
|
AddonModAssignSyncProvider,
|
||||||
|
@ -85,7 +89,7 @@ export class AddonModAssignSubmissionListPage implements AfterViewInit, OnDestro
|
||||||
const moduleId = CoreNavigator.getRequiredRouteNumberParam('cmId');
|
const moduleId = CoreNavigator.getRequiredRouteNumberParam('cmId');
|
||||||
const courseId = CoreNavigator.getRequiredRouteNumberParam('courseId');
|
const courseId = CoreNavigator.getRequiredRouteNumberParam('courseId');
|
||||||
const groupId = CoreNavigator.getRouteNumberParam('groupId') || 0;
|
const groupId = CoreNavigator.getRouteNumberParam('groupId') || 0;
|
||||||
const selectedStatus = CoreNavigator.getRouteParam('status');
|
const selectedStatus = CoreNavigator.getRouteParam<AddonModAssignListFilterName>('status');
|
||||||
const submissionsSource = CoreRoutedItemsManagerSourcesTracker.getOrCreateSource(
|
const submissionsSource = CoreRoutedItemsManagerSourcesTracker.getOrCreateSource(
|
||||||
AddonModAssignSubmissionsSource,
|
AddonModAssignSubmissionsSource,
|
||||||
[courseId, moduleId, selectedStatus],
|
[courseId, moduleId, selectedStatus],
|
||||||
|
@ -143,7 +147,7 @@ export class AddonModAssignSubmissionListPage implements AfterViewInit, OnDestro
|
||||||
this.title = Translate.instant(
|
this.title = Translate.instant(
|
||||||
selectedStatus
|
selectedStatus
|
||||||
? (
|
? (
|
||||||
selectedStatus === AddonModAssignProvider.NEED_GRADING
|
selectedStatus === AddonModAssignListFilterName.NEED_GRADING
|
||||||
? 'addon.mod_assign.numberofsubmissionsneedgrading'
|
? 'addon.mod_assign.numberofsubmissionsneedgrading'
|
||||||
: `addon.mod_assign.submissionstatus_${selectedStatus}`
|
: `addon.mod_assign.submissionstatus_${selectedStatus}`
|
||||||
)
|
)
|
||||||
|
|
|
@ -22,7 +22,7 @@ import { IonRefresher } from '@ionic/angular';
|
||||||
import { CoreNavigator } from '@services/navigator';
|
import { CoreNavigator } from '@services/navigator';
|
||||||
import { CoreScreen } from '@services/screen';
|
import { CoreScreen } from '@services/screen';
|
||||||
import { CoreDomUtils } from '@services/utils/dom';
|
import { CoreDomUtils } from '@services/utils/dom';
|
||||||
import { AddonModAssignSubmissionsSource } from '../../classes/submissions-source';
|
import { AddonModAssignListFilterName, AddonModAssignSubmissionsSource } from '../../classes/submissions-source';
|
||||||
import { AddonModAssignSubmissionComponent } from '../../components/submission/submission';
|
import { AddonModAssignSubmissionComponent } from '../../components/submission/submission';
|
||||||
import { AddonModAssign, AddonModAssignAssign } from '../../services/assign';
|
import { AddonModAssign, AddonModAssignAssign } from '../../services/assign';
|
||||||
|
|
||||||
|
@ -53,7 +53,7 @@ export class AddonModAssignSubmissionReviewPage implements OnInit, OnDestroy, Ca
|
||||||
constructor(protected route: ActivatedRoute) { }
|
constructor(protected route: ActivatedRoute) { }
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Component being initialized.
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
ngOnInit(): void {
|
ngOnInit(): void {
|
||||||
this.route.queryParams.subscribe((params) => {
|
this.route.queryParams.subscribe((params) => {
|
||||||
|
@ -63,7 +63,7 @@ export class AddonModAssignSubmissionReviewPage implements OnInit, OnDestroy, Ca
|
||||||
this.submitId = CoreNavigator.getRouteNumberParam('submitId') || 0;
|
this.submitId = CoreNavigator.getRouteNumberParam('submitId') || 0;
|
||||||
this.blindId = CoreNavigator.getRouteNumberParam('blindId', { params });
|
this.blindId = CoreNavigator.getRouteNumberParam('blindId', { params });
|
||||||
const groupId = CoreNavigator.getRequiredRouteNumberParam('groupId');
|
const groupId = CoreNavigator.getRequiredRouteNumberParam('groupId');
|
||||||
const selectedStatus = CoreNavigator.getRouteParam('selectedStatus');
|
const selectedStatus = CoreNavigator.getRouteParam<AddonModAssignListFilterName>('selectedStatus');
|
||||||
const submissionsSource = CoreRoutedItemsManagerSourcesTracker.getOrCreateSource(
|
const submissionsSource = CoreRoutedItemsManagerSourcesTracker.getOrCreateSource(
|
||||||
AddonModAssignSubmissionsSource,
|
AddonModAssignSubmissionsSource,
|
||||||
[this.courseId, this.moduleId, selectedStatus],
|
[this.courseId, this.moduleId, selectedStatus],
|
||||||
|
|
|
@ -25,6 +25,7 @@ import {
|
||||||
AddonModAssign,
|
AddonModAssign,
|
||||||
AddonModAssignPlugin,
|
AddonModAssignPlugin,
|
||||||
AddonModAssignSavePluginData,
|
AddonModAssignSavePluginData,
|
||||||
|
AddonModAssignSubmissionStatusValues,
|
||||||
} from './assign';
|
} from './assign';
|
||||||
import { AddonModAssignOffline } from './assign-offline';
|
import { AddonModAssignOffline } from './assign-offline';
|
||||||
import { CoreUtils } from '@services/utils/utils';
|
import { CoreUtils } from '@services/utils/utils';
|
||||||
|
@ -55,8 +56,8 @@ export class AddonModAssignHelperProvider {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (submission.status == AddonModAssignProvider.SUBMISSION_STATUS_NEW ||
|
if (submission.status == AddonModAssignSubmissionStatusValues.NEW ||
|
||||||
submission.status == AddonModAssignProvider.SUBMISSION_STATUS_REOPENED) {
|
submission.status == AddonModAssignSubmissionStatusValues.REOPENED) {
|
||||||
// It's a new submission, allow creating it in offline.
|
// It's a new submission, allow creating it in offline.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -149,7 +150,7 @@ export class AddonModAssignHelperProvider {
|
||||||
attemptnumber: 0,
|
attemptnumber: 0,
|
||||||
timecreated: 0,
|
timecreated: 0,
|
||||||
timemodified: 0,
|
timemodified: 0,
|
||||||
status: '',
|
status: AddonModAssignSubmissionStatusValues.NEW,
|
||||||
groupid: 0,
|
groupid: 0,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -398,48 +399,42 @@ export class AddonModAssignHelperProvider {
|
||||||
groupId?: number,
|
groupId?: number,
|
||||||
options: CoreSitesCommonWSOptions = {},
|
options: CoreSitesCommonWSOptions = {},
|
||||||
): Promise<AddonModAssignSubmissionFormatted[]> {
|
): Promise<AddonModAssignSubmissionFormatted[]> {
|
||||||
const parts = await this.getParticipants(assign, groupId, options);
|
const participants = await this.getParticipants(assign, groupId, options);
|
||||||
|
|
||||||
const blind = assign.blindmarking && !assign.revealidentities;
|
const blind = !!assign.blindmarking && !assign.revealidentities;
|
||||||
const result: AddonModAssignSubmissionFormatted[] = [];
|
const teamsubmission = !!assign.teamsubmission;
|
||||||
const participants: {[id: number]: AddonModAssignParticipant} = CoreUtils.arrayToObject(parts, 'id');
|
|
||||||
|
|
||||||
submissions.forEach((submission) => {
|
if (teamsubmission) {
|
||||||
submission.submitid = submission.userid && submission.userid > 0 ? submission.userid : submission.blindid;
|
// On team submission discard user submissions.
|
||||||
if (submission.submitid === undefined || submission.submitid <= 0) {
|
submissions = submissions.filter((submission) => submission.userid == 0);
|
||||||
return;
|
}
|
||||||
|
|
||||||
|
return participants.map((participant) => {
|
||||||
|
const groupId = participant.groupid ??
|
||||||
|
(participant.groups && participant.groups[0] ? participant.groups[0].id : 0);
|
||||||
|
|
||||||
|
const foundSubmission = submissions.find((submission) => {
|
||||||
|
if (teamsubmission) {
|
||||||
|
return submission.groupid == groupId;
|
||||||
|
}
|
||||||
|
|
||||||
|
const submitId = submission.userid && submission.userid > 0 ? submission.userid : submission.blindid;
|
||||||
|
|
||||||
|
return participant.id == submitId;
|
||||||
|
});
|
||||||
|
|
||||||
|
let submission: AddonModAssignSubmissionFormatted | undefined;
|
||||||
|
if (!foundSubmission) {
|
||||||
|
// Create submission if none.
|
||||||
|
submission = this.createEmptySubmission();
|
||||||
|
submission.groupid = groupId;
|
||||||
|
submission.status = participant.submitted
|
||||||
|
? AddonModAssignSubmissionStatusValues.SUBMITTED
|
||||||
|
: AddonModAssignSubmissionStatusValues.NEW;
|
||||||
|
} else {
|
||||||
|
submission = Object.assign({}, foundSubmission);
|
||||||
}
|
}
|
||||||
|
|
||||||
const participant = participants[submission.submitid];
|
|
||||||
if (!participant) {
|
|
||||||
// Avoid permission denied error. Participant not found on list.
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
delete participants[submission.submitid];
|
|
||||||
|
|
||||||
if (!blind) {
|
|
||||||
submission.userfullname = participant.fullname;
|
|
||||||
submission.userprofileimageurl = participant.profileimageurl;
|
|
||||||
}
|
|
||||||
|
|
||||||
submission.manyGroups = !!participant.groups && participant.groups.length > 1;
|
|
||||||
submission.noGroups = !!participant.groups && participant.groups.length == 0;
|
|
||||||
if (participant.groupname) {
|
|
||||||
submission.groupid = participant.groupid!;
|
|
||||||
submission.groupname = participant.groupname;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add to the list.
|
|
||||||
if (submission.userfullname || submission.blindid) {
|
|
||||||
result.push(submission);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// Create a submission for each participant left in the list (the participants already treated were removed).
|
|
||||||
CoreUtils.objectToArray(participants).forEach((participant: AddonModAssignParticipant) => {
|
|
||||||
const submission = this.createEmptySubmission();
|
|
||||||
|
|
||||||
submission.submitid = participant.id;
|
submission.submitid = participant.id;
|
||||||
|
|
||||||
if (!blind) {
|
if (!blind) {
|
||||||
|
@ -453,16 +448,13 @@ export class AddonModAssignHelperProvider {
|
||||||
submission.manyGroups = !!participant.groups && participant.groups.length > 1;
|
submission.manyGroups = !!participant.groups && participant.groups.length > 1;
|
||||||
submission.noGroups = !!participant.groups && participant.groups.length == 0;
|
submission.noGroups = !!participant.groups && participant.groups.length == 0;
|
||||||
if (participant.groupname) {
|
if (participant.groupname) {
|
||||||
submission.groupid = participant.groupid!;
|
submission.groupid = participant.groupid;
|
||||||
submission.groupname = participant.groupname;
|
submission.groupname = participant.groupname;
|
||||||
}
|
}
|
||||||
submission.status = participant.submitted ? AddonModAssignProvider.SUBMISSION_STATUS_SUBMITTED :
|
|
||||||
AddonModAssignProvider.SUBMISSION_STATUS_NEW;
|
|
||||||
|
|
||||||
result.push(submission);
|
return submission;
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -714,8 +706,9 @@ export const AddonModAssignHelper = makeSingleton(AddonModAssignHelperProvider);
|
||||||
* Assign submission with some calculated data.
|
* Assign submission with some calculated data.
|
||||||
*/
|
*/
|
||||||
export type AddonModAssignSubmissionFormatted =
|
export type AddonModAssignSubmissionFormatted =
|
||||||
Omit<AddonModAssignSubmission, 'userid'> & {
|
Omit<AddonModAssignSubmission, 'userid'|'groupid'> & {
|
||||||
userid?: number; // Student id.
|
userid?: number; // Student id.
|
||||||
|
groupid?: number; // Group id.
|
||||||
blindid?: number; // Calculated in the app. Blindid of the user that did the submission.
|
blindid?: number; // Calculated in the app. Blindid of the user that did the submission.
|
||||||
submitid?: number; // Calculated in the app. Userid or blindid of the user that did the submission.
|
submitid?: number; // Calculated in the app. Userid or blindid of the user that did the submission.
|
||||||
userfullname?: string; // Calculated in the app. Full name of the user that did the submission.
|
userfullname?: string; // Calculated in the app. Full name of the user that did the submission.
|
||||||
|
|
|
@ -34,6 +34,7 @@ import { CoreWSError } from '@classes/errors/wserror';
|
||||||
import { AddonModAssignAutoSyncData, AddonModAssignManualSyncData, AddonModAssignSyncProvider } from './assign-sync';
|
import { AddonModAssignAutoSyncData, AddonModAssignManualSyncData, AddonModAssignSyncProvider } from './assign-sync';
|
||||||
import { CoreFormFields } from '@singletons/form';
|
import { CoreFormFields } from '@singletons/form';
|
||||||
import { CoreFileHelper } from '@services/file-helper';
|
import { CoreFileHelper } from '@services/file-helper';
|
||||||
|
import { CoreIonicColorNames } from '@singletons/colors';
|
||||||
|
|
||||||
const ROOT_CACHE_KEY = 'mmaModAssign:';
|
const ROOT_CACHE_KEY = 'mmaModAssign:';
|
||||||
|
|
||||||
|
@ -64,23 +65,6 @@ export class AddonModAssignProvider {
|
||||||
static readonly SUBMISSION_COMPONENT = 'mmaModAssignSubmission';
|
static readonly SUBMISSION_COMPONENT = 'mmaModAssignSubmission';
|
||||||
static readonly UNLIMITED_ATTEMPTS = -1;
|
static readonly UNLIMITED_ATTEMPTS = -1;
|
||||||
|
|
||||||
// Submission status.
|
|
||||||
static readonly SUBMISSION_STATUS_NEW = 'new';
|
|
||||||
static readonly SUBMISSION_STATUS_REOPENED = 'reopened';
|
|
||||||
static readonly SUBMISSION_STATUS_DRAFT = 'draft';
|
|
||||||
static readonly SUBMISSION_STATUS_SUBMITTED = 'submitted';
|
|
||||||
|
|
||||||
// "Re-open" methods (to retry the assign).
|
|
||||||
static readonly ATTEMPT_REOPEN_METHOD_NONE = 'none';
|
|
||||||
static readonly ATTEMPT_REOPEN_METHOD_MANUAL = 'manual';
|
|
||||||
|
|
||||||
// Grading status.
|
|
||||||
static readonly GRADING_STATUS_GRADED = 'graded';
|
|
||||||
static readonly GRADING_STATUS_NOT_GRADED = 'notgraded';
|
|
||||||
static readonly MARKING_WORKFLOW_STATE_RELEASED = 'released';
|
|
||||||
static readonly NEED_GRADING = 'needgrading';
|
|
||||||
static readonly GRADED_FOLLOWUP_SUBMIT = 'gradedfollowupsubmit';
|
|
||||||
|
|
||||||
// Group submissions warnings.
|
// Group submissions warnings.
|
||||||
static readonly WARN_GROUPS_REQUIRED = 'warnrequired';
|
static readonly WARN_GROUPS_REQUIRED = 'warnrequired';
|
||||||
static readonly WARN_GROUPS_OPTIONAL = 'warnoptional';
|
static readonly WARN_GROUPS_OPTIONAL = 'warnoptional';
|
||||||
|
@ -108,17 +92,17 @@ export class AddonModAssignProvider {
|
||||||
const teamSubmission = submissionStatus.lastattempt?.teamsubmission;
|
const teamSubmission = submissionStatus.lastattempt?.teamsubmission;
|
||||||
|
|
||||||
if (teamSubmission) {
|
if (teamSubmission) {
|
||||||
if (teamSubmission.status === AddonModAssignProvider.SUBMISSION_STATUS_SUBMITTED) {
|
if (teamSubmission.status === AddonModAssignSubmissionStatusValues.SUBMITTED) {
|
||||||
// The assignment submission has been completed.
|
// The assignment submission has been completed.
|
||||||
return false;
|
return false;
|
||||||
} else if (userSubmission && userSubmission.status === AddonModAssignProvider.SUBMISSION_STATUS_SUBMITTED) {
|
} else if (userSubmission && userSubmission.status === AddonModAssignSubmissionStatusValues.SUBMITTED) {
|
||||||
// The user has already clicked the submit button on the team submission.
|
// The user has already clicked the submit button on the team submission.
|
||||||
return false;
|
return false;
|
||||||
} else if (assign.preventsubmissionnotingroup && !submissionStatus.lastattempt?.submissiongroup) {
|
} else if (assign.preventsubmissionnotingroup && !submissionStatus.lastattempt?.submissiongroup) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
} else if (userSubmission) {
|
} else if (userSubmission) {
|
||||||
if (userSubmission.status === AddonModAssignProvider.SUBMISSION_STATUS_SUBMITTED) {
|
if (userSubmission.status === AddonModAssignSubmissionStatusValues.SUBMITTED) {
|
||||||
// The assignment submission has been completed.
|
// The assignment submission has been completed.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -347,17 +331,17 @@ export class AddonModAssignProvider {
|
||||||
* @param status Grading status name
|
* @param status Grading status name
|
||||||
* @return The color name.
|
* @return The color name.
|
||||||
*/
|
*/
|
||||||
getSubmissionGradingStatusColor(status?: string): string {
|
getSubmissionGradingStatusColor(status?: AddonModAssignGradingStates): CoreIonicColorNames {
|
||||||
if (!status) {
|
if (!status) {
|
||||||
return '';
|
return CoreIonicColorNames.NONE;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status == AddonModAssignProvider.GRADING_STATUS_GRADED ||
|
if (status == AddonModAssignGradingStates.GRADED
|
||||||
status == AddonModAssignProvider.MARKING_WORKFLOW_STATE_RELEASED) {
|
|| status == AddonModAssignGradingStates.MARKING_WORKFLOW_STATE_RELEASED) {
|
||||||
return 'success';
|
return CoreIonicColorNames.SUCCESS;
|
||||||
}
|
}
|
||||||
|
|
||||||
return 'danger';
|
return CoreIonicColorNames.DANGER;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -366,13 +350,14 @@ export class AddonModAssignProvider {
|
||||||
* @param status Grading Status name
|
* @param status Grading Status name
|
||||||
* @return The status translation identifier.
|
* @return The status translation identifier.
|
||||||
*/
|
*/
|
||||||
getSubmissionGradingStatusTranslationId(status?: string): string | undefined {
|
getSubmissionGradingStatusTranslationId(status?: AddonModAssignGradingStates): string | undefined {
|
||||||
if (!status) {
|
if (!status) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (status == AddonModAssignProvider.GRADING_STATUS_GRADED || status == AddonModAssignProvider.GRADING_STATUS_NOT_GRADED
|
if (status == AddonModAssignGradingStates.GRADED
|
||||||
|| status == AddonModAssignProvider.GRADED_FOLLOWUP_SUBMIT) {
|
|| status == AddonModAssignGradingStates.NOT_GRADED
|
||||||
|
|| status == AddonModAssignGradingStates.GRADED_FOLLOWUP_SUBMIT) {
|
||||||
return 'addon.mod_assign.' + status;
|
return 'addon.mod_assign.' + status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -604,20 +589,20 @@ export class AddonModAssignProvider {
|
||||||
* @param status Status name
|
* @param status Status name
|
||||||
* @return The color name.
|
* @return The color name.
|
||||||
*/
|
*/
|
||||||
getSubmissionStatusColor(status: string): string {
|
getSubmissionStatusColor(status: AddonModAssignSubmissionStatusValues): CoreIonicColorNames {
|
||||||
switch (status) {
|
switch (status) {
|
||||||
case 'submitted':
|
case AddonModAssignSubmissionStatusValues.SUBMITTED:
|
||||||
return 'success';
|
return CoreIonicColorNames.SUCCESS;
|
||||||
case 'draft':
|
case AddonModAssignSubmissionStatusValues.DRAFT:
|
||||||
return 'info';
|
return CoreIonicColorNames.INFO;
|
||||||
case 'new':
|
case AddonModAssignSubmissionStatusValues.NEW:
|
||||||
case 'noattempt':
|
case AddonModAssignSubmissionStatusValues.NO_ATTEMPT:
|
||||||
case 'noonlinesubmissions':
|
case AddonModAssignSubmissionStatusValues.NO_ONLINE_SUBMISSIONS:
|
||||||
case 'nosubmission':
|
case AddonModAssignSubmissionStatusValues.NO_SUBMISSION:
|
||||||
case 'gradedfollowupsubmit':
|
case AddonModAssignSubmissionStatusValues.GRADED_FOLLOWUP_SUBMIT:
|
||||||
return 'danger';
|
return CoreIonicColorNames.DANGER;
|
||||||
default:
|
default:
|
||||||
return 'light';
|
return CoreIonicColorNames.LIGHT;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -878,7 +863,7 @@ export class AddonModAssignProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (submission) {
|
if (submission) {
|
||||||
if (assign.submissiondrafts && submission.status == AddonModAssignProvider.SUBMISSION_STATUS_SUBMITTED) {
|
if (assign.submissiondrafts && submission.status == AddonModAssignSubmissionStatusValues.SUBMITTED) {
|
||||||
// Drafts are tracked and the student has submitted the assignment.
|
// Drafts are tracked and the student has submitted the assignment.
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -970,13 +955,17 @@ export class AddonModAssignProvider {
|
||||||
* @return Promise resolved with boolean: whether it needs to be graded or not.
|
* @return Promise resolved with boolean: whether it needs to be graded or not.
|
||||||
*/
|
*/
|
||||||
async needsSubmissionToBeGraded(submission: AddonModAssignSubmissionFormatted, assignId: number): Promise<boolean> {
|
async needsSubmissionToBeGraded(submission: AddonModAssignSubmissionFormatted, assignId: number): Promise<boolean> {
|
||||||
|
if (submission.status != AddonModAssignSubmissionStatusValues.SUBMITTED) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
if (!submission.gradingstatus) {
|
if (!submission.gradingstatus) {
|
||||||
// This should not happen, but it's better to show rather than not showing any of the submissions.
|
// This should not happen, but it's better to show rather than not showing any of the submissions.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (submission.gradingstatus != AddonModAssignProvider.GRADING_STATUS_GRADED &&
|
if (submission.gradingstatus != AddonModAssignGradingStates.GRADED &&
|
||||||
submission.gradingstatus != AddonModAssignProvider.MARKING_WORKFLOW_STATE_RELEASED) {
|
submission.gradingstatus != AddonModAssignGradingStates.MARKING_WORKFLOW_STATE_RELEASED) {
|
||||||
// Not graded.
|
// Not graded.
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@ -1349,7 +1338,7 @@ export type AddonModAssignAssign = {
|
||||||
blindmarking: number; // If enabled, hide identities until reveal identities actioned.
|
blindmarking: number; // If enabled, hide identities until reveal identities actioned.
|
||||||
hidegrader?: number; // @since 3.7. If enabled, hide grader to student.
|
hidegrader?: number; // @since 3.7. If enabled, hide grader to student.
|
||||||
revealidentities: number; // Show identities for a blind marking assignment.
|
revealidentities: number; // Show identities for a blind marking assignment.
|
||||||
attemptreopenmethod: string; // Method used to control opening new attempts.
|
attemptreopenmethod: AddonModAssignAttemptReopenMethodValues; // Method used to control opening new attempts.
|
||||||
maxattempts: number; // Maximum number of attempts allowed.
|
maxattempts: number; // Maximum number of attempts allowed.
|
||||||
markingworkflow: number; // Enable marking workflow.
|
markingworkflow: number; // Enable marking workflow.
|
||||||
markingallocation: number; // Enable marking allocation.
|
markingallocation: number; // Enable marking allocation.
|
||||||
|
@ -1400,12 +1389,12 @@ export type AddonModAssignSubmission = {
|
||||||
attemptnumber: number; // Attempt number.
|
attemptnumber: number; // Attempt number.
|
||||||
timecreated: number; // Submission creation time.
|
timecreated: number; // Submission creation time.
|
||||||
timemodified: number; // Submission last modified time.
|
timemodified: number; // Submission last modified time.
|
||||||
status: string; // Submission status.
|
status: AddonModAssignSubmissionStatusValues; // Submission status.
|
||||||
groupid: number; // Group id.
|
groupid: number; // Group id.
|
||||||
assignment?: number; // Assignment id.
|
assignment?: number; // Assignment id.
|
||||||
latest?: number; // Latest attempt.
|
latest?: number; // Latest attempt.
|
||||||
plugins?: AddonModAssignPlugin[]; // Plugins.
|
plugins?: AddonModAssignPlugin[]; // Plugins.
|
||||||
gradingstatus?: string; // Grading status.
|
gradingstatus?: AddonModAssignGradingStates; // Grading status.
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1454,7 +1443,7 @@ export type AddonModAssignSubmissionAttempt = {
|
||||||
cansubmit: boolean; // Whether the user can submit.
|
cansubmit: boolean; // Whether the user can submit.
|
||||||
extensionduedate: number; // Extension due date.
|
extensionduedate: number; // Extension due date.
|
||||||
blindmarking: boolean; // Whether blind marking is enabled.
|
blindmarking: boolean; // Whether blind marking is enabled.
|
||||||
gradingstatus: string; // Grading status.
|
gradingstatus: AddonModAssignGradingStates; // Grading status.
|
||||||
usergroups: number[]; // User groups in the course.
|
usergroups: number[]; // User groups in the course.
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -1749,3 +1738,41 @@ export type AddonModAssignSubmissionSavedEventData = AddonModAssignSubmittedForG
|
||||||
* Data sent by GRADED_EVENT event.
|
* Data sent by GRADED_EVENT event.
|
||||||
*/
|
*/
|
||||||
export type AddonModAssignGradedEventData = AddonModAssignSubmittedForGradingEventData;
|
export type AddonModAssignGradedEventData = AddonModAssignSubmittedForGradingEventData;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Submission status.
|
||||||
|
* Constants on LMS starting with ASSIGN_SUBMISSION_STATUS_
|
||||||
|
*/
|
||||||
|
export enum AddonModAssignSubmissionStatusValues {
|
||||||
|
SUBMITTED = 'submitted',
|
||||||
|
DRAFT = 'draft',
|
||||||
|
NEW = 'new',
|
||||||
|
REOPENED = 'reopened',
|
||||||
|
// Added by App Statuses.
|
||||||
|
NO_ATTEMPT = 'noattempt',
|
||||||
|
NO_ONLINE_SUBMISSIONS = 'noonlinesubmissions',
|
||||||
|
NO_SUBMISSION = 'nosubmission',
|
||||||
|
GRADED_FOLLOWUP_SUBMIT = 'gradedfollowupsubmit',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Grading status.
|
||||||
|
* Constants on LMS starting with ASSIGN_GRADING_STATUS_
|
||||||
|
*/
|
||||||
|
export enum AddonModAssignGradingStates {
|
||||||
|
GRADED = 'graded',
|
||||||
|
NOT_GRADED = 'notgraded',
|
||||||
|
// Added by App Statuses.
|
||||||
|
MARKING_WORKFLOW_STATE_RELEASED = 'released', // with ASSIGN_MARKING_WORKFLOW_STATE_RELEASED
|
||||||
|
GRADED_FOLLOWUP_SUBMIT = 'gradedfollowupsubmit',
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Reopen attempt methods.
|
||||||
|
* Constants on LMS starting with ASSIGN_ATTEMPT_REOPEN_METHOD_
|
||||||
|
*/
|
||||||
|
export enum AddonModAssignAttemptReopenMethodValues {
|
||||||
|
NONE = 'none',
|
||||||
|
MANUAL = 'manual',
|
||||||
|
UNTILPASS = 'untilpass',
|
||||||
|
};
|
||||||
|
|
|
@ -44,7 +44,6 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
.core-loading-content {
|
.core-loading-content {
|
||||||
display: contents;
|
|
||||||
@include core-transition(opacity, 200ms);
|
@include core-transition(opacity, 200ms);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,18 @@ interface ColorComponents {
|
||||||
blue: number; // Blue component of an RGB color [0-255].
|
blue: number; // Blue component of an RGB color [0-255].
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Ionic color names.
|
||||||
|
*/
|
||||||
|
export enum CoreIonicColorNames {
|
||||||
|
SUCCESS = 'success',
|
||||||
|
INFO = 'info',
|
||||||
|
DANGER = 'danger',
|
||||||
|
DARK = 'dark',
|
||||||
|
LIGHT = 'light',
|
||||||
|
NONE = '',
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Singleton with helper functions for colors.
|
* Singleton with helper functions for colors.
|
||||||
*/
|
*/
|
||||||
|
|
Loading…
Reference in New Issue