MOBILE-4263 quiz: Remove non-null assertions
parent
4792e47737
commit
9744eb5137
|
@ -333,15 +333,16 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const bestGrade = this.bestGrade.grade;
|
||||||
const formattedGradebookGrade = AddonModQuiz.formatGrade(this.gradebookData.grade, quiz.decimalpoints);
|
const formattedGradebookGrade = AddonModQuiz.formatGrade(this.gradebookData.grade, quiz.decimalpoints);
|
||||||
const formattedBestGrade = AddonModQuiz.formatGrade(this.bestGrade.grade, quiz.decimalpoints);
|
const formattedBestGrade = AddonModQuiz.formatGrade(bestGrade, quiz.decimalpoints);
|
||||||
let gradeToShow = formattedGradebookGrade; // By default we show the grade in the gradebook.
|
let gradeToShow = formattedGradebookGrade; // By default we show the grade in the gradebook.
|
||||||
|
|
||||||
this.showResults = true;
|
this.showResults = true;
|
||||||
this.gradeOverridden = formattedGradebookGrade != formattedBestGrade;
|
this.gradeOverridden = formattedGradebookGrade != formattedBestGrade;
|
||||||
this.gradebookFeedback = this.gradebookData.feedback;
|
this.gradebookFeedback = this.gradebookData.feedback;
|
||||||
|
|
||||||
if (this.bestGrade.grade! > this.gradebookData.grade && this.gradebookData.grade == quiz.grade) {
|
if (bestGrade && bestGrade > this.gradebookData.grade && this.gradebookData.grade == quiz.grade) {
|
||||||
// The best grade is higher than the max grade for the quiz.
|
// The best grade is higher than the max grade for the quiz.
|
||||||
// We'll do like Moodle web and show the best grade instead of the gradebook grade.
|
// We'll do like Moodle web and show the best grade instead of the gradebook grade.
|
||||||
this.gradeOverridden = false;
|
this.gradeOverridden = false;
|
||||||
|
@ -556,8 +557,16 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
|
||||||
*
|
*
|
||||||
* @returns Promise resolved when done.
|
* @returns Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected sync(): Promise<AddonModQuizSyncResult> {
|
protected async sync(): Promise<AddonModQuizSyncResult> {
|
||||||
return AddonModQuizSync.syncQuiz(this.candidateQuiz!, true);
|
if (!this.candidateQuiz) {
|
||||||
|
return {
|
||||||
|
warnings: [],
|
||||||
|
attemptFinished: false,
|
||||||
|
updated: false,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
return AddonModQuizSync.syncQuiz(this.candidateQuiz, true);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -579,36 +588,31 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
|
||||||
}
|
}
|
||||||
|
|
||||||
const lastFinished = AddonModQuiz.getLastFinishedAttemptFromList(attempts);
|
const lastFinished = AddonModQuiz.getLastFinishedAttemptFromList(attempts);
|
||||||
const promises: Promise<unknown>[] = [];
|
let openReview = false;
|
||||||
|
|
||||||
if (this.autoReview && lastFinished && lastFinished.id >= this.autoReview.attemptId) {
|
if (this.autoReview && lastFinished && lastFinished.id >= this.autoReview.attemptId) {
|
||||||
// User just finished an attempt in offline and it seems it's been synced, since it's finished in online.
|
// User just finished an attempt in offline and it seems it's been synced, since it's finished in online.
|
||||||
// Go to the review of this attempt if the user hasn't left this view.
|
// Go to the review of this attempt if the user hasn't left this view.
|
||||||
if (!this.isDestroyed && this.isCurrentView) {
|
if (!this.isDestroyed && this.isCurrentView) {
|
||||||
promises.push(this.goToAutoReview());
|
openReview = true;
|
||||||
}
|
}
|
||||||
this.autoReview = undefined;
|
this.autoReview = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Get combined review options.
|
const [options] = await Promise.all([
|
||||||
promises.push(AddonModQuiz.getCombinedReviewOptions(quiz.id, { cmId: this.module.id }).then((options) => {
|
AddonModQuiz.getCombinedReviewOptions(quiz.id, { cmId: this.module.id }),
|
||||||
this.options = options;
|
this.getQuizGrade(),
|
||||||
|
openReview ? this.goToAutoReview() : undefined,
|
||||||
return;
|
]);
|
||||||
}));
|
|
||||||
|
|
||||||
// Get best grade.
|
|
||||||
promises.push(this.getQuizGrade());
|
|
||||||
|
|
||||||
await Promise.all(promises);
|
|
||||||
|
|
||||||
|
this.options = options;
|
||||||
const grade = this.gradebookData?.grade !== undefined ? this.gradebookData.grade : this.bestGrade?.grade;
|
const grade = this.gradebookData?.grade !== undefined ? this.gradebookData.grade : this.bestGrade?.grade;
|
||||||
const quizGrade = AddonModQuiz.formatGrade(grade, quiz.decimalpoints);
|
const quizGrade = AddonModQuiz.formatGrade(grade, quiz.decimalpoints);
|
||||||
|
|
||||||
// Calculate data to construct the header of the attempts table.
|
// Calculate data to construct the header of the attempts table.
|
||||||
AddonModQuizHelper.setQuizCalculatedData(quiz, this.options!);
|
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 >= AddonModQuizProvider.QUESTION_OPTIONS_MARK_AND_MAX;
|
||||||
|
|
||||||
// Calculate data to show for each attempt.
|
// Calculate data to show for each attempt.
|
||||||
const formattedAttempts = await Promise.all(attempts.map((attempt, index) => {
|
const formattedAttempts = await Promise.all(attempts.map((attempt, index) => {
|
||||||
|
|
|
@ -61,6 +61,6 @@ export type AddonModQuizNavigationQuestion = CoreQuestionQuestionParsed & {
|
||||||
};
|
};
|
||||||
|
|
||||||
export type AddonModQuizNavigationModalReturn = {
|
export type AddonModQuizNavigationModalReturn = {
|
||||||
page?: number;
|
page: number;
|
||||||
slot?: number;
|
slot?: number;
|
||||||
};
|
};
|
||||||
|
|
|
@ -66,11 +66,13 @@ export class AddonModQuizPreflightModalComponent implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const quiz = this.quiz;
|
||||||
|
|
||||||
await Promise.all(this.rules.map(async (rule) => {
|
await Promise.all(this.rules.map(async (rule) => {
|
||||||
// Check if preflight is required for rule and, if so, get the component to render it.
|
// Check if preflight is required for rule and, if so, get the component to render it.
|
||||||
const required = await AddonModQuizAccessRuleDelegate.isPreflightCheckRequiredForRule(
|
const required = await AddonModQuizAccessRuleDelegate.isPreflightCheckRequiredForRule(
|
||||||
rule,
|
rule,
|
||||||
this.quiz!,
|
quiz,
|
||||||
this.attempt,
|
this.attempt,
|
||||||
this.prefetch,
|
this.prefetch,
|
||||||
this.siteId,
|
this.siteId,
|
||||||
|
|
|
@ -94,20 +94,20 @@ export class AddonModQuizAttemptPage implements OnInit {
|
||||||
// Load attempt data.
|
// Load attempt data.
|
||||||
const [options, accessInfo, attempt] = await Promise.all([
|
const [options, accessInfo, attempt] = await Promise.all([
|
||||||
AddonModQuiz.getCombinedReviewOptions(this.quiz.id, { cmId: this.quiz.coursemodule }),
|
AddonModQuiz.getCombinedReviewOptions(this.quiz.id, { cmId: this.quiz.coursemodule }),
|
||||||
this.fetchAccessInfo(),
|
this.fetchAccessInfo(this.quiz),
|
||||||
this.fetchAttempt(),
|
this.fetchAttempt(this.quiz.id),
|
||||||
]);
|
]);
|
||||||
|
|
||||||
// Set calculated data.
|
// Set calculated data.
|
||||||
this.showReviewColumn = accessInfo.canreviewmyattempts;
|
this.showReviewColumn = accessInfo.canreviewmyattempts;
|
||||||
AddonModQuizHelper.setQuizCalculatedData(this.quiz, options);
|
AddonModQuizHelper.setQuizCalculatedData(this.quiz, options);
|
||||||
|
|
||||||
this.attempt = await AddonModQuizHelper.setAttemptCalculatedData(this.quiz!, attempt, false, undefined, true);
|
this.attempt = await AddonModQuizHelper.setAttemptCalculatedData(this.quiz, attempt, false, undefined, true);
|
||||||
|
|
||||||
// Check if the feedback should be displayed.
|
// Check if the feedback should be displayed.
|
||||||
const grade = Number(this.attempt!.rescaledGrade);
|
const grade = Number(this.attempt.rescaledGrade);
|
||||||
|
|
||||||
if (this.quiz.showFeedbackColumn && AddonModQuiz.isAttemptFinished(this.attempt!.state) &&
|
if (this.quiz.showFeedbackColumn && AddonModQuiz.isAttemptFinished(this.attempt.state) &&
|
||||||
options.someoptions.overallfeedback && !isNaN(grade)) {
|
options.someoptions.overallfeedback && !isNaN(grade)) {
|
||||||
|
|
||||||
// Feedback should be displayed, get the feedback for the grade.
|
// Feedback should be displayed, get the feedback for the grade.
|
||||||
|
@ -127,11 +127,12 @@ export class AddonModQuizAttemptPage implements OnInit {
|
||||||
/**
|
/**
|
||||||
* Get the attempt.
|
* Get the attempt.
|
||||||
*
|
*
|
||||||
|
* @param quizId Quiz ID.
|
||||||
* @returns Promise resolved when done.
|
* @returns Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected async fetchAttempt(): Promise<AddonModQuizAttemptWSData> {
|
protected async fetchAttempt(quizId: number): Promise<AddonModQuizAttemptWSData> {
|
||||||
// Get all the attempts and search the one we want.
|
// Get all the attempts and search the one we want.
|
||||||
const attempts = await AddonModQuiz.getUserAttempts(this.quiz!.id, { cmId: this.cmId });
|
const attempts = await AddonModQuiz.getUserAttempts(quizId, { cmId: this.cmId });
|
||||||
|
|
||||||
const attempt = attempts.find(attempt => attempt.id == this.attemptId);
|
const attempt = attempts.find(attempt => attempt.id == this.attemptId);
|
||||||
|
|
||||||
|
@ -148,10 +149,11 @@ export class AddonModQuizAttemptPage implements OnInit {
|
||||||
/**
|
/**
|
||||||
* Get the access info.
|
* Get the access info.
|
||||||
*
|
*
|
||||||
|
* @param quiz Quiz instance.
|
||||||
* @returns Promise resolved when done.
|
* @returns Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected async fetchAccessInfo(): Promise<AddonModQuizGetQuizAccessInformationWSResponse> {
|
protected async fetchAccessInfo(quiz: AddonModQuizQuizData): Promise<AddonModQuizGetQuizAccessInformationWSResponse> {
|
||||||
const accessInfo = await AddonModQuiz.getQuizAccessInformation(this.quiz!.id, { cmId: this.cmId });
|
const accessInfo = await AddonModQuiz.getQuizAccessInformation(quiz.id, { cmId: this.cmId });
|
||||||
|
|
||||||
if (!accessInfo.canreviewmyattempts) {
|
if (!accessInfo.canreviewmyattempts) {
|
||||||
return accessInfo;
|
return accessInfo;
|
||||||
|
@ -161,7 +163,7 @@ export class AddonModQuizAttemptPage implements OnInit {
|
||||||
await CoreUtils.ignoreErrors(AddonModQuiz.invalidateAttemptReviewForPage(this.attemptId, -1));
|
await CoreUtils.ignoreErrors(AddonModQuiz.invalidateAttemptReviewForPage(this.attemptId, -1));
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await AddonModQuiz.getAttemptReview(this.attemptId, { page: -1, cmId: this.quiz!.coursemodule });
|
await AddonModQuiz.getAttemptReview(this.attemptId, { page: -1, cmId: quiz.coursemodule });
|
||||||
} catch {
|
} catch {
|
||||||
// Error getting the review, assume the user cannot review the attempt.
|
// Error getting the review, assume the user cannot review the attempt.
|
||||||
accessInfo.canreviewmyattempts = false;
|
accessInfo.canreviewmyattempts = false;
|
||||||
|
@ -202,7 +204,11 @@ export class AddonModQuizAttemptPage implements OnInit {
|
||||||
* @returns Promise resolved when done.
|
* @returns Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
async reviewAttempt(): Promise<void> {
|
async reviewAttempt(): Promise<void> {
|
||||||
CoreNavigator.navigate(`../../review/${this.attempt!.id}`);
|
if (!this.attempt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
CoreNavigator.navigate(`../../review/${this.attempt.id}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -203,6 +203,10 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
||||||
* @param button Clicked button.
|
* @param button Clicked button.
|
||||||
*/
|
*/
|
||||||
async behaviourButtonClicked(button: CoreQuestionBehaviourButton): Promise<void> {
|
async behaviourButtonClicked(button: CoreQuestionBehaviourButton): Promise<void> {
|
||||||
|
if (!this.quiz || !this.attempt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let modal: CoreIonLoadingElement | undefined;
|
let modal: CoreIonLoadingElement | undefined;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -212,13 +216,13 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
||||||
modal = await CoreDomUtils.showModalLoading('core.sending', true);
|
modal = await CoreDomUtils.showModalLoading('core.sending', true);
|
||||||
|
|
||||||
// Get the answers.
|
// Get the answers.
|
||||||
const answers = await this.prepareAnswers();
|
const answers = await this.prepareAnswers(this.quiz.coursemodule);
|
||||||
|
|
||||||
// Add the clicked button data.
|
// Add the clicked button data.
|
||||||
answers[button.name] = button.value;
|
answers[button.name] = button.value;
|
||||||
|
|
||||||
// Behaviour checks are always in online.
|
// Behaviour checks are always in online.
|
||||||
await AddonModQuiz.processAttempt(this.quiz!, this.attempt!, answers, this.preflightData);
|
await AddonModQuiz.processAttempt(this.quiz, this.attempt, answers, this.preflightData);
|
||||||
|
|
||||||
this.reloadNavigation = true; // Data sent to server, navigation should be reloaded.
|
this.reloadNavigation = true; // Data sent to server, navigation should be reloaded.
|
||||||
|
|
||||||
|
@ -230,7 +234,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
||||||
this.content?.scrollToTop(); // Scroll top so the spinner is seen.
|
this.content?.scrollToTop(); // Scroll top so the spinner is seen.
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await this.loadPage(this.attempt!.currentpage!);
|
await this.loadPage(this.attempt.currentpage ?? 0);
|
||||||
} finally {
|
} finally {
|
||||||
this.loaded = true;
|
this.loaded = true;
|
||||||
if (scrollTop != -1) {
|
if (scrollTop != -1) {
|
||||||
|
@ -311,8 +315,8 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
// If the user isn't seeing the summary, start the check again.
|
// If the user isn't seeing the summary, start the check again.
|
||||||
if (!this.showSummary) {
|
if (!this.showSummary && this.quiz) {
|
||||||
this.autoSave.startCheckChangesProcess(this.quiz!, this.attempt, this.preflightData, this.offline);
|
this.autoSave.startCheckChangesProcess(this.quiz, this.attempt, this.preflightData, this.offline);
|
||||||
}
|
}
|
||||||
|
|
||||||
CoreDomUtils.showErrorModalDefault(error, 'addon.mod_quiz.errorgetquestions', true);
|
CoreDomUtils.showErrorModalDefault(error, 'addon.mod_quiz.errorgetquestions', true);
|
||||||
|
@ -351,7 +355,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
||||||
this.offline = await AddonModQuiz.isLastAttemptOfflineUnfinished(this.quiz);
|
this.offline = await AddonModQuiz.isLastAttemptOfflineUnfinished(this.quiz);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (this.quiz!.timelimit && this.quiz!.timelimit > 0) {
|
if (this.quiz.timelimit && this.quiz.timelimit > 0) {
|
||||||
this.readableTimeLimit = CoreTime.formatTime(this.quiz.timelimit);
|
this.readableTimeLimit = CoreTime.formatTime(this.quiz.timelimit);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,11 +398,15 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
||||||
* @returns Promise resolved when done.
|
* @returns Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
async finishAttempt(userFinish?: boolean, timeUp?: boolean): Promise<void> {
|
async finishAttempt(userFinish?: boolean, timeUp?: boolean): Promise<void> {
|
||||||
|
if (!this.quiz || !this.attempt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let modal: CoreIonLoadingElement | undefined;
|
let modal: CoreIonLoadingElement | undefined;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Show confirm if the user clicked the finish button and the quiz is in progress.
|
// 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 == AddonModQuizProvider.ATTEMPT_IN_PROGRESS) {
|
||||||
await CoreDomUtils.showConfirm(Translate.instant('addon.mod_quiz.confirmclose'));
|
await CoreDomUtils.showConfirm(Translate.instant('addon.mod_quiz.confirmclose'));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -408,8 +416,8 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
||||||
|
|
||||||
// Trigger an event to notify the attempt was finished.
|
// Trigger an event to notify the attempt was finished.
|
||||||
CoreEvents.trigger(AddonModQuizProvider.ATTEMPT_FINISHED_EVENT, {
|
CoreEvents.trigger(AddonModQuizProvider.ATTEMPT_FINISHED_EVENT, {
|
||||||
quizId: this.quiz!.id,
|
quizId: this.quiz.id,
|
||||||
attemptId: this.attempt!.id,
|
attemptId: this.attempt.id,
|
||||||
synced: !this.offline,
|
synced: !this.offline,
|
||||||
}, CoreSites.getCurrentSiteId());
|
}, CoreSites.getCurrentSiteId());
|
||||||
|
|
||||||
|
@ -431,9 +439,13 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
||||||
* @returns Promise resolved when done.
|
* @returns Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected async fixSequenceChecks(): Promise<void> {
|
protected async fixSequenceChecks(): Promise<void> {
|
||||||
|
if (!this.attempt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Get current page data again to get the latest sequencechecks.
|
// Get current page data again to get the latest sequencechecks.
|
||||||
const data = await AddonModQuiz.getAttemptData(this.attempt!.id, this.attempt!.currentpage!, this.preflightData, {
|
const data = await AddonModQuiz.getAttemptData(this.attempt.id, this.attempt.currentpage ?? 0, this.preflightData, {
|
||||||
cmId: this.quiz!.coursemodule,
|
cmId: this.quiz?.coursemodule,
|
||||||
readingStrategy: this.offline ? CoreSitesReadingStrategy.PREFER_CACHE : CoreSitesReadingStrategy.ONLY_NETWORK,
|
readingStrategy: this.offline ? CoreSitesReadingStrategy.PREFER_CACHE : CoreSitesReadingStrategy.ONLY_NETWORK,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -465,14 +477,14 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
||||||
* Initializes the timer if enabled.
|
* Initializes the timer if enabled.
|
||||||
*/
|
*/
|
||||||
protected initTimer(): void {
|
protected initTimer(): void {
|
||||||
if (!this.attemptAccessInfo?.endtime || this.attemptAccessInfo.endtime < 0) {
|
if (!this.quizAccessInfo || !this.attempt || !this.attemptAccessInfo?.endtime || this.attemptAccessInfo.endtime < 0) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Quiz has an end time. Check if time left should be shown.
|
// Quiz has an end time. Check if time left should be shown.
|
||||||
const shouldShowTime = AddonModQuiz.shouldShowTimeLeft(
|
const shouldShowTime = AddonModQuiz.shouldShowTimeLeft(
|
||||||
this.quizAccessInfo!.activerulenames,
|
this.quizAccessInfo.activerulenames,
|
||||||
this.attempt!,
|
this.attempt,
|
||||||
this.attemptAccessInfo.endtime,
|
this.attemptAccessInfo.endtime,
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -490,8 +502,12 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
||||||
* @returns Promise resolved when done.
|
* @returns Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected async loadPage(page: number): Promise<void> {
|
protected async loadPage(page: number): Promise<void> {
|
||||||
const data = await AddonModQuiz.getAttemptData(this.attempt!.id, page, this.preflightData, {
|
if (!this.quiz || !this.attempt) {
|
||||||
cmId: this.quiz!.coursemodule,
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const data = await AddonModQuiz.getAttemptData(this.attempt.id, page, this.preflightData, {
|
||||||
|
cmId: this.quiz.coursemodule,
|
||||||
readingStrategy: this.offline ? CoreSitesReadingStrategy.PREFER_CACHE : CoreSitesReadingStrategy.ONLY_NETWORK,
|
readingStrategy: this.offline ? CoreSitesReadingStrategy.PREFER_CACHE : CoreSitesReadingStrategy.ONLY_NETWORK,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -523,7 +539,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Start looking for changes.
|
// Start looking for changes.
|
||||||
this.autoSave.startCheckChangesProcess(this.quiz!, this.attempt, this.preflightData, this.offline);
|
this.autoSave.startCheckChangesProcess(this.quiz, this.attempt, this.preflightData, this.offline);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -532,23 +548,27 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
||||||
* @returns Promise resolved when done.
|
* @returns Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected async loadSummary(): Promise<void> {
|
protected async loadSummary(): Promise<void> {
|
||||||
|
if (!this.quiz || !this.attempt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
this.summaryQuestions = [];
|
this.summaryQuestions = [];
|
||||||
|
|
||||||
this.summaryQuestions = await AddonModQuiz.getAttemptSummary(this.attempt!.id, this.preflightData, {
|
this.summaryQuestions = await AddonModQuiz.getAttemptSummary(this.attempt.id, this.preflightData, {
|
||||||
cmId: this.quiz!.coursemodule,
|
cmId: this.quiz.coursemodule,
|
||||||
loadLocal: this.offline,
|
loadLocal: this.offline,
|
||||||
readingStrategy: this.offline ? CoreSitesReadingStrategy.PREFER_CACHE : CoreSitesReadingStrategy.ONLY_NETWORK,
|
readingStrategy: this.offline ? CoreSitesReadingStrategy.PREFER_CACHE : CoreSitesReadingStrategy.ONLY_NETWORK,
|
||||||
});
|
});
|
||||||
|
|
||||||
this.showSummary = true;
|
this.showSummary = true;
|
||||||
this.canReturn = this.attempt!.state == AddonModQuizProvider.ATTEMPT_IN_PROGRESS && !this.attempt!.finishedOffline;
|
this.canReturn = this.attempt.state == AddonModQuizProvider.ATTEMPT_IN_PROGRESS && !this.attempt.finishedOffline;
|
||||||
this.preventSubmitMessages = AddonModQuiz.getPreventSubmitMessages(this.summaryQuestions);
|
this.preventSubmitMessages = AddonModQuiz.getPreventSubmitMessages(this.summaryQuestions);
|
||||||
|
|
||||||
this.dueDateWarning = AddonModQuiz.getAttemptDueDateWarning(this.quiz!, this.attempt!);
|
this.dueDateWarning = AddonModQuiz.getAttemptDueDateWarning(this.quiz, this.attempt);
|
||||||
|
|
||||||
// Log summary as viewed.
|
// Log summary as viewed.
|
||||||
CoreUtils.ignoreErrors(
|
CoreUtils.ignoreErrors(
|
||||||
AddonModQuiz.logViewAttemptSummary(this.attempt!.id, this.preflightData, this.quiz!.id, this.quiz!.name),
|
AddonModQuiz.logViewAttemptSummary(this.attempt.id, this.preflightData, this.quiz.id, this.quiz.name),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -558,9 +578,13 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
||||||
* @returns Promise resolved when done.
|
* @returns Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected async loadNavigation(): Promise<void> {
|
protected async loadNavigation(): Promise<void> {
|
||||||
|
if (!this.attempt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// We use the attempt summary to build the navigation because it contains all the questions.
|
// We use the attempt summary to build the navigation because it contains all the questions.
|
||||||
this.navigation = await AddonModQuiz.getAttemptSummary(this.attempt!.id, this.preflightData, {
|
this.navigation = await AddonModQuiz.getAttemptSummary(this.attempt.id, this.preflightData, {
|
||||||
cmId: this.quiz!.coursemodule,
|
cmId: this.quiz?.coursemodule,
|
||||||
loadLocal: this.offline,
|
loadLocal: this.offline,
|
||||||
readingStrategy: this.offline ? CoreSitesReadingStrategy.PREFER_CACHE : CoreSitesReadingStrategy.ONLY_NETWORK,
|
readingStrategy: this.offline ? CoreSitesReadingStrategy.PREFER_CACHE : CoreSitesReadingStrategy.ONLY_NETWORK,
|
||||||
});
|
});
|
||||||
|
@ -602,21 +626,22 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.changePage(modalData.page!, true, modalData.slot);
|
this.changePage(modalData.page, true, modalData.slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Prepare the answers to be sent for the attempt.
|
* Prepare the answers to be sent for the attempt.
|
||||||
*
|
*
|
||||||
|
* @param componentId Component ID.
|
||||||
* @returns Promise resolved with the answers.
|
* @returns Promise resolved with the answers.
|
||||||
*/
|
*/
|
||||||
protected prepareAnswers(): Promise<CoreQuestionsAnswers> {
|
protected prepareAnswers(componentId: number): Promise<CoreQuestionsAnswers> {
|
||||||
return CoreQuestionHelper.prepareAnswers(
|
return CoreQuestionHelper.prepareAnswers(
|
||||||
this.questions,
|
this.questions,
|
||||||
this.getAnswers(),
|
this.getAnswers(),
|
||||||
this.offline,
|
this.offline,
|
||||||
this.component,
|
this.component,
|
||||||
this.quiz!.coursemodule,
|
componentId,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -629,18 +654,22 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
||||||
* @returns Promise resolved when done.
|
* @returns Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected async processAttempt(userFinish?: boolean, timeUp?: boolean, retrying?: boolean): Promise<void> {
|
protected async processAttempt(userFinish?: boolean, timeUp?: boolean, retrying?: boolean): Promise<void> {
|
||||||
|
if (!this.quiz || !this.attempt) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Get the answers to send.
|
// Get the answers to send.
|
||||||
let answers: CoreQuestionsAnswers = {};
|
let answers: CoreQuestionsAnswers = {};
|
||||||
|
|
||||||
if (!this.showSummary) {
|
if (!this.showSummary) {
|
||||||
answers = await this.prepareAnswers();
|
answers = await this.prepareAnswers(this.quiz.coursemodule);
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// Send the answers.
|
// Send the answers.
|
||||||
await AddonModQuiz.processAttempt(
|
await AddonModQuiz.processAttempt(
|
||||||
this.quiz!,
|
this.quiz,
|
||||||
this.attempt!,
|
this.attempt,
|
||||||
answers,
|
answers,
|
||||||
this.preflightData,
|
this.preflightData,
|
||||||
userFinish,
|
userFinish,
|
||||||
|
@ -676,7 +705,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
||||||
CoreForms.triggerFormSubmittedEvent(this.formElement, !this.offline, CoreSites.getCurrentSiteId());
|
CoreForms.triggerFormSubmittedEvent(this.formElement, !this.offline, CoreSites.getCurrentSiteId());
|
||||||
}
|
}
|
||||||
|
|
||||||
return CoreQuestionHelper.clearTmpData(this.questions, this.component, this.quiz!.coursemodule);
|
return CoreQuestionHelper.clearTmpData(this.questions, this.component, this.quiz.coursemodule);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -731,12 +760,16 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
||||||
* @returns Promise resolved when done.
|
* @returns Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected async startOrContinueAttempt(): Promise<void> {
|
protected async startOrContinueAttempt(): Promise<void> {
|
||||||
|
if (!this.quiz || !this.quizAccessInfo) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
let attempt = this.newAttempt ? undefined : this.lastAttempt;
|
let attempt = this.newAttempt ? undefined : this.lastAttempt;
|
||||||
|
|
||||||
// Get the preflight data and start attempt if needed.
|
// Get the preflight data and start attempt if needed.
|
||||||
attempt = await AddonModQuizHelper.getAndCheckPreflightData(
|
attempt = await AddonModQuizHelper.getAndCheckPreflightData(
|
||||||
this.quiz!,
|
this.quiz,
|
||||||
this.quizAccessInfo!,
|
this.quizAccessInfo,
|
||||||
this.preflightData,
|
this.preflightData,
|
||||||
attempt,
|
attempt,
|
||||||
this.offline,
|
this.offline,
|
||||||
|
@ -745,8 +778,8 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
||||||
);
|
);
|
||||||
|
|
||||||
// Re-fetch attempt access information with the right attempt (might have changed because a new attempt was created).
|
// Re-fetch attempt access information with the right attempt (might have changed because a new attempt was created).
|
||||||
this.attemptAccessInfo = await AddonModQuiz.getAttemptAccessInformation(this.quiz!.id, attempt.id, {
|
this.attemptAccessInfo = await AddonModQuiz.getAttemptAccessInformation(this.quiz.id, attempt.id, {
|
||||||
cmId: this.quiz!.coursemodule,
|
cmId: this.quiz.coursemodule,
|
||||||
readingStrategy: this.offline ? CoreSitesReadingStrategy.PREFER_CACHE : CoreSitesReadingStrategy.ONLY_NETWORK,
|
readingStrategy: this.offline ? CoreSitesReadingStrategy.PREFER_CACHE : CoreSitesReadingStrategy.ONLY_NETWORK,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -756,7 +789,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
||||||
|
|
||||||
if (this.attempt.state != AddonModQuizProvider.ATTEMPT_OVERDUE && !this.attempt.finishedOffline) {
|
if (this.attempt.state != AddonModQuizProvider.ATTEMPT_OVERDUE && !this.attempt.finishedOffline) {
|
||||||
// Attempt not overdue and not finished in offline, load page.
|
// Attempt not overdue and not finished in offline, load page.
|
||||||
await this.loadPage(this.attempt.currentpage!);
|
await this.loadPage(this.attempt.currentpage ?? 0);
|
||||||
|
|
||||||
this.initTimer();
|
this.initTimer();
|
||||||
} else {
|
} else {
|
||||||
|
|
|
@ -112,7 +112,7 @@ export class AddonModQuizReviewPage implements OnInit {
|
||||||
* @param slot Slot of the question to scroll to.
|
* @param slot Slot of the question to scroll to.
|
||||||
*/
|
*/
|
||||||
async changePage(page: number, slot?: number): Promise<void> {
|
async changePage(page: number, slot?: number): Promise<void> {
|
||||||
if (slot !== undefined && (this.attempt!.currentpage == -1 || page == this.currentPage)) {
|
if (slot !== undefined && (this.attempt?.currentpage == -1 || page == this.currentPage)) {
|
||||||
// Scrol to a certain question in the current page.
|
// Scrol to a certain question in the current page.
|
||||||
this.scrollToQuestion(slot);
|
this.scrollToQuestion(slot);
|
||||||
|
|
||||||
|
@ -174,7 +174,7 @@ export class AddonModQuizReviewPage implements OnInit {
|
||||||
* @returns Promise resolved when done.
|
* @returns Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected async loadPage(page: number): Promise<void> {
|
protected async loadPage(page: number): Promise<void> {
|
||||||
const data = await AddonModQuiz.getAttemptReview(this.attemptId, { page, cmId: this.quiz!.coursemodule });
|
const data = await AddonModQuiz.getAttemptReview(this.attemptId, { page, cmId: this.quiz?.coursemodule });
|
||||||
|
|
||||||
this.attempt = data.attempt;
|
this.attempt = data.attempt;
|
||||||
this.attempt.currentpage = page;
|
this.attempt.currentpage = page;
|
||||||
|
@ -203,7 +203,7 @@ export class AddonModQuizReviewPage implements OnInit {
|
||||||
*/
|
*/
|
||||||
protected async loadNavigation(): Promise<void> {
|
protected async loadNavigation(): Promise<void> {
|
||||||
// Get all questions in single page to retrieve all the questions.
|
// Get all questions in single page to retrieve all the questions.
|
||||||
const data = await AddonModQuiz.getAttemptReview(this.attemptId, { page: -1, cmId: this.quiz!.coursemodule });
|
const data = await AddonModQuiz.getAttemptReview(this.attemptId, { page: -1, cmId: this.quiz?.coursemodule });
|
||||||
|
|
||||||
this.navigation = data.questions;
|
this.navigation = data.questions;
|
||||||
|
|
||||||
|
@ -260,7 +260,7 @@ export class AddonModQuizReviewPage implements OnInit {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.readableState = AddonModQuiz.getAttemptReadableStateName(this.attempt!.state || '');
|
this.readableState = AddonModQuiz.getAttemptReadableStateName(this.attempt.state ?? '');
|
||||||
|
|
||||||
if (this.attempt.state != AddonModQuizProvider.ATTEMPT_FINISHED) {
|
if (this.attempt.state != AddonModQuizProvider.ATTEMPT_FINISHED) {
|
||||||
return;
|
return;
|
||||||
|
@ -283,7 +283,7 @@ export class AddonModQuizReviewPage implements OnInit {
|
||||||
}
|
}
|
||||||
|
|
||||||
// Treat grade.
|
// Treat grade.
|
||||||
if (this.options!.someoptions.marks >= AddonModQuizProvider.QUESTION_OPTIONS_MARK_AND_MAX &&
|
if (this.options && this.options.someoptions.marks >= AddonModQuizProvider.QUESTION_OPTIONS_MARK_AND_MAX &&
|
||||||
AddonModQuiz.quizHasGrades(this.quiz)) {
|
AddonModQuiz.quizHasGrades(this.quiz)) {
|
||||||
|
|
||||||
if (data.grade === null || data.grade === undefined) {
|
if (data.grade === null || data.grade === undefined) {
|
||||||
|
@ -348,7 +348,7 @@ export class AddonModQuizReviewPage implements OnInit {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
this.changePage(modalData.page!, modalData.slot);
|
this.changePage(modalData.page, modalData.slot);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -241,7 +241,7 @@ export class AddonModQuizPrefetchHandlerService extends CoreCourseActivityPrefet
|
||||||
|
|
||||||
const isLastFinished = !attempts.length || AddonModQuiz.isAttemptFinished(attempts[attempts.length - 1].state);
|
const isLastFinished = !attempts.length || AddonModQuiz.isAttemptFinished(attempts[attempts.length - 1].state);
|
||||||
|
|
||||||
return quiz.attempts === 0 || quiz.attempts! > attempts.length || !isLastFinished;
|
return quiz.attempts === 0 || (quiz.attempts ?? 0) > attempts.length || !isLastFinished;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -444,7 +444,7 @@ export class AddonModQuizPrefetchHandlerService extends CoreCourseActivityPrefet
|
||||||
if (attempt.state == AddonModQuizProvider.ATTEMPT_IN_PROGRESS) {
|
if (attempt.state == AddonModQuizProvider.ATTEMPT_IN_PROGRESS) {
|
||||||
// Get data for each page.
|
// Get data for each page.
|
||||||
promises = promises.concat(pages.map(async (page) => {
|
promises = promises.concat(pages.map(async (page) => {
|
||||||
if (isSequential && page < attempt.currentpage!) {
|
if (isSequential && attempt.currentpage && page < attempt.currentpage) {
|
||||||
// Sequential quiz, cannot get pages before the current one.
|
// Sequential quiz, cannot get pages before the current one.
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
|
@ -43,7 +43,7 @@ export class AddonModQuizPushClickHandlerService implements CorePushNotification
|
||||||
*/
|
*/
|
||||||
async handles(notification: AddonModQuizPushNotificationData): Promise<boolean> {
|
async handles(notification: AddonModQuizPushNotificationData): Promise<boolean> {
|
||||||
return CoreUtils.isTrueOrOne(notification.notif) && notification.moodlecomponent == 'mod_quiz' &&
|
return CoreUtils.isTrueOrOne(notification.notif) && notification.moodlecomponent == 'mod_quiz' &&
|
||||||
this.SUPPORTED_NAMES.indexOf(notification.name!) != -1;
|
this.SUPPORTED_NAMES.indexOf(notification.name ?? '') != -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -358,7 +358,7 @@ export class AddonModQuizHelperProvider {
|
||||||
if (attempt) {
|
if (attempt) {
|
||||||
if (attempt.state != AddonModQuizProvider.ATTEMPT_OVERDUE && !attempt.finishedOffline) {
|
if (attempt.state != AddonModQuizProvider.ATTEMPT_OVERDUE && !attempt.finishedOffline) {
|
||||||
// We're continuing an attempt. Call getAttemptData to validate the preflight data.
|
// We're continuing an attempt. Call getAttemptData to validate the preflight data.
|
||||||
await AddonModQuiz.getAttemptData(attempt.id, attempt.currentpage!, preflightData, modOptions);
|
await AddonModQuiz.getAttemptData(attempt.id, attempt.currentpage ?? 0, preflightData, modOptions);
|
||||||
|
|
||||||
if (offline) {
|
if (offline) {
|
||||||
// Get current page stored in local.
|
// Get current page stored in local.
|
||||||
|
|
|
@ -198,11 +198,11 @@ export class AddonModQuizOfflineProvider {
|
||||||
} else {
|
} else {
|
||||||
entry = {
|
entry = {
|
||||||
quizid: quiz.id,
|
quizid: quiz.id,
|
||||||
userid: attempt.userid!,
|
userid: attempt.userid ?? CoreSites.getCurrentSiteUserId(),
|
||||||
id: attempt.id,
|
id: attempt.id,
|
||||||
courseid: quiz.course,
|
courseid: quiz.course,
|
||||||
timecreated: now,
|
timecreated: now,
|
||||||
attempt: attempt.attempt!,
|
attempt: attempt.attempt ?? 0,
|
||||||
currentpage: attempt.currentpage,
|
currentpage: attempt.currentpage,
|
||||||
timemodified: now,
|
timemodified: now,
|
||||||
finished: finish ? 1 : 0,
|
finished: finish ? 1 : 0,
|
||||||
|
@ -284,10 +284,12 @@ export class AddonModQuizOfflineProvider {
|
||||||
|
|
||||||
if (questions[slot]) {
|
if (questions[slot]) {
|
||||||
if (!questionsWithAnswers[slot]) {
|
if (!questionsWithAnswers[slot]) {
|
||||||
questionsWithAnswers[slot] = questions[slot];
|
questionsWithAnswers[slot] = {
|
||||||
questionsWithAnswers[slot].answers = {};
|
...questions[slot],
|
||||||
|
answers: {},
|
||||||
|
};
|
||||||
}
|
}
|
||||||
questionsWithAnswers[slot].answers![nameWithoutPrefix] = answers[name];
|
questionsWithAnswers[slot].answers[nameWithoutPrefix] = answers[name];
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -295,7 +297,7 @@ export class AddonModQuizOfflineProvider {
|
||||||
await Promise.all(Object.values(questionsWithAnswers).map(async (question) => {
|
await Promise.all(Object.values(questionsWithAnswers).map(async (question) => {
|
||||||
|
|
||||||
const state = await CoreQuestionBehaviourDelegate.determineNewState(
|
const state = await CoreQuestionBehaviourDelegate.determineNewState(
|
||||||
quiz.preferredbehaviour!,
|
quiz.preferredbehaviour ?? '',
|
||||||
AddonModQuizProvider.COMPONENT,
|
AddonModQuizProvider.COMPONENT,
|
||||||
attempt.id,
|
attempt.id,
|
||||||
question,
|
question,
|
||||||
|
@ -317,7 +319,7 @@ export class AddonModQuizOfflineProvider {
|
||||||
AddonModQuizProvider.COMPONENT,
|
AddonModQuizProvider.COMPONENT,
|
||||||
quiz.id,
|
quiz.id,
|
||||||
attempt.id,
|
attempt.id,
|
||||||
attempt.userid!,
|
attempt.userid ?? CoreSites.getCurrentSiteUserId(),
|
||||||
answers,
|
answers,
|
||||||
timeMod,
|
timeMod,
|
||||||
siteId,
|
siteId,
|
||||||
|
@ -332,7 +334,7 @@ export class AddonModQuizOfflineProvider {
|
||||||
AddonModQuizProvider.COMPONENT,
|
AddonModQuizProvider.COMPONENT,
|
||||||
quiz.id,
|
quiz.id,
|
||||||
attempt.id,
|
attempt.id,
|
||||||
attempt.userid!,
|
attempt.userid ?? CoreSites.getCurrentSiteUserId(),
|
||||||
question,
|
question,
|
||||||
newStates[slot],
|
newStates[slot],
|
||||||
siteId,
|
siteId,
|
||||||
|
|
|
@ -305,8 +305,9 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider
|
||||||
|
|
||||||
// Get all the offline attempts for the quiz. It should always be 0 or 1 attempt
|
// Get all the offline attempts for the quiz. It should always be 0 or 1 attempt
|
||||||
const offlineAttempts = await AddonModQuizOffline.getQuizAttempts(quiz.id, siteId);
|
const offlineAttempts = await AddonModQuizOffline.getQuizAttempts(quiz.id, siteId);
|
||||||
|
const offlineAttempt = offlineAttempts.pop();
|
||||||
|
|
||||||
if (!offlineAttempts.length) {
|
if (!offlineAttempt) {
|
||||||
// Nothing to sync, finish.
|
// Nothing to sync, finish.
|
||||||
return this.finishSync(siteId, quiz, courseId, warnings);
|
return this.finishSync(siteId, quiz, courseId, warnings);
|
||||||
}
|
}
|
||||||
|
@ -316,8 +317,6 @@ export class AddonModQuizSyncProvider extends CoreCourseActivitySyncBaseProvider
|
||||||
throw new CoreError(Translate.instant('core.cannotconnect'));
|
throw new CoreError(Translate.instant('core.cannotconnect'));
|
||||||
}
|
}
|
||||||
|
|
||||||
const offlineAttempt = offlineAttempts.pop()!;
|
|
||||||
|
|
||||||
// Now get the list of online attempts to make sure this attempt exists and isn't finished.
|
// Now get the list of online attempts to make sure this attempt exists and isn't finished.
|
||||||
const onlineAttempts = await AddonModQuiz.getUserAttempts(quiz.id, modOptions);
|
const onlineAttempts = await AddonModQuiz.getUserAttempts(quiz.id, modOptions);
|
||||||
|
|
||||||
|
|
|
@ -290,7 +290,7 @@ export class AddonModQuizProvider {
|
||||||
return dueDate * 1000;
|
return dueDate * 1000;
|
||||||
|
|
||||||
case AddonModQuizProvider.ATTEMPT_OVERDUE:
|
case AddonModQuizProvider.ATTEMPT_OVERDUE:
|
||||||
return (dueDate + quiz.graceperiod!) * 1000;
|
return (dueDate + (quiz.graceperiod ?? 0)) * 1000;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
this.logger.warn('Unexpected state when getting due date: ' + attempt.state);
|
this.logger.warn('Unexpected state when getting due date: ' + attempt.state);
|
||||||
|
@ -356,7 +356,7 @@ export class AddonModQuizProvider {
|
||||||
Translate.instant('addon.mod_quiz.statefinished'),
|
Translate.instant('addon.mod_quiz.statefinished'),
|
||||||
Translate.instant(
|
Translate.instant(
|
||||||
'addon.mod_quiz.statefinisheddetails',
|
'addon.mod_quiz.statefinisheddetails',
|
||||||
{ $a: CoreTimeUtils.userDate(attempt.timefinish! * 1000) },
|
{ $a: CoreTimeUtils.userDate((attempt.timefinish ?? 0) * 1000) },
|
||||||
),
|
),
|
||||||
];
|
];
|
||||||
|
|
||||||
|
@ -625,7 +625,7 @@ export class AddonModQuizProvider {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (quiz.questiondecimalpoints == -1) {
|
if (quiz.questiondecimalpoints == -1) {
|
||||||
return quiz.decimalpoints!;
|
return quiz.decimalpoints ?? 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
return quiz.questiondecimalpoints;
|
return quiz.questiondecimalpoints;
|
||||||
|
@ -1780,7 +1780,7 @@ export class AddonModQuizProvider {
|
||||||
* @returns Whether quiz is graded.
|
* @returns Whether quiz is graded.
|
||||||
*/
|
*/
|
||||||
quizHasGrades(quiz: AddonModQuizQuizWSData): boolean {
|
quizHasGrades(quiz: AddonModQuizQuizWSData): boolean {
|
||||||
return quiz.grade! >= 0.000005 && quiz.sumgrades! >= 0.000005;
|
return (quiz.grade ?? 0) >= 0.000005 && (quiz.sumgrades ?? 0) >= 0.000005;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -1802,8 +1802,8 @@ export class AddonModQuizProvider {
|
||||||
|
|
||||||
const rawGradeNum = typeof rawGrade == 'string' ? parseFloat(rawGrade) : rawGrade;
|
const rawGradeNum = typeof rawGrade == 'string' ? parseFloat(rawGrade) : rawGrade;
|
||||||
if (rawGradeNum !== undefined && rawGradeNum !== null && !isNaN(rawGradeNum)) {
|
if (rawGradeNum !== undefined && rawGradeNum !== null && !isNaN(rawGradeNum)) {
|
||||||
if (quiz.sumgrades! >= 0.000005) {
|
if (quiz.sumgrades && quiz.sumgrades >= 0.000005) {
|
||||||
grade = rawGradeNum * quiz.grade! / quiz.sumgrades!;
|
grade = rawGradeNum * (quiz.grade ?? 0) / quiz.sumgrades;
|
||||||
} else {
|
} else {
|
||||||
grade = 0;
|
grade = 0;
|
||||||
}
|
}
|
||||||
|
@ -1816,7 +1816,7 @@ export class AddonModQuizProvider {
|
||||||
if (format === 'question') {
|
if (format === 'question') {
|
||||||
return this.formatGrade(grade, this.getGradeDecimals(quiz));
|
return this.formatGrade(grade, this.getGradeDecimals(quiz));
|
||||||
} else if (format) {
|
} else if (format) {
|
||||||
return this.formatGrade(grade, quiz.decimalpoints!);
|
return this.formatGrade(grade, quiz.decimalpoints ?? 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
return String(grade);
|
return String(grade);
|
||||||
|
|
|
@ -132,5 +132,5 @@ export const CoreQuestionBehaviourDelegate = makeSingleton(CoreQuestionBehaviour
|
||||||
* Answers classified by question slot.
|
* Answers classified by question slot.
|
||||||
*/
|
*/
|
||||||
export type CoreQuestionQuestionWithAnswers = CoreQuestionQuestionParsed & {
|
export type CoreQuestionQuestionWithAnswers = CoreQuestionQuestionParsed & {
|
||||||
answers?: CoreQuestionsAnswers;
|
answers: CoreQuestionsAnswers;
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue