MOBILE-4550 quiz: Move quiz service constants to constants file

main
Dani Palou 2024-03-21 14:02:27 +01:00
parent df3b328bac
commit 17bd64a5e0
13 changed files with 142 additions and 121 deletions

View File

@ -15,8 +15,9 @@
import { Injectable } from '@angular/core';
import { AddonModQuizAccessRuleHandler } from '@addons/mod/quiz/services/access-rules-delegate';
import { AddonModQuizAttemptWSData, AddonModQuizProvider } from '@addons/mod/quiz/services/quiz';
import { AddonModQuizAttemptWSData } from '@addons/mod/quiz/services/quiz';
import { makeSingleton } from '@singletons';
import { ADDON_MOD_QUIZ_SHOW_TIME_BEFORE_DEADLINE } from '@addons/mod/quiz/constants';
/**
* Handler to support open/close date access rule.
@ -50,8 +51,8 @@ export class AddonModQuizAccessOpenCloseDateHandlerService implements AddonModQu
return false;
}
// Show the time left only if it's less than QUIZ_SHOW_TIME_BEFORE_DEADLINE.
if (timeNow > endTime - AddonModQuizProvider.QUIZ_SHOW_TIME_BEFORE_DEADLINE) {
// Show the time left only if it's less than ADDON_MOD_QUIZ_SHOW_TIME_BEFORE_DEADLINE.
if (timeNow > endTime - ADDON_MOD_QUIZ_SHOW_TIME_BEFORE_DEADLINE) {
return true;
}

View File

@ -36,7 +36,6 @@ import {
AddonModQuizGetAttemptAccessInformationWSResponse,
AddonModQuizGetQuizAccessInformationWSResponse,
AddonModQuizGetUserBestGradeWSResponse,
AddonModQuizProvider,
} from '../../services/quiz';
import { AddonModQuizAttempt, AddonModQuizHelper, AddonModQuizQuizData } from '../../services/quiz-helper';
import {
@ -45,6 +44,8 @@ import {
AddonModQuizSyncProvider,
AddonModQuizSyncResult,
} from '../../services/quiz-sync';
import { ADDON_MOD_QUIZ_ATTEMPT_FINISHED_EVENT, ADDON_MOD_QUIZ_COMPONENT, AddonModQuizGradeMethods } from '../../constants';
import { QuestionDisplayOptionsMarks } from '@features/question/constants';
/**
* Component that displays a quiz entry page.
@ -56,7 +57,7 @@ import {
})
export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComponent implements OnInit, OnDestroy {
component = AddonModQuizProvider.COMPONENT;
component = ADDON_MOD_QUIZ_COMPONENT;
pluginName = 'quiz';
quiz?: AddonModQuizQuizData; // The quiz.
now?: number; // Current time.
@ -110,7 +111,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
// Listen for attempt finished events.
this.finishedObserver = CoreEvents.on(
AddonModQuizProvider.ATTEMPT_FINISHED_EVENT,
ADDON_MOD_QUIZ_ATTEMPT_FINISHED_EVENT,
(data) => {
// Go to review attempt if an attempt in this quiz was finished and synced.
if (this.quiz && data.quizId == this.quiz.id) {
@ -609,12 +610,12 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
// Calculate data to construct the header of the attempts table.
AddonModQuizHelper.setQuizCalculatedData(quiz, this.options);
this.overallStats = !!lastFinished && this.options.alloptions.marks >= AddonModQuizProvider.QUESTION_OPTIONS_MARK_AND_MAX;
this.overallStats = !!lastFinished && this.options.alloptions.marks >= QuestionDisplayOptionsMarks.MARK_AND_MAX;
// Calculate data to show for each attempt.
const formattedAttempts = await Promise.all(attempts.map((attempt, index) => {
// Highlight the highest grade if appropriate.
const shouldHighlight = this.overallStats && quiz.grademethod == AddonModQuizProvider.GRADEHIGHEST &&
const shouldHighlight = this.overallStats && quiz.grademethod === AddonModQuizGradeMethods.HIGHEST_GRADE &&
attempts.length > 1;
const isLast = index == attempts.length - 1;

View File

@ -12,4 +12,30 @@
// See the License for the specific language governing permissions and
// limitations under the License.
export const ADDON_MOD_QUIZ_COMPONENT = 'mmaModQuiz';
export const ADDON_MOD_QUIZ_FEATURE_NAME = 'CoreCourseModuleDelegate_AddonModQuiz';
export const ADDON_MOD_QUIZ_ATTEMPT_FINISHED_EVENT = 'addon_mod_quiz_attempt_finished';
export const ADDON_MOD_QUIZ_SHOW_TIME_BEFORE_DEADLINE = 3600;
/**
* Possible grade methods for a quiz.
*/
export const enum AddonModQuizGradeMethods {
HIGHEST_GRADE = 1,
AVERAGE_GRADE = 2,
FIRST_ATTEMPT = 3,
LAST_ATTEMPT = 4,
}
/**
* Possible states for an attempt.
*/
export const enum AddonModQuizAttemptStates {
IN_PROGRESS = 'inprogress',
OVERDUE = 'overdue',
FINISHED = 'finished',
ABANDONED = 'abandoned',
}

View File

@ -23,9 +23,9 @@ import {
AddonModQuiz,
AddonModQuizAttemptWSData,
AddonModQuizGetQuizAccessInformationWSResponse,
AddonModQuizProvider,
} from '../../services/quiz';
import { AddonModQuizAttempt, AddonModQuizHelper, AddonModQuizQuizData } from '../../services/quiz-helper';
import { ADDON_MOD_QUIZ_COMPONENT } from '../../constants';
/**
* Page that displays some summary data about an attempt.
@ -39,7 +39,7 @@ export class AddonModQuizAttemptPage implements OnInit {
courseId!: number; // The course ID the quiz belongs to.
quiz?: AddonModQuizQuizData; // The quiz the attempt belongs to.
attempt?: AddonModQuizAttempt; // The attempt to view.
component = AddonModQuizProvider.COMPONENT; // Component to link the files to.
component = ADDON_MOD_QUIZ_COMPONENT; // Component to link the files to.
componentId?: number; // Component ID to use in conjunction with the component.
loaded = false; // Whether data has been loaded.
feedback?: string; // Attempt feedback.

View File

@ -38,7 +38,6 @@ import {
AddonModQuizAttemptWSData,
AddonModQuizGetAttemptAccessInformationWSResponse,
AddonModQuizGetQuizAccessInformationWSResponse,
AddonModQuizProvider,
AddonModQuizQuizWSData,
} from '../../services/quiz';
import { AddonModQuizAttempt, AddonModQuizHelper } from '../../services/quiz-helper';
@ -50,6 +49,7 @@ import { CoreTime } from '@singletons/time';
import { CoreDirectivesRegistry } from '@singletons/directives-registry';
import { CoreWSError } from '@classes/errors/wserror';
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
import { ADDON_MOD_QUIZ_ATTEMPT_FINISHED_EVENT, AddonModQuizAttemptStates, ADDON_MOD_QUIZ_COMPONENT } from '../../constants';
/**
* Page that allows attempting a quiz.
@ -68,7 +68,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
quiz?: AddonModQuizQuizWSData; // The quiz the attempt belongs to.
attempt?: AddonModQuizAttempt; // The attempt being attempted.
moduleUrl?: string; // URL to the module in the site.
component = AddonModQuizProvider.COMPONENT; // Component to link the files to.
component = ADDON_MOD_QUIZ_COMPONENT; // Component to link the files to.
loaded = false; // Whether data has been loaded.
quizAborted = false; // Whether the quiz was aborted due to an error.
offline = false; // Whether the quiz is being attempted in offline mode.
@ -146,7 +146,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
if (this.quiz) {
// Unblock the quiz so it can be synced.
CoreSync.unblockOperation(AddonModQuizProvider.COMPONENT, this.quiz.id);
CoreSync.unblockOperation(ADDON_MOD_QUIZ_COMPONENT, this.quiz.id);
}
}
@ -263,7 +263,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
return;
}
if (page != -1 && (this.attempt.state == AddonModQuizProvider.ATTEMPT_OVERDUE || this.attempt.finishedOffline)) {
if (page != -1 && (this.attempt.state === AddonModQuizAttemptStates.OVERDUE || this.attempt.finishedOffline)) {
// We can't load a page if overdue or the local attempt is finished.
return;
} else if (page == this.attempt.currentpage && !this.showSummary && slot !== undefined) {
@ -341,7 +341,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
this.quiz = await AddonModQuiz.getQuiz(this.courseId, this.cmId);
// Block the quiz so it cannot be synced.
CoreSync.blockOperation(AddonModQuizProvider.COMPONENT, this.quiz.id);
CoreSync.blockOperation(ADDON_MOD_QUIZ_COMPONENT, this.quiz.id);
// Wait for any ongoing sync to finish. We won't sync a quiz while it's being played.
await AddonModQuizSync.waitForSync(this.quiz.id);
@ -408,7 +408,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
try {
// Show confirm if the user clicked the finish button and the quiz is in progress.
if (!timeUp && this.attempt.state == AddonModQuizProvider.ATTEMPT_IN_PROGRESS) {
if (!timeUp && this.attempt.state === AddonModQuizAttemptStates.IN_PROGRESS) {
let message = Translate.instant('addon.mod_quiz.confirmclose');
const unansweredCount = this.summaryQuestions
@ -444,7 +444,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
await this.processAttempt(userFinish, timeUp);
// Trigger an event to notify the attempt was finished.
CoreEvents.trigger(AddonModQuizProvider.ATTEMPT_FINISHED_EVENT, {
CoreEvents.trigger(ADDON_MOD_QUIZ_ATTEMPT_FINISHED_EVENT, {
quizId: this.quiz.id,
attemptId: this.attempt.id,
synced: !this.offline,
@ -679,7 +679,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
});
this.showSummary = true;
this.canReturn = this.attempt.state == AddonModQuizProvider.ATTEMPT_IN_PROGRESS && !this.attempt.finishedOffline;
this.canReturn = this.attempt.state === AddonModQuizAttemptStates.IN_PROGRESS && !this.attempt.finishedOffline;
this.preventSubmitMessages = AddonModQuiz.getPreventSubmitMessages(this.summaryQuestions);
this.dueDateWarning = AddonModQuiz.getAttemptDueDateWarning(this.quiz, this.attempt);
@ -904,7 +904,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
await this.loadNavigation();
if (this.attempt.state != AddonModQuizProvider.ATTEMPT_OVERDUE && !this.attempt.finishedOffline) {
if (this.attempt.state !== AddonModQuizAttemptStates.OVERDUE && !this.attempt.finishedOffline) {
// Attempt not overdue and not finished in offline, load page.
await this.loadPage(this.attempt.currentpage ?? 0);

View File

@ -32,12 +32,13 @@ import {
AddonModQuizAttemptWSData,
AddonModQuizCombinedReviewOptions,
AddonModQuizGetAttemptReviewResponse,
AddonModQuizProvider,
AddonModQuizQuizWSData,
AddonModQuizWSAdditionalData,
} from '../../services/quiz';
import { AddonModQuizHelper } from '../../services/quiz-helper';
import { CoreAnalytics, CoreAnalyticsEventType } from '@services/analytics';
import { AddonModQuizAttemptStates, ADDON_MOD_QUIZ_COMPONENT } from '../../constants';
import { QuestionDisplayOptionsMarks } from '@features/question/constants';
/**
* Page that allows reviewing a quiz attempt.
@ -52,7 +53,7 @@ export class AddonModQuizReviewPage implements OnInit {
@ViewChild(IonContent) content?: IonContent;
attempt?: AddonModQuizAttemptWSData; // The attempt being reviewed.
component = AddonModQuizProvider.COMPONENT; // Component to link the files to.
component = ADDON_MOD_QUIZ_COMPONENT; // Component to link the files to.
showAll = false; // Whether to view all questions in the same page.
numPages = 1; // Number of pages.
showCompleted = false; // Whether to show completed time.
@ -265,7 +266,7 @@ export class AddonModQuizReviewPage implements OnInit {
this.readableState = AddonModQuiz.getAttemptReadableStateName(this.attempt.state ?? '');
if (this.attempt.state != AddonModQuizProvider.ATTEMPT_FINISHED) {
if (this.attempt.state !== AddonModQuizAttemptStates.FINISHED) {
return;
}
@ -299,7 +300,7 @@ export class AddonModQuizReviewPage implements OnInit {
}
// Treat grade.
if (this.options && this.options.someoptions.marks >= AddonModQuizProvider.QUESTION_OPTIONS_MARK_AND_MAX &&
if (this.options && this.options.someoptions.marks >= QuestionDisplayOptionsMarks.MARK_AND_MAX &&
AddonModQuiz.quizHasGrades(this.quiz)) {
if (data.grade === null || data.grade === undefined) {

View File

@ -33,7 +33,7 @@ import { AddonModQuizPrefetchHandler } from './services/handlers/prefetch';
import { AddonModQuizPushClickHandler } from './services/handlers/push-click';
import { AddonModQuizReviewLinkHandler } from './services/handlers/review-link';
import { AddonModQuizSyncCronHandler } from './services/handlers/sync-cron';
import { AddonModQuizProvider } from './services/quiz';
import { ADDON_MOD_QUIZ_COMPONENT } from './constants';
/**
* Get mod Quiz services.
@ -98,7 +98,7 @@ const routes: Routes = [
CorePushNotificationsDelegate.registerClickHandler(AddonModQuizPushClickHandler.instance);
CoreCronDelegate.register(AddonModQuizSyncCronHandler.instance);
CoreCourseHelper.registerModuleReminderClick(AddonModQuizProvider.COMPONENT);
CoreCourseHelper.registerModuleReminderClick(ADDON_MOD_QUIZ_COMPONENT);
},
},
],

View File

@ -32,11 +32,11 @@ import {
AddonModQuiz,
AddonModQuizAttemptWSData,
AddonModQuizGetQuizAccessInformationWSResponse,
AddonModQuizProvider,
AddonModQuizQuizWSData,
} from '../quiz';
import { AddonModQuizHelper } from '../quiz-helper';
import { AddonModQuizSync, AddonModQuizSyncResult } from '../quiz-sync';
import { AddonModQuizAttemptStates, ADDON_MOD_QUIZ_COMPONENT } from '../../constants';
/**
* Handler to prefetch quizzes.
@ -46,7 +46,7 @@ export class AddonModQuizPrefetchHandlerService extends CoreCourseActivityPrefet
name = 'AddonModQuiz';
modName = 'quiz';
component = AddonModQuizProvider.COMPONENT;
component = ADDON_MOD_QUIZ_COMPONENT;
updatesNames = /^configuration$|^.*files$|^grades$|^gradeitems$|^questions$|^attempts$/;
/**
@ -321,7 +321,7 @@ export class AddonModQuizPrefetchHandlerService extends CoreCourseActivityPrefet
AddonModQuiz.getUserAttempts(quiz.id, modOptions),
AddonModQuiz.getAttemptAccessInformation(quiz.id, 0, modOptions),
AddonModQuiz.getQuizRequiredQtypes(quiz.id, modOptions),
CoreFilepool.addFilesToQueue(siteId, introFiles, AddonModQuizProvider.COMPONENT, module.id),
CoreFilepool.addFilesToQueue(siteId, introFiles, ADDON_MOD_QUIZ_COMPONENT, module.id),
]);
// Check if we need to start a new attempt.
@ -353,17 +353,17 @@ export class AddonModQuizPrefetchHandlerService extends CoreCourseActivityPrefet
const attemptFiles = await this.getAttemptsFeedbackFiles(quiz, attempts, siteId);
return CoreFilepool.addFilesToQueue(siteId, attemptFiles, AddonModQuizProvider.COMPONENT, module.id);
return CoreFilepool.addFilesToQueue(siteId, attemptFiles, ADDON_MOD_QUIZ_COMPONENT, module.id);
}));
// Update the download time to prevent detecting the new attempt as an update.
promises.push(CoreUtils.ignoreErrors(
CoreFilepool.updatePackageDownloadTime(siteId, AddonModQuizProvider.COMPONENT, module.id),
CoreFilepool.updatePackageDownloadTime(siteId, ADDON_MOD_QUIZ_COMPONENT, module.id),
));
} else {
// Use the already fetched attempts.
promises.push(this.getAttemptsFeedbackFiles(quiz, attempts, siteId).then((attemptFiles) =>
CoreFilepool.addFilesToQueue(siteId, attemptFiles, AddonModQuizProvider.COMPONENT, module.id)));
CoreFilepool.addFilesToQueue(siteId, attemptFiles, ADDON_MOD_QUIZ_COMPONENT, module.id)));
}
// Fetch attempt related data.
@ -444,7 +444,7 @@ export class AddonModQuizPrefetchHandlerService extends CoreCourseActivityPrefet
promises.push(AddonModQuiz.getAttemptAccessInformation(quiz.id, attempt.id, modOptions));
promises.push(AddonModQuiz.getAttemptSummary(attempt.id, preflightData, modOptions));
if (attempt.state == AddonModQuizProvider.ATTEMPT_IN_PROGRESS) {
if (attempt.state === AddonModQuizAttemptStates.IN_PROGRESS) {
// Get data for each page.
promises = promises.concat(pages.map(async (page) => {
if (isSequential && typeof attempt.currentpage === 'number' && page < attempt.currentpage) {

View File

@ -30,10 +30,11 @@ import {
AddonModQuizAttemptWSData,
AddonModQuizCombinedReviewOptions,
AddonModQuizGetQuizAccessInformationWSResponse,
AddonModQuizProvider,
AddonModQuizQuizWSData,
} from './quiz';
import { AddonModQuizOffline } from './quiz-offline';
import { AddonModQuizAttemptStates } from '../constants';
import { QuestionDisplayOptionsMarks } from '@features/question/constants';
/**
* Helper service that provides some features for quiz.
@ -291,7 +292,7 @@ export class AddonModQuizHelperProvider {
// Highlight the highest grade if appropriate.
formattedAttempt.highlightGrade = !!(highlight && !attempt.preview &&
attempt.state == AddonModQuizProvider.ATTEMPT_FINISHED && formattedAttempt.readableGrade == bestGrade);
attempt.state === AddonModQuizAttemptStates.FINISHED && formattedAttempt.readableGrade == bestGrade);
} else {
formattedAttempt.readableGrade = '';
}
@ -317,7 +318,7 @@ export class AddonModQuizHelperProvider {
formattedQuiz.gradeFormatted = AddonModQuiz.formatGrade(quiz.grade, quiz.decimalpoints);
formattedQuiz.showAttemptColumn = quiz.attempts != 1;
formattedQuiz.showGradeColumn = options.someoptions.marks >= AddonModQuizProvider.QUESTION_OPTIONS_MARK_AND_MAX &&
formattedQuiz.showGradeColumn = options.someoptions.marks >= QuestionDisplayOptionsMarks.MARK_AND_MAX &&
AddonModQuiz.quizHasGrades(quiz);
formattedQuiz.showMarkColumn = formattedQuiz.showGradeColumn && quiz.grade != quiz.sumgrades;
formattedQuiz.showFeedbackColumn = !!quiz.hasfeedback && !!options.alloptions.overallfeedback;
@ -356,7 +357,7 @@ export class AddonModQuizHelperProvider {
try {
if (attempt) {
if (attempt.state != AddonModQuizProvider.ATTEMPT_OVERDUE && !attempt.finishedOffline) {
if (attempt.state !== AddonModQuizAttemptStates.OVERDUE && !attempt.finishedOffline) {
// We're continuing an attempt. Call getAttemptData to validate the preflight data.
await AddonModQuiz.getAttemptData(attempt.id, attempt.currentpage ?? 0, preflightData, modOptions);

View File

@ -23,7 +23,8 @@ import { CoreUtils } from '@services/utils/utils';
import { makeSingleton, Translate } from '@singletons';
import { CoreLogger } from '@singletons/logger';
import { AddonModQuizAttemptDBRecord, ATTEMPTS_TABLE_NAME } from './database/quiz';
import { AddonModQuizAttemptWSData, AddonModQuizProvider, AddonModQuizQuizWSData } from './quiz';
import { AddonModQuizAttemptWSData, AddonModQuizQuizWSData } from './quiz';
import { ADDON_MOD_QUIZ_COMPONENT } from '../constants';
/**
* Service to handle offline quiz.
@ -103,7 +104,7 @@ export class AddonModQuizOfflineProvider {
* @returns Promise resolved with the answers.
*/
getAttemptAnswers(attemptId: number, siteId?: string): Promise<CoreQuestionAnswerDBRecord[]> {
return CoreQuestion.getAttemptAnswers(AddonModQuizProvider.COMPONENT, attemptId, siteId);
return CoreQuestion.getAttemptAnswers(ADDON_MOD_QUIZ_COMPONENT, attemptId, siteId);
}
/**
@ -149,7 +150,7 @@ export class AddonModQuizOfflineProvider {
await Promise.all(questions.map(async (question) => {
const dbQuestion = await CoreUtils.ignoreErrors(
CoreQuestion.getQuestion(AddonModQuizProvider.COMPONENT, attemptId, question.slot, siteId),
CoreQuestion.getQuestion(ADDON_MOD_QUIZ_COMPONENT, attemptId, question.slot, siteId),
);
if (!dbQuestion) {
@ -230,8 +231,8 @@ export class AddonModQuizOfflineProvider {
const db = await CoreSites.getSiteDb(siteId);
await Promise.all([
CoreQuestion.removeAttemptAnswers(AddonModQuizProvider.COMPONENT, attemptId, siteId),
CoreQuestion.removeAttemptQuestions(AddonModQuizProvider.COMPONENT, attemptId, siteId),
CoreQuestion.removeAttemptAnswers(ADDON_MOD_QUIZ_COMPONENT, attemptId, siteId),
CoreQuestion.removeAttemptQuestions(ADDON_MOD_QUIZ_COMPONENT, attemptId, siteId),
db.deleteRecords(ATTEMPTS_TABLE_NAME, { id: attemptId }),
]);
}
@ -248,8 +249,8 @@ export class AddonModQuizOfflineProvider {
siteId = siteId || CoreSites.getCurrentSiteId();
await Promise.all([
CoreQuestion.removeQuestion(AddonModQuizProvider.COMPONENT, attemptId, slot, siteId),
CoreQuestion.removeQuestionAnswers(AddonModQuizProvider.COMPONENT, attemptId, slot, siteId),
CoreQuestion.removeQuestion(ADDON_MOD_QUIZ_COMPONENT, attemptId, slot, siteId),
CoreQuestion.removeQuestionAnswers(ADDON_MOD_QUIZ_COMPONENT, attemptId, slot, siteId),
]);
}
@ -299,7 +300,7 @@ export class AddonModQuizOfflineProvider {
const state = await CoreQuestionBehaviourDelegate.determineNewState(
quiz.preferredbehaviour ?? '',
AddonModQuizProvider.COMPONENT,
ADDON_MOD_QUIZ_COMPONENT,
attempt.id,
question,
quiz.coursemodule,
@ -312,12 +313,12 @@ export class AddonModQuizOfflineProvider {
}
// Delete previously stored answers for this question.
await CoreQuestion.removeQuestionAnswers(AddonModQuizProvider.COMPONENT, attempt.id, question.slot, siteId);
await CoreQuestion.removeQuestionAnswers(ADDON_MOD_QUIZ_COMPONENT, attempt.id, question.slot, siteId);
}));
// Now save the answers.
await CoreQuestion.saveAnswers(
AddonModQuizProvider.COMPONENT,
ADDON_MOD_QUIZ_COMPONENT,
quiz.id,
attempt.id,
attempt.userid ?? CoreSites.getCurrentSiteUserId(),
@ -332,7 +333,7 @@ export class AddonModQuizOfflineProvider {
const question = questionsWithAnswers[Number(slot)];
await CoreQuestion.saveQuestion(
AddonModQuizProvider.COMPONENT,
ADDON_MOD_QUIZ_COMPONENT,
quiz.id,
attempt.id,
attempt.userid ?? CoreSites.getCurrentSiteUserId(),

View File

@ -29,8 +29,9 @@ import { makeSingleton, Translate } from '@singletons';
import { CoreEvents } from '@singletons/events';
import { AddonModQuizAttemptDBRecord } from './database/quiz';
import { AddonModQuizPrefetchHandler } from './handlers/prefetch';
import { AddonModQuiz, AddonModQuizAttemptWSData, AddonModQuizProvider, AddonModQuizQuizWSData } from './quiz';
import { AddonModQuiz, AddonModQuizAttemptWSData, AddonModQuizQuizWSData } from './quiz';
import { AddonModQuizOffline, AddonModQuizQuestionsWithAnswers } from './quiz-offline';
import { ADDON_MOD_QUIZ_COMPONENT } from '../constants';
/**
* Service to sync quizzes.
@ -79,7 +80,7 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider
for (const slot in options.onlineQuestions) {
promises.push(CoreQuestionDelegate.deleteOfflineData(
options.onlineQuestions[slot],
AddonModQuizProvider.COMPONENT,
ADDON_MOD_QUIZ_COMPONENT,
quiz.coursemodule,
siteId,
));
@ -204,7 +205,7 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider
}
quizIds[attempt.quizid] = true;
if (CoreSync.isBlocked(AddonModQuizProvider.COMPONENT, attempt.quizid, siteId)) {
if (CoreSync.isBlocked(ADDON_MOD_QUIZ_COMPONENT, attempt.quizid, siteId)) {
return;
}
@ -268,7 +269,7 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider
}
// Verify that quiz isn't blocked.
if (CoreSync.isBlocked(AddonModQuizProvider.COMPONENT, quiz.id, siteId)) {
if (CoreSync.isBlocked(ADDON_MOD_QUIZ_COMPONENT, quiz.id, siteId)) {
this.logger.debug('Cannot sync quiz ' + quiz.id + ' because it is blocked.');
throw new CoreError(Translate.instant('core.errorsyncblocked', { $a: this.componentTranslate }));
@ -300,7 +301,7 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider
// Sync offline logs.
await CoreUtils.ignoreErrors(
CoreCourseLogHelper.syncActivity(AddonModQuizProvider.COMPONENT, quiz.id, siteId),
CoreCourseLogHelper.syncActivity(ADDON_MOD_QUIZ_COMPONENT, quiz.id, siteId),
);
// Get all the offline attempts for the quiz. It should always be 0 or 1 attempt
@ -381,7 +382,7 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider
await CoreQuestionDelegate.prepareSyncData(
onlineQuestion,
offlineQuestions[slot].answers,
AddonModQuizProvider.COMPONENT,
ADDON_MOD_QUIZ_COMPONENT,
quiz.coursemodule,
siteId,
);

View File

@ -42,8 +42,12 @@ import { AddonModQuizOffline, AddonModQuizQuestionsWithAnswers } from './quiz-of
import { AddonModQuizAutoSyncData, AddonModQuizSyncProvider } from './quiz-sync';
import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site';
import { QUESTION_INVALID_STATE_CLASSES, QUESTION_TODO_STATE_CLASSES } from '@features/question/constants';
const ROOT_CACHE_KEY = 'mmaModQuiz:';
import {
ADDON_MOD_QUIZ_ATTEMPT_FINISHED_EVENT,
AddonModQuizAttemptStates,
ADDON_MOD_QUIZ_COMPONENT,
AddonModQuizGradeMethods,
} from '../constants';
declare module '@singletons/events' {
@ -53,7 +57,7 @@ declare module '@singletons/events' {
* @see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation
*/
export interface CoreEventsData {
[AddonModQuizProvider.ATTEMPT_FINISHED_EVENT]: AddonModQuizAttemptFinishedData;
[ADDON_MOD_QUIZ_ATTEMPT_FINISHED_EVENT]: AddonModQuizAttemptFinishedData;
[AddonModQuizSyncProvider.AUTO_SYNCED]: AddonModQuizAutoSyncData;
}
@ -65,27 +69,7 @@ declare module '@singletons/events' {
@Injectable({ providedIn: 'root' })
export class AddonModQuizProvider {
static readonly COMPONENT = 'mmaModQuiz';
static readonly ATTEMPT_FINISHED_EVENT = 'addon_mod_quiz_attempt_finished';
// Grade methods.
static readonly GRADEHIGHEST = 1;
static readonly GRADEAVERAGE = 2;
static readonly ATTEMPTFIRST = 3;
static readonly ATTEMPTLAST = 4;
// Question options.
static readonly QUESTION_OPTIONS_MAX_ONLY = 1;
static readonly QUESTION_OPTIONS_MARK_AND_MAX = 2;
// Attempt state.
static readonly ATTEMPT_IN_PROGRESS = 'inprogress';
static readonly ATTEMPT_OVERDUE = 'overdue';
static readonly ATTEMPT_FINISHED = 'finished';
static readonly ATTEMPT_ABANDONED = 'abandoned';
// Show the countdown timer if there is less than this amount of time left before the the quiz close date.
static readonly QUIZ_SHOW_TIME_BEFORE_DEADLINE = 3600;
protected static readonly ROOT_CACHE_KEY = 'mmaModQuiz:';
protected logger: CoreLogger;
@ -164,7 +148,7 @@ export class AddonModQuizProvider {
* @returns Cache key.
*/
protected getAttemptAccessInformationCommonCacheKey(quizId: number): string {
return ROOT_CACHE_KEY + 'attemptAccessInformation:' + quizId;
return AddonModQuizProvider.ROOT_CACHE_KEY + 'attemptAccessInformation:' + quizId;
}
/**
@ -189,7 +173,7 @@ export class AddonModQuizProvider {
};
const preSets: CoreSiteWSPreSets = {
cacheKey: this.getAttemptAccessInformationCacheKey(quizId, attemptId),
component: AddonModQuizProvider.COMPONENT,
component: ADDON_MOD_QUIZ_COMPONENT,
componentId: options.cmId,
...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
};
@ -215,7 +199,7 @@ export class AddonModQuizProvider {
* @returns Cache key.
*/
protected getAttemptDataCommonCacheKey(attemptId: number): string {
return ROOT_CACHE_KEY + 'attemptData:' + attemptId;
return AddonModQuizProvider.ROOT_CACHE_KEY + 'attemptData:' + attemptId;
}
/**
@ -248,7 +232,7 @@ export class AddonModQuizProvider {
};
const preSets: CoreSiteWSPreSets = {
cacheKey: this.getAttemptDataCacheKey(attemptId, page),
component: AddonModQuizProvider.COMPONENT,
component: ADDON_MOD_QUIZ_COMPONENT,
componentId: options.cmId,
...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
};
@ -288,10 +272,10 @@ export class AddonModQuizProvider {
}
switch (attempt.state) {
case AddonModQuizProvider.ATTEMPT_IN_PROGRESS:
case AddonModQuizAttemptStates.IN_PROGRESS:
return dueDate * 1000;
case AddonModQuizProvider.ATTEMPT_OVERDUE:
case AddonModQuizAttemptStates.OVERDUE:
return (dueDate + (quiz.graceperiod ?? 0)) * 1000;
default:
@ -311,7 +295,7 @@ export class AddonModQuizProvider {
getAttemptDueDateWarning(quiz: AddonModQuizQuizWSData, attempt: AddonModQuizAttemptWSData): string | undefined {
const dueDate = this.getAttemptDueDate(quiz, attempt);
if (attempt.state === AddonModQuizProvider.ATTEMPT_OVERDUE) {
if (attempt.state === AddonModQuizAttemptStates.OVERDUE) {
return Translate.instant(
'addon.mod_quiz.overduemustbesubmittedby',
{ $a: CoreTimeUtils.userDate(dueDate) },
@ -334,10 +318,10 @@ export class AddonModQuizProvider {
}
switch (attempt.state) {
case AddonModQuizProvider.ATTEMPT_IN_PROGRESS:
case AddonModQuizAttemptStates.IN_PROGRESS:
return [Translate.instant('addon.mod_quiz.stateinprogress')];
case AddonModQuizProvider.ATTEMPT_OVERDUE: {
case AddonModQuizAttemptStates.OVERDUE: {
const sentences: string[] = [];
const dueDate = this.getAttemptDueDate(quiz, attempt);
@ -353,7 +337,7 @@ export class AddonModQuizProvider {
return sentences;
}
case AddonModQuizProvider.ATTEMPT_FINISHED:
case AddonModQuizAttemptStates.FINISHED:
return [
Translate.instant('addon.mod_quiz.statefinished'),
Translate.instant(
@ -362,7 +346,7 @@ export class AddonModQuizProvider {
),
];
case AddonModQuizProvider.ATTEMPT_ABANDONED:
case AddonModQuizAttemptStates.ABANDONED:
return [Translate.instant('addon.mod_quiz.stateabandoned')];
default:
@ -378,16 +362,16 @@ export class AddonModQuizProvider {
*/
getAttemptReadableStateName(state: string): string {
switch (state) {
case AddonModQuizProvider.ATTEMPT_IN_PROGRESS:
case AddonModQuizAttemptStates.IN_PROGRESS:
return Translate.instant('addon.mod_quiz.stateinprogress');
case AddonModQuizProvider.ATTEMPT_OVERDUE:
case AddonModQuizAttemptStates.OVERDUE:
return Translate.instant('addon.mod_quiz.stateoverdue');
case AddonModQuizProvider.ATTEMPT_FINISHED:
case AddonModQuizAttemptStates.FINISHED:
return Translate.instant('addon.mod_quiz.statefinished');
case AddonModQuizProvider.ATTEMPT_ABANDONED:
case AddonModQuizAttemptStates.ABANDONED:
return Translate.instant('addon.mod_quiz.stateabandoned');
default:
@ -413,7 +397,7 @@ export class AddonModQuizProvider {
* @returns Cache key.
*/
protected getAttemptReviewCommonCacheKey(attemptId: number): string {
return ROOT_CACHE_KEY + 'attemptReview:' + attemptId;
return AddonModQuizProvider.ROOT_CACHE_KEY + 'attemptReview:' + attemptId;
}
/**
@ -438,7 +422,7 @@ export class AddonModQuizProvider {
const preSets = {
cacheKey: this.getAttemptReviewCacheKey(attemptId, page),
cacheErrors: ['noreview'],
component: AddonModQuizProvider.COMPONENT,
component: ADDON_MOD_QUIZ_COMPONENT,
componentId: options.cmId,
...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
};
@ -457,7 +441,7 @@ export class AddonModQuizProvider {
* @returns Cache key.
*/
protected getAttemptSummaryCacheKey(attemptId: number): string {
return ROOT_CACHE_KEY + 'attemptSummary:' + attemptId;
return AddonModQuizProvider.ROOT_CACHE_KEY + 'attemptSummary:' + attemptId;
}
/**
@ -487,7 +471,7 @@ export class AddonModQuizProvider {
};
const preSets: CoreSiteWSPreSets = {
cacheKey: this.getAttemptSummaryCacheKey(attemptId),
component: AddonModQuizProvider.COMPONENT,
component: ADDON_MOD_QUIZ_COMPONENT,
componentId: options.cmId,
...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
};
@ -521,7 +505,7 @@ export class AddonModQuizProvider {
* @returns Cache key.
*/
protected getCombinedReviewOptionsCommonCacheKey(quizId: number): string {
return ROOT_CACHE_KEY + 'combinedReviewOptions:' + quizId;
return AddonModQuizProvider.ROOT_CACHE_KEY + 'combinedReviewOptions:' + quizId;
}
/**
@ -544,7 +528,7 @@ export class AddonModQuizProvider {
};
const preSets: CoreSiteWSPreSets = {
cacheKey: this.getCombinedReviewOptionsCacheKey(quizId, userId),
component: AddonModQuizProvider.COMPONENT,
component: ADDON_MOD_QUIZ_COMPONENT,
componentId: options.cmId,
...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
};
@ -581,7 +565,7 @@ export class AddonModQuizProvider {
* @returns Cache key.
*/
protected getFeedbackForGradeCommonCacheKey(quizId: number): string {
return ROOT_CACHE_KEY + 'feedbackForGrade:' + quizId;
return AddonModQuizProvider.ROOT_CACHE_KEY + 'feedbackForGrade:' + quizId;
}
/**
@ -606,7 +590,7 @@ export class AddonModQuizProvider {
const preSets: CoreSiteWSPreSets = {
cacheKey: this.getFeedbackForGradeCacheKey(quizId, grade),
updateFrequency: CoreSite.FREQUENCY_RARELY,
component: AddonModQuizProvider.COMPONENT,
component: ADDON_MOD_QUIZ_COMPONENT,
componentId: options.cmId,
...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
};
@ -719,7 +703,7 @@ export class AddonModQuizProvider {
* @returns Cache key.
*/
protected getQuizDataCacheKey(courseId: number): string {
return ROOT_CACHE_KEY + 'quiz:' + courseId;
return AddonModQuizProvider.ROOT_CACHE_KEY + 'quiz:' + courseId;
}
/**
@ -746,7 +730,7 @@ export class AddonModQuizProvider {
const preSets: CoreSiteWSPreSets = {
cacheKey: this.getQuizDataCacheKey(courseId),
updateFrequency: CoreSite.FREQUENCY_RARELY,
component: AddonModQuizProvider.COMPONENT,
component: ADDON_MOD_QUIZ_COMPONENT,
...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
};
@ -797,7 +781,7 @@ export class AddonModQuizProvider {
* @returns Cache key.
*/
protected getQuizAccessInformationCacheKey(quizId: number): string {
return ROOT_CACHE_KEY + 'quizAccessInformation:' + quizId;
return AddonModQuizProvider.ROOT_CACHE_KEY + 'quizAccessInformation:' + quizId;
}
/**
@ -818,7 +802,7 @@ export class AddonModQuizProvider {
};
const preSets: CoreSiteWSPreSets = {
cacheKey: this.getQuizAccessInformationCacheKey(quizId),
component: AddonModQuizProvider.COMPONENT,
component: ADDON_MOD_QUIZ_COMPONENT,
componentId: options.cmId,
...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
};
@ -842,13 +826,13 @@ export class AddonModQuizProvider {
}
switch (method) {
case AddonModQuizProvider.GRADEHIGHEST:
case AddonModQuizGradeMethods.HIGHEST_GRADE:
return Translate.instant('addon.mod_quiz.gradehighest');
case AddonModQuizProvider.GRADEAVERAGE:
case AddonModQuizGradeMethods.AVERAGE_GRADE:
return Translate.instant('addon.mod_quiz.gradeaverage');
case AddonModQuizProvider.ATTEMPTFIRST:
case AddonModQuizGradeMethods.FIRST_ATTEMPT:
return Translate.instant('addon.mod_quiz.attemptfirst');
case AddonModQuizProvider.ATTEMPTLAST:
case AddonModQuizGradeMethods.LAST_ATTEMPT:
return Translate.instant('addon.mod_quiz.attemptlast');
default:
return '';
@ -862,7 +846,7 @@ export class AddonModQuizProvider {
* @returns Cache key.
*/
protected getQuizRequiredQtypesCacheKey(quizId: number): string {
return ROOT_CACHE_KEY + 'quizRequiredQtypes:' + quizId;
return AddonModQuizProvider.ROOT_CACHE_KEY + 'quizRequiredQtypes:' + quizId;
}
/**
@ -881,7 +865,7 @@ export class AddonModQuizProvider {
const preSets: CoreSiteWSPreSets = {
cacheKey: this.getQuizRequiredQtypesCacheKey(quizId),
updateFrequency: CoreSite.FREQUENCY_SOMETIMES,
component: AddonModQuizProvider.COMPONENT,
component: ADDON_MOD_QUIZ_COMPONENT,
componentId: options.cmId,
...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
};
@ -1015,7 +999,7 @@ export class AddonModQuizProvider {
* @returns Cache key.
*/
protected getUserAttemptsCommonCacheKey(quizId: number): string {
return ROOT_CACHE_KEY + 'userAttempts:' + quizId;
return AddonModQuizProvider.ROOT_CACHE_KEY + 'userAttempts:' + quizId;
}
/**
@ -1045,7 +1029,7 @@ export class AddonModQuizProvider {
const preSets: CoreSiteWSPreSets = {
cacheKey: this.getUserAttemptsCacheKey(quizId, userId),
updateFrequency: CoreSite.FREQUENCY_SOMETIMES,
component: AddonModQuizProvider.COMPONENT,
component: ADDON_MOD_QUIZ_COMPONENT,
componentId: options.cmId,
...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
};
@ -1073,7 +1057,7 @@ export class AddonModQuizProvider {
* @returns Cache key.
*/
protected getUserBestGradeCommonCacheKey(quizId: number): string {
return ROOT_CACHE_KEY + 'userBestGrade:' + quizId;
return AddonModQuizProvider.ROOT_CACHE_KEY + 'userBestGrade:' + quizId;
}
/**
@ -1093,7 +1077,7 @@ export class AddonModQuizProvider {
};
const preSets: CoreSiteWSPreSets = {
cacheKey: this.getUserBestGradeCacheKey(quizId, userId),
component: AddonModQuizProvider.COMPONENT,
component: ADDON_MOD_QUIZ_COMPONENT,
componentId: options.cmId,
...CoreSites.getReadingStrategyPreSets(options.readingStrategy), // Include reading strategy preSets.
};
@ -1430,7 +1414,7 @@ export class AddonModQuizProvider {
* @returns Whether it's finished.
*/
isAttemptFinished(state?: string): boolean {
return state == AddonModQuizProvider.ATTEMPT_FINISHED || state == AddonModQuizProvider.ATTEMPT_ABANDONED;
return state === AddonModQuizAttemptStates.FINISHED || state === AddonModQuizAttemptStates.ABANDONED;
}
/**
@ -1461,7 +1445,7 @@ export class AddonModQuizProvider {
* @returns Whether it's nearly over or over.
*/
isAttemptTimeNearlyOver(quiz: AddonModQuizQuizWSData, attempt: AddonModQuizAttemptWSData): boolean {
if (attempt.state != AddonModQuizProvider.ATTEMPT_IN_PROGRESS) {
if (attempt.state !== AddonModQuizAttemptStates.IN_PROGRESS) {
// Attempt not in progress, return true.
return true;
}
@ -1600,7 +1584,7 @@ export class AddonModQuizProvider {
return CoreCourseLogHelper.log(
'mod_quiz_view_attempt_review',
params,
AddonModQuizProvider.COMPONENT,
ADDON_MOD_QUIZ_COMPONENT,
quizId,
siteId,
);
@ -1633,7 +1617,7 @@ export class AddonModQuizProvider {
return CoreCourseLogHelper.log(
'mod_quiz_view_attempt_summary',
params,
AddonModQuizProvider.COMPONENT,
ADDON_MOD_QUIZ_COMPONENT,
quizId,
siteId,
);
@ -1654,7 +1638,7 @@ export class AddonModQuizProvider {
return CoreCourseLogHelper.log(
'mod_quiz_view_quiz',
params,
AddonModQuizProvider.COMPONENT,
ADDON_MOD_QUIZ_COMPONENT,
id,
siteId,
);
@ -1897,7 +1881,7 @@ export class AddonModQuizProvider {
shouldShowTimeLeft(rules: string[], attempt: AddonModQuizAttemptWSData, endTime: number): boolean {
const timeNow = CoreTimeUtils.timestamp();
if (attempt.state != AddonModQuizProvider.ATTEMPT_IN_PROGRESS) {
if (attempt.state !== AddonModQuizAttemptStates.IN_PROGRESS) {
return false;
}
@ -2392,7 +2376,7 @@ export type AddonModQuizViewQuizWSParams = {
};
/**
* Data passed to ATTEMPT_FINISHED_EVENT event.
* Data passed to ADDON_MOD_QUIZ_ATTEMPT_FINISHED_EVENT event.
*/
export type AddonModQuizAttemptFinishedData = {
quizId: number;

View File

@ -19,3 +19,8 @@ export const QUESTION_NEEDS_GRADING_STATE_CLASSES = ['requiresgrading', 'complet
export const QUESTION_FINISHED_STATE_CLASSES = ['complete'] as const;
export const QUESTION_GAVE_UP_STATE_CLASSES = ['notanswered'] as const;
export const QUESTION_GRADED_STATE_CLASSES = ['complete', 'incorrect', 'partiallycorrect', 'correct'] as const;
export const enum QuestionDisplayOptionsMarks {
MAX_ONLY = 1,
MARK_AND_MAX = 2,
}