MOBILE-3833 course: Use completion enums

main
Pau Ferrer Ocaña 2021-12-16 10:44:09 +01:00
parent efece1d4ac
commit 13fbaf0b0f
8 changed files with 101 additions and 51 deletions

View File

@ -100,7 +100,7 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
this.showCompletion = !!CoreSites.getRequiredCurrentSite().isVersionGreaterEqualThan('3.11');
if (this.showCompletion) {
CoreCourseHelper.calculateModuleCompletionData(this.module, this.courseId);
CoreCourseHelper.calculateModuleCompletionData(this.module);
CoreCourseHelper.loadModuleOfflineCompletion(this.courseId, this.module);
this.completionObserver = CoreEvents.on(CoreEvents.COMPLETION_MODULE_VIEWED, async (data) => {
@ -428,7 +428,7 @@ export class CoreCourseModuleMainResourceComponent implements OnInit, OnDestroy,
protected async fetchModule(): Promise<void> {
const module = await CoreCourse.getModule(this.module.id, this.courseId);
CoreCourseHelper.calculateModuleCompletionData(module, this.courseId);
CoreCourseHelper.calculateModuleCompletionData(module);
await CoreCourseHelper.loadModuleOfflineCompletion(this.courseId, module);

View File

@ -33,6 +33,7 @@ import { CoreDynamicComponent } from '@components/dynamic-component/dynamic-comp
import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
import {
CoreCourse,
CoreCourseModuleCompletionStatus,
CoreCourseProvider,
} from '@features/course/services/course';
import {
@ -643,7 +644,7 @@ export class CoreCourseFormatComponent implements OnInit, OnChanges, OnDestroy {
const moduleProgressPercent = 100 / (completionModules || 1);
// Use min/max here to avoid floating point rounding errors over/under-flowing the progress bar.
if (completionData.state === CoreCourseProvider.COMPLETION_COMPLETE) {
if (completionData.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE) {
this.course.progress = Math.min(100, this.course.progress + moduleProgressPercent);
} else {
this.course.progress = Math.max(0, this.course.progress - moduleProgressPercent);

View File

@ -15,7 +15,7 @@
import { Component } from '@angular/core';
import { CoreUser } from '@features/user/services/user';
import { CoreCourseProvider } from '@features/course/services/course';
import { CoreCourseModuleCompletionStatus, CoreCourseModuleCompletionTracking } from '@features/course/services/course';
import { CoreFilterHelper } from '@features/filter/services/filter-helper';
import { Translate } from '@singletons';
import { CoreCourseModuleCompletionBaseComponent } from '@features/course/classes/module-completion';
@ -52,28 +52,28 @@ export class CoreCourseModuleCompletionLegacyComponent extends CoreCourseModuleC
let langKey: string | undefined;
let image: string | undefined;
if (this.completion.tracking === CoreCourseProvider.COMPLETION_TRACKING_MANUAL &&
this.completion.state === CoreCourseProvider.COMPLETION_INCOMPLETE) {
if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_MANUAL &&
this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE) {
image = 'completion-manual-n';
langKey = 'core.completion-alt-manual-n';
} else if (this.completion.tracking === CoreCourseProvider.COMPLETION_TRACKING_MANUAL &&
this.completion.state === CoreCourseProvider.COMPLETION_COMPLETE) {
} else if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_MANUAL &&
this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE) {
image = 'completion-manual-y';
langKey = 'core.completion-alt-manual-y';
} else if (this.completion.tracking === CoreCourseProvider.COMPLETION_TRACKING_AUTOMATIC &&
this.completion.state === CoreCourseProvider.COMPLETION_INCOMPLETE) {
} else if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_AUTOMATIC &&
this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE) {
image = 'completion-auto-n';
langKey = 'core.completion-alt-auto-n';
} else if (this.completion.tracking === CoreCourseProvider.COMPLETION_TRACKING_AUTOMATIC &&
this.completion.state === CoreCourseProvider.COMPLETION_COMPLETE) {
} else if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_AUTOMATIC &&
this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE) {
image = 'completion-auto-y';
langKey = 'core.completion-alt-auto-y';
} else if (this.completion.tracking === CoreCourseProvider.COMPLETION_TRACKING_AUTOMATIC &&
this.completion.state === CoreCourseProvider.COMPLETION_COMPLETE_PASS) {
} else if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_AUTOMATIC &&
this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_PASS) {
image = 'completion-auto-pass';
langKey = 'core.completion-alt-auto-pass';
} else if (this.completion.tracking === CoreCourseProvider.COMPLETION_TRACKING_AUTOMATIC &&
this.completion.state === CoreCourseProvider.COMPLETION_COMPLETE_FAIL) {
} else if (this.completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_AUTOMATIC &&
this.completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_FAIL) {
image = 'completion-auto-fail';
langKey = 'core.completion-alt-auto-fail';
}

View File

@ -15,7 +15,7 @@
import { Component, Input } from '@angular/core';
import { CoreCourseModuleCompletionBaseComponent } from '@features/course/classes/module-completion';
import { CoreCourseModuleWSRuleDetails, CoreCourseProvider } from '@features/course/services/course';
import { CoreCourseModuleCompletionStatus, CoreCourseModuleWSRuleDetails } from '@features/course/services/course';
import { CoreUser } from '@features/user/services/user';
import { Translate } from '@singletons';
@ -51,10 +51,10 @@ export class CoreCourseModuleCompletionComponent extends CoreCourseModuleComplet
// Format rules.
this.details = await Promise.all(this.completion.details.map(async (rule: CompletionRule) => {
rule.statuscomplete = rule.rulevalue.status == CoreCourseProvider.COMPLETION_COMPLETE ||
rule.rulevalue.status == CoreCourseProvider.COMPLETION_COMPLETE_PASS;
rule.statuscompletefail = rule.rulevalue.status == CoreCourseProvider.COMPLETION_COMPLETE_FAIL;
rule.statusincomplete = rule.rulevalue.status == CoreCourseProvider.COMPLETION_INCOMPLETE;
rule.statuscomplete = rule.rulevalue.status == CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE ||
rule.rulevalue.status == CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_PASS;
rule.statuscompletefail = rule.rulevalue.status == CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_FAIL;
rule.statusincomplete = rule.rulevalue.status == CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE;
rule.accessibleDescription = null;
if (this.completion!.overrideby) {

View File

@ -15,7 +15,11 @@
import { Component, Input, OnInit } from '@angular/core';
import { CoreCourseSection } from '@features/course/services/course-helper';
import { CoreCourseProvider } from '@features/course/services/course';
import {
CoreCourseModuleCompletionStatus,
CoreCourseModuleCompletionTracking,
CoreCourseProvider,
} from '@features/course/services/course';
import { CoreCourseAnyCourseData } from '@features/courses/services/courses';
import { CoreUtils } from '@services/utils/utils';
import { ModalController } from '@singletons';
@ -56,14 +60,14 @@ export class CoreCourseSectionSelectorComponent implements OnInit {
let complete = 0;
let total = 0;
section.modules.forEach((module) => {
if (!module.uservisible || module.completiondata === undefined || module.completiondata.tracking === undefined ||
module.completiondata.tracking <= CoreCourseProvider.COMPLETION_TRACKING_NONE) {
if (!module.uservisible || module.completiondata === undefined ||
module.completiondata.tracking == CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_NONE) {
return;
}
total++;
if (module.completiondata.state == CoreCourseProvider.COMPLETION_COMPLETE ||
module.completiondata.state == CoreCourseProvider.COMPLETION_COMPLETE_PASS) {
if (module.completiondata.state == CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE ||
module.completiondata.state == CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_PASS) {
complete++;
}
});

View File

@ -73,7 +73,7 @@ export class CoreCourseModulePreviewPage implements OnInit {
this.module = await CoreCourse.getModule(this.module.id, this.courseId);
}
CoreCourseHelper.calculateModuleCompletionData(this.module, this.courseId);
CoreCourseHelper.calculateModuleCompletionData(this.module);
await CoreCourseHelper.loadModuleOfflineCompletion(this.courseId, this.module);

View File

@ -25,6 +25,8 @@ import {
CoreCourseWSModule,
CoreCourseProvider,
CoreCourseWSSection,
CoreCourseModuleCompletionTracking,
CoreCourseModuleCompletionStatus,
} from './course';
import { CoreConstants } from '@/core/constants';
import { CoreLogger } from '@singletons/logger';
@ -200,8 +202,8 @@ export class CoreCourseHelperProvider {
);
if (module.completiondata) {
this.calculateModuleCompletionData(module, courseId, courseName);
} else if (completionStatus && typeof completionStatus[module.id] != 'undefined') {
this.calculateModuleCompletionData(module, undefined, courseName);
} else if (completionStatus && completionStatus[module.id] !== undefined) {
// Should not happen on > 3.6. Check if activity has completions and if it's marked.
const activityStatus = completionStatus[module.id];
@ -229,18 +231,18 @@ export class CoreCourseHelperProvider {
* Calculate completion data of a module.
*
* @param module Module.
* @param courseId Course ID of the module.
* @param courseId Not used since 4.0
* @param courseName Course name.
*/
calculateModuleCompletionData(module: CoreCourseModule, courseId: number, courseName?: string): void {
calculateModuleCompletionData(module: CoreCourseModule, courseId?: number, courseName?: string): void {
if (!module.completiondata || !module.completion) {
return;
}
module.completiondata.courseId = courseId;
module.completiondata.courseName = courseName;
module.completiondata.courseId = module.course;
module.completiondata.tracking = module.completion;
module.completiondata.cmid = module.id;
module.completiondata.courseName = courseName;
}
/**
@ -2069,7 +2071,8 @@ export class CoreCourseHelperProvider {
return;
}
if (typeof completion.cmid == 'undefined' || completion.tracking !== 1) {
if (completion.cmid === undefined ||
completion.tracking !== CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_MANUAL) {
return;
}
@ -2077,14 +2080,15 @@ export class CoreCourseHelperProvider {
event?.stopPropagation();
const modal = await CoreDomUtils.showModalLoading();
completion.state = completion.state === 1 ? 0 : 1;
completion.state = completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE
? CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE
: CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE;
try {
const response = await CoreCourse.markCompletedManually(
completion.cmid,
completion.state === 1,
completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE,
completion.courseId!,
completion.courseName,
);
if (response.offline) {
@ -2093,7 +2097,11 @@ export class CoreCourseHelperProvider {
return response;
} catch (error) {
completion.state = completion.state === 1 ? 0 : 1;
// Restore previous state.
completion.state = completion.state === CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE
? CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE
: CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE;
CoreDomUtils.showErrorModalDefault(error, 'core.errorchangecompletion', true);
} finally {
modal.dismiss();
@ -2139,7 +2147,7 @@ export type CoreCourseModule = Omit<CoreCourseWSModule, 'completiondata'> & {
export type CoreCourseModuleCompletionData = CoreCourseModuleWSCompletionData & {
courseId?: number;
courseName?: string;
tracking?: number;
tracking?: CoreCourseModuleCompletionTracking;
cmid?: number;
offline?: boolean;
};

View File

@ -61,6 +61,25 @@ declare module '@singletons/events' {
}
/**
* Completion status valid values.
*/
export enum CoreCourseModuleCompletionStatus {
COMPLETION_INCOMPLETE = 0,
COMPLETION_COMPLETE = 1,
COMPLETION_COMPLETE_PASS = 2,
COMPLETION_COMPLETE_FAIL = 3,
}
/**
* Completion tracking valid values.
*/
export enum CoreCourseModuleCompletionTracking {
COMPLETION_TRACKING_NONE = 0,
COMPLETION_TRACKING_MANUAL = 1,
COMPLETION_TRACKING_AUTOMATIC = 2,
}
/**
* Service that provides some features regarding a course.
*/
@ -73,13 +92,34 @@ export class CoreCourseProvider {
static readonly ACCESS_DEFAULT = 'courses_access_default';
static readonly ALL_COURSES_CLEARED = -1;
/**
* @deprecated since 4.0, use CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_NONE instead.
*/
static readonly COMPLETION_TRACKING_NONE = 0;
/**
* @deprecated since 4.0, use CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_MANUAL instead.
*/
static readonly COMPLETION_TRACKING_MANUAL = 1;
/**
* @deprecated since 4.0, use CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_AUTOMATIC instead.
*/
static readonly COMPLETION_TRACKING_AUTOMATIC = 2;
/**
* @deprecated since 4.0, use CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE instead.
*/
static readonly COMPLETION_INCOMPLETE = 0;
/**
* @deprecated since 4.0, use CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE instead.
*/
static readonly COMPLETION_COMPLETE = 1;
/**
* @deprecated since 4.0, use CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_PASS instead.
*/
static readonly COMPLETION_COMPLETE_PASS = 2;
/**
* @deprecated since 4.0, use CoreCourseModuleCompletionStatus.COMPLETION_COMPLETE_FAIL instead.
*/
static readonly COMPLETION_COMPLETE_FAIL = 3;
static readonly COMPONENT = 'CoreCourse';
@ -151,7 +191,8 @@ export class CoreCourseProvider {
* @param completion Completion status of the module.
*/
checkModuleCompletion(courseId: number, completion?: CoreCourseModuleCompletionData): void {
if (completion && completion.tracking === CoreCourseProvider.COMPLETION_TRACKING_AUTOMATIC && completion.state === 0) {
if (completion && completion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_AUTOMATIC &&
completion.state === CoreCourseModuleCompletionStatus.COMPLETION_INCOMPLETE) {
this.invalidateSections(courseId).finally(() => {
CoreEvents.trigger(CoreEvents.COMPLETION_MODULE_VIEWED, {
courseId: courseId,
@ -256,7 +297,7 @@ export class CoreCourseProvider {
const onlineCompletion = completionStatus[offlineCompletion.cmid];
// If the activity uses manual completion, override the value with the offline one.
if (onlineCompletion.tracking === 1) {
if (onlineCompletion.tracking === CoreCourseModuleCompletionTracking.COMPLETION_TRACKING_MANUAL) {
onlineCompletion.state = offlineCompletion.completed;
onlineCompletion.offline = true;
}
@ -986,7 +1027,7 @@ export class CoreCourseProvider {
CoreCourseOffline.markCompletedManually(cmId, completed, courseId, courseName, siteId);
// The offline function requires a courseId and it could be missing because it's a calculated field.
if (!CoreApp.isOnline() && courseId) {
if (!CoreApp.isOnline()) {
// App is offline, store the action.
return storeOffline();
}
@ -996,18 +1037,14 @@ export class CoreCourseProvider {
const result = await this.markCompletedManuallyOnline(cmId, completed, siteId);
// Data sent to server, if there is some offline data delete it now.
try {
await CoreCourseOffline.deleteManualCompletion(cmId, siteId);
} catch {
// Ignore errors, shouldn't happen.
}
await CoreUtils.ignoreErrors(CoreCourseOffline.deleteManualCompletion(cmId, siteId));
// Invalidate module now, completion has changed.
await this.invalidateModule(cmId, siteId);
return result;
} catch (error) {
if (CoreUtils.isWebServiceError(error) || !courseId) {
if (CoreUtils.isWebServiceError(error)) {
// The WebService has thrown an error, this means that responses cannot be submitted.
throw error;
} else {
@ -1346,7 +1383,7 @@ export type CoreCourseCompletionActivityStatus = {
instance: number; // Instance ID.
state: number; // Completion state value: 0 means incomplete, 1 complete, 2 complete pass, 3 complete fail.
timecompleted: number; // Timestamp for completed activity.
tracking: number; // Type of tracking: 0 means none, 1 manual, 2 automatic.
tracking: CoreCourseModuleCompletionTracking; // Type of tracking: 0 means none, 1 manual, 2 automatic.
overrideby?: number | null; // The user id who has overriden the status, or null.
valueused?: boolean; // Whether the completion status affects the availability of another activity.
hascompletion?: boolean; // @since 3.11. Whether this activity module has completion enabled.
@ -1497,7 +1534,7 @@ export type CoreCourseWSModule = {
afterlink?: string; // After link info to be displayed.
customdata?: string; // Custom data (JSON encoded).
noviewlink?: boolean; // Whether the module has no view page.
completion?: number; // Type of completion tracking: 0 means none, 1 manual, 2 automatic.
completion?: CoreCourseModuleCompletionTracking; // Type of completion tracking: 0 means none, 1 manual, 2 automatic.
completiondata?: CoreCourseModuleWSCompletionData; // Module completion data.
contents?: CoreCourseModuleContentFile[];
dates?: {
@ -1517,7 +1554,7 @@ export type CoreCourseWSModule = {
* Module completion data.
*/
export type CoreCourseModuleWSCompletionData = {
state: number; // Completion state value: 0 means incomplete, 1 complete, 2 complete pass, 3 complete fail.
state: CoreCourseModuleCompletionStatus; // Completion state value.
timecompleted: number; // Timestamp for completion status.
overrideby: number | null; // The user id who has overriden the status.
valueused?: boolean; // Whether the completion status affects the availability of another activity.