MOBILE-4450 quiz: Calculate in app whether attempt can be reviewed
parent
17bd64a5e0
commit
08797cc9d5
|
@ -234,7 +234,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
|
||||||
this.unsupportedQuestions = AddonModQuiz.getUnsupportedQuestions(types);
|
this.unsupportedQuestions = AddonModQuiz.getUnsupportedQuestions(types);
|
||||||
this.hasSupportedQuestions = !!types.find((type) => type != 'random' && this.unsupportedQuestions.indexOf(type) == -1);
|
this.hasSupportedQuestions = !!types.find((type) => type != 'random' && this.unsupportedQuestions.indexOf(type) == -1);
|
||||||
|
|
||||||
await this.getAttempts(quiz);
|
await this.getAttempts(quiz, this.quizAccessInfo);
|
||||||
|
|
||||||
// Quiz is ready to be shown, move it to the variable that is displayed.
|
// Quiz is ready to be shown, move it to the variable that is displayed.
|
||||||
this.quiz = quiz;
|
this.quiz = quiz;
|
||||||
|
@ -246,7 +246,10 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
|
||||||
* @param quiz Quiz instance.
|
* @param quiz Quiz instance.
|
||||||
* @returns Promise resolved when done.
|
* @returns Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected async getAttempts(quiz: AddonModQuizQuizData): Promise<void> {
|
protected async getAttempts(
|
||||||
|
quiz: AddonModQuizQuizData,
|
||||||
|
accessInfo: AddonModQuizGetQuizAccessInformationWSResponse,
|
||||||
|
): Promise<void> {
|
||||||
// Always get the best grade because it includes the grade to pass.
|
// Always get the best grade because it includes the grade to pass.
|
||||||
this.bestGrade = await AddonModQuiz.getUserBestGrade(quiz.id, { cmId: this.module.id });
|
this.bestGrade = await AddonModQuiz.getUserBestGrade(quiz.id, { cmId: this.module.id });
|
||||||
|
|
||||||
|
@ -256,7 +259,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
|
||||||
// Get attempts.
|
// Get attempts.
|
||||||
const attempts = await AddonModQuiz.getUserAttempts(quiz.id, { cmId: this.module.id });
|
const attempts = await AddonModQuiz.getUserAttempts(quiz.id, { cmId: this.module.id });
|
||||||
|
|
||||||
this.attempts = await this.treatAttempts(quiz, attempts);
|
this.attempts = await this.treatAttempts(quiz, accessInfo, attempts);
|
||||||
|
|
||||||
// Check if user can create/continue attempts.
|
// Check if user can create/continue attempts.
|
||||||
if (this.attempts.length) {
|
if (this.attempts.length) {
|
||||||
|
@ -572,11 +575,13 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
|
||||||
* Treat user attempts.
|
* Treat user attempts.
|
||||||
*
|
*
|
||||||
* @param quiz Quiz data.
|
* @param quiz Quiz data.
|
||||||
|
* @param accessInfo Quiz access information.
|
||||||
* @param attempts The attempts to treat.
|
* @param attempts The attempts to treat.
|
||||||
* @returns Promise resolved when done.
|
* @returns Promise resolved when done.
|
||||||
*/
|
*/
|
||||||
protected async treatAttempts(
|
protected async treatAttempts(
|
||||||
quiz: AddonModQuizQuizData,
|
quiz: AddonModQuizQuizData,
|
||||||
|
accessInfo: AddonModQuizGetQuizAccessInformationWSResponse,
|
||||||
attempts: AddonModQuizAttemptWSData[],
|
attempts: AddonModQuizAttemptWSData[],
|
||||||
): Promise<AddonModQuizAttempt[]> {
|
): Promise<AddonModQuizAttempt[]> {
|
||||||
if (!attempts || !attempts.length) {
|
if (!attempts || !attempts.length) {
|
||||||
|
@ -619,7 +624,7 @@ export class AddonModQuizIndexComponent extends CoreCourseModuleMainActivityComp
|
||||||
attempts.length > 1;
|
attempts.length > 1;
|
||||||
const isLast = index == attempts.length - 1;
|
const isLast = index == attempts.length - 1;
|
||||||
|
|
||||||
return AddonModQuizHelper.setAttemptCalculatedData(quiz, attempt, shouldHighlight, quizGrade, isLast);
|
return AddonModQuizHelper.setAttemptCalculatedData(quiz, accessInfo, attempt, shouldHighlight, quizGrade, isLast);
|
||||||
}));
|
}));
|
||||||
|
|
||||||
return formattedAttempts;
|
return formattedAttempts;
|
||||||
|
|
|
@ -19,6 +19,7 @@ export const ADDON_MOD_QUIZ_FEATURE_NAME = 'CoreCourseModuleDelegate_AddonModQui
|
||||||
export const ADDON_MOD_QUIZ_ATTEMPT_FINISHED_EVENT = 'addon_mod_quiz_attempt_finished';
|
export const ADDON_MOD_QUIZ_ATTEMPT_FINISHED_EVENT = 'addon_mod_quiz_attempt_finished';
|
||||||
|
|
||||||
export const ADDON_MOD_QUIZ_SHOW_TIME_BEFORE_DEADLINE = 3600;
|
export const ADDON_MOD_QUIZ_SHOW_TIME_BEFORE_DEADLINE = 3600;
|
||||||
|
export const ADDON_MOD_QUIZ_IMMEDIATELY_AFTER_PERIOD = 120; // Time considered 'immedately after the attempt', in seconds.
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Possible grade methods for a quiz.
|
* Possible grade methods for a quiz.
|
||||||
|
@ -39,3 +40,13 @@ export const enum AddonModQuizAttemptStates {
|
||||||
FINISHED = 'finished',
|
FINISHED = 'finished',
|
||||||
ABANDONED = 'abandoned',
|
ABANDONED = 'abandoned',
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Bitmask patterns to determine if data should be displayed based on the attempt state.
|
||||||
|
*/
|
||||||
|
export const enum AddonModQuizDisplayOptionsAttemptStates {
|
||||||
|
DURING = 0x10000,
|
||||||
|
IMMEDIATELY_AFTER = 0x01000,
|
||||||
|
LATER_WHILE_OPEN = 0x00100,
|
||||||
|
AFTER_CLOSE = 0x00010,
|
||||||
|
}
|
||||||
|
|
|
@ -102,7 +102,14 @@ export class AddonModQuizAttemptPage implements OnInit {
|
||||||
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,
|
||||||
|
accessInfo,
|
||||||
|
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);
|
||||||
|
|
|
@ -383,6 +383,7 @@ export class AddonModQuizPlayerPage implements OnInit, OnDestroy, CanLeave {
|
||||||
// Get the last attempt. If it's finished, start a new one.
|
// Get the last attempt. If it's finished, start a new one.
|
||||||
this.lastAttempt = await AddonModQuizHelper.setAttemptCalculatedData(
|
this.lastAttempt = await AddonModQuizHelper.setAttemptCalculatedData(
|
||||||
this.quiz,
|
this.quiz,
|
||||||
|
this.quizAccessInfo,
|
||||||
attempts[attempts.length - 1],
|
attempts[attempts.length - 1],
|
||||||
false,
|
false,
|
||||||
undefined,
|
undefined,
|
||||||
|
|
|
@ -33,8 +33,9 @@ import {
|
||||||
AddonModQuizQuizWSData,
|
AddonModQuizQuizWSData,
|
||||||
} from './quiz';
|
} from './quiz';
|
||||||
import { AddonModQuizOffline } from './quiz-offline';
|
import { AddonModQuizOffline } from './quiz-offline';
|
||||||
import { AddonModQuizAttemptStates } from '../constants';
|
import { AddonModQuizAttemptStates, AddonModQuizDisplayOptionsAttemptStates } from '../constants';
|
||||||
import { QuestionDisplayOptionsMarks } from '@features/question/constants';
|
import { QuestionDisplayOptionsMarks } from '@features/question/constants';
|
||||||
|
import { CoreGroups } from '@services/groups';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper service that provides some features for quiz.
|
* Helper service that provides some features for quiz.
|
||||||
|
@ -42,6 +43,84 @@ import { QuestionDisplayOptionsMarks } from '@features/question/constants';
|
||||||
@Injectable({ providedIn: 'root' })
|
@Injectable({ providedIn: 'root' })
|
||||||
export class AddonModQuizHelperProvider {
|
export class AddonModQuizHelperProvider {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if current user can review an attempt.
|
||||||
|
*
|
||||||
|
* @param quiz Quiz.
|
||||||
|
* @param accessInfo Access info.
|
||||||
|
* @param attempt Attempt.
|
||||||
|
* @returns Whether user can review the attempt.
|
||||||
|
*/
|
||||||
|
async canReviewAttempt(
|
||||||
|
quiz: AddonModQuizQuizWSData,
|
||||||
|
accessInfo: AddonModQuizGetQuizAccessInformationWSResponse,
|
||||||
|
attempt: AddonModQuizAttemptWSData,
|
||||||
|
): Promise<boolean> {
|
||||||
|
if (!this.hasReviewCapabilityForAttempt(quiz, accessInfo, attempt)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attempt.userid !== CoreSites.getCurrentSiteUserId()) {
|
||||||
|
return this.canReviewOtherUserAttempt(quiz, accessInfo, attempt);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!AddonModQuiz.isAttemptFinished(attempt.state)) {
|
||||||
|
// Cannot review own unfinished attempts.
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (attempt.preview && accessInfo.canpreview) {
|
||||||
|
// A teacher can always review their own preview no matter the review options settings.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!attempt.preview && accessInfo.canviewreports) {
|
||||||
|
// Users who can see reports should be shown everything, except during preview.
|
||||||
|
// In LMS, the capability 'moodle/grade:viewhidden' is also checked but the app doesn't have this info.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const options = AddonModQuiz.getDisplayOptionsForQuiz(quiz, AddonModQuiz.getAttemptStateDisplayOption(quiz, attempt));
|
||||||
|
|
||||||
|
return options.attempt;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if current user can review another user attempt.
|
||||||
|
*
|
||||||
|
* @param quiz Quiz.
|
||||||
|
* @param accessInfo Access info.
|
||||||
|
* @param attempt Attempt.
|
||||||
|
* @returns Whether user can review the attempt.
|
||||||
|
*/
|
||||||
|
protected async canReviewOtherUserAttempt(
|
||||||
|
quiz: AddonModQuizQuizWSData,
|
||||||
|
accessInfo: AddonModQuizGetQuizAccessInformationWSResponse,
|
||||||
|
attempt: AddonModQuizAttemptWSData,
|
||||||
|
): Promise<boolean> {
|
||||||
|
if (!accessInfo.canviewreports) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
const groupInfo = await CoreGroups.getActivityGroupInfo(quiz.coursemodule);
|
||||||
|
if (groupInfo.canAccessAllGroups || !groupInfo.separateGroups) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the current user and the attempt's user share any group.
|
||||||
|
if (!groupInfo.groups.length) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const attemptUserGroups = await CoreGroups.getUserGroupsInCourse(quiz.course, undefined, attempt.userid);
|
||||||
|
|
||||||
|
return attemptUserGroups.some(attemptUserGroup => groupInfo.groups.find(group => attemptUserGroup.id === group.id));
|
||||||
|
} catch {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate a preflight data or show a modal to input the preflight data if required.
|
* Validate a preflight data or show a modal to input the preflight data if required.
|
||||||
* It calls AddonModQuizProvider.startAttempt if a new attempt is needed.
|
* It calls AddonModQuizProvider.startAttempt if a new attempt is needed.
|
||||||
|
@ -253,10 +332,34 @@ export class AddonModQuizHelperProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if current user has the necessary capabilities to review an attempt.
|
||||||
|
*
|
||||||
|
* @param quiz Quiz.
|
||||||
|
* @param accessInfo Access info.
|
||||||
|
* @param attempt Attempt.
|
||||||
|
* @returns Whether user has the capability.
|
||||||
|
*/
|
||||||
|
hasReviewCapabilityForAttempt(
|
||||||
|
quiz: AddonModQuizQuizWSData,
|
||||||
|
accessInfo: AddonModQuizGetQuizAccessInformationWSResponse,
|
||||||
|
attempt: AddonModQuizAttemptWSData,
|
||||||
|
): boolean {
|
||||||
|
if (accessInfo.canviewreports || accessInfo.canpreview) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const displayOption = AddonModQuiz.getAttemptStateDisplayOption(quiz, attempt);
|
||||||
|
|
||||||
|
return displayOption === AddonModQuizDisplayOptionsAttemptStates.IMMEDIATELY_AFTER ?
|
||||||
|
accessInfo.canattempt : accessInfo.canreviewmyattempts;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Add some calculated data to the attempt.
|
* Add some calculated data to the attempt.
|
||||||
*
|
*
|
||||||
* @param quiz Quiz.
|
* @param quiz Quiz.
|
||||||
|
* @param accessInfo Quiz access info.
|
||||||
* @param attempt Attempt.
|
* @param attempt Attempt.
|
||||||
* @param highlight Whether we should check if attempt should be highlighted.
|
* @param highlight Whether we should check if attempt should be highlighted.
|
||||||
* @param bestGrade Quiz's best grade (formatted). Required if highlight=true.
|
* @param bestGrade Quiz's best grade (formatted). Required if highlight=true.
|
||||||
|
@ -266,6 +369,7 @@ export class AddonModQuizHelperProvider {
|
||||||
*/
|
*/
|
||||||
async setAttemptCalculatedData(
|
async setAttemptCalculatedData(
|
||||||
quiz: AddonModQuizQuizData,
|
quiz: AddonModQuizQuizData,
|
||||||
|
accessInfo: AddonModQuizGetQuizAccessInformationWSResponse,
|
||||||
attempt: AddonModQuizAttemptWSData,
|
attempt: AddonModQuizAttemptWSData,
|
||||||
highlight?: boolean,
|
highlight?: boolean,
|
||||||
bestGrade?: string,
|
bestGrade?: string,
|
||||||
|
@ -301,6 +405,8 @@ export class AddonModQuizHelperProvider {
|
||||||
formattedAttempt.finishedOffline = await AddonModQuiz.isAttemptFinishedOffline(attempt.id, siteId);
|
formattedAttempt.finishedOffline = await AddonModQuiz.isAttemptFinishedOffline(attempt.id, siteId);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
formattedAttempt.canReview = await this.canReviewAttempt(quiz, accessInfo, attempt);
|
||||||
|
|
||||||
return formattedAttempt;
|
return formattedAttempt;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -434,4 +540,5 @@ export type AddonModQuizAttempt = AddonModQuizAttemptWSData & {
|
||||||
readableMark?: string;
|
readableMark?: string;
|
||||||
readableGrade?: string;
|
readableGrade?: string;
|
||||||
highlightGrade?: boolean;
|
highlightGrade?: boolean;
|
||||||
|
canReview?: boolean;
|
||||||
};
|
};
|
||||||
|
|
|
@ -41,12 +41,19 @@ import { AddonModQuizAttempt } from './quiz-helper';
|
||||||
import { AddonModQuizOffline, AddonModQuizQuestionsWithAnswers } from './quiz-offline';
|
import { AddonModQuizOffline, AddonModQuizQuestionsWithAnswers } from './quiz-offline';
|
||||||
import { AddonModQuizAutoSyncData, AddonModQuizSyncProvider } from './quiz-sync';
|
import { AddonModQuizAutoSyncData, AddonModQuizSyncProvider } from './quiz-sync';
|
||||||
import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site';
|
import { CoreSiteWSPreSets } from '@classes/sites/authenticated-site';
|
||||||
import { QUESTION_INVALID_STATE_CLASSES, QUESTION_TODO_STATE_CLASSES } from '@features/question/constants';
|
import {
|
||||||
|
QUESTION_INVALID_STATE_CLASSES,
|
||||||
|
QUESTION_TODO_STATE_CLASSES,
|
||||||
|
QuestionDisplayOptionsMarks,
|
||||||
|
QuestionDisplayOptionsValues,
|
||||||
|
} from '@features/question/constants';
|
||||||
import {
|
import {
|
||||||
ADDON_MOD_QUIZ_ATTEMPT_FINISHED_EVENT,
|
ADDON_MOD_QUIZ_ATTEMPT_FINISHED_EVENT,
|
||||||
AddonModQuizAttemptStates,
|
AddonModQuizAttemptStates,
|
||||||
ADDON_MOD_QUIZ_COMPONENT,
|
ADDON_MOD_QUIZ_COMPONENT,
|
||||||
AddonModQuizGradeMethods,
|
AddonModQuizGradeMethods,
|
||||||
|
AddonModQuizDisplayOptionsAttemptStates,
|
||||||
|
ADDON_MOD_QUIZ_IMMEDIATELY_AFTER_PERIOD,
|
||||||
} from '../constants';
|
} from '../constants';
|
||||||
|
|
||||||
declare module '@singletons/events' {
|
declare module '@singletons/events' {
|
||||||
|
@ -305,6 +312,105 @@ export class AddonModQuizProvider {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get the display option value related to the attempt state.
|
||||||
|
* Equivalent to LMS quiz_attempt_state.
|
||||||
|
*
|
||||||
|
* @param quiz Quiz.
|
||||||
|
* @param attempt Attempt.
|
||||||
|
* @returns Display option value.
|
||||||
|
*/
|
||||||
|
getAttemptStateDisplayOption(
|
||||||
|
quiz: AddonModQuizQuizWSData,
|
||||||
|
attempt: AddonModQuizAttemptWSData,
|
||||||
|
): AddonModQuizDisplayOptionsAttemptStates {
|
||||||
|
if (attempt.state === AddonModQuizAttemptStates.IN_PROGRESS) {
|
||||||
|
return AddonModQuizDisplayOptionsAttemptStates.DURING;
|
||||||
|
} else if (quiz.timeclose && Date.now() >= quiz.timeclose * 1000) {
|
||||||
|
return AddonModQuizDisplayOptionsAttemptStates.AFTER_CLOSE;
|
||||||
|
} else if (Date.now() < ((attempt.timefinish ?? 0) + ADDON_MOD_QUIZ_IMMEDIATELY_AFTER_PERIOD) * 1000) {
|
||||||
|
return AddonModQuizDisplayOptionsAttemptStates.IMMEDIATELY_AFTER;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AddonModQuizDisplayOptionsAttemptStates.LATER_WHILE_OPEN;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Get display options for a certain quiz.
|
||||||
|
* Equivalent to LMS display_options::make_from_quiz.
|
||||||
|
*
|
||||||
|
* @param quiz Quiz.
|
||||||
|
* @param state State.
|
||||||
|
* @returns Display options.
|
||||||
|
*/
|
||||||
|
getDisplayOptionsForQuiz(
|
||||||
|
quiz: AddonModQuizQuizWSData,
|
||||||
|
state: AddonModQuizDisplayOptionsAttemptStates,
|
||||||
|
): AddonModQuizDisplayOptions {
|
||||||
|
const marksOption = this.calculateDisplayOptionValue(
|
||||||
|
quiz.reviewmarks ?? 0,
|
||||||
|
state,
|
||||||
|
QuestionDisplayOptionsMarks.MARK_AND_MAX,
|
||||||
|
QuestionDisplayOptionsMarks.MAX_ONLY,
|
||||||
|
);
|
||||||
|
const feedbackOption = this.calculateDisplayOptionValue(quiz.reviewspecificfeedback ?? 0, state);
|
||||||
|
|
||||||
|
return {
|
||||||
|
attempt: this.calculateDisplayOptionValue(quiz.reviewattempt ?? 0, state, true, false),
|
||||||
|
correctness: this.calculateDisplayOptionValue(quiz.reviewcorrectness ?? 0, state),
|
||||||
|
marks: quiz.reviewmaxmarks !== undefined ?
|
||||||
|
this.calculateDisplayOptionValue<QuestionDisplayOptionsMarks | QuestionDisplayOptionsValues>(
|
||||||
|
quiz.reviewmaxmarks,
|
||||||
|
state,
|
||||||
|
marksOption,
|
||||||
|
QuestionDisplayOptionsValues.HIDDEN,
|
||||||
|
) :
|
||||||
|
marksOption,
|
||||||
|
feedback: feedbackOption,
|
||||||
|
generalfeedback: this.calculateDisplayOptionValue(quiz.reviewgeneralfeedback ?? 0, state),
|
||||||
|
rightanswer: this.calculateDisplayOptionValue(quiz.reviewrightanswer ?? 0, state),
|
||||||
|
overallfeedback: this.calculateDisplayOptionValue(quiz.reviewoverallfeedback ?? 0, state),
|
||||||
|
numpartscorrect: feedbackOption,
|
||||||
|
manualcomment: feedbackOption,
|
||||||
|
markdp: quiz.questiondecimalpoints !== undefined && quiz.questiondecimalpoints !== -1 ?
|
||||||
|
quiz.questiondecimalpoints :
|
||||||
|
(quiz.decimalpoints ?? 0),
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Calculate the value for a certain display option.
|
||||||
|
*
|
||||||
|
* @param setting Setting value related to the option.
|
||||||
|
* @param state Display options state.
|
||||||
|
* @param whenSet Value to return if setting is set.
|
||||||
|
* @param whenNotSet Value to return if setting is not set.
|
||||||
|
* @returns Display option.
|
||||||
|
*/
|
||||||
|
protected calculateDisplayOptionValue<T = AddonModQuizDisplayOptionValue>(
|
||||||
|
setting: number,
|
||||||
|
state: AddonModQuizDisplayOptionsAttemptStates,
|
||||||
|
whenSet: T,
|
||||||
|
whenNotSet: T,
|
||||||
|
): T;
|
||||||
|
protected calculateDisplayOptionValue(
|
||||||
|
setting: number,
|
||||||
|
state: AddonModQuizDisplayOptionsAttemptStates,
|
||||||
|
): QuestionDisplayOptionsValues;
|
||||||
|
protected calculateDisplayOptionValue(
|
||||||
|
setting: number,
|
||||||
|
state: AddonModQuizDisplayOptionsAttemptStates,
|
||||||
|
whenSet: AddonModQuizDisplayOptionValue = QuestionDisplayOptionsValues.VISIBLE,
|
||||||
|
whenNotSet: AddonModQuizDisplayOptionValue = QuestionDisplayOptionsValues.HIDDEN,
|
||||||
|
): AddonModQuizDisplayOptionValue {
|
||||||
|
// eslint-disable-next-line no-bitwise
|
||||||
|
if (setting & state) {
|
||||||
|
return whenSet;
|
||||||
|
}
|
||||||
|
|
||||||
|
return whenNotSet;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Turn attempt's state into a readable state, including some extra data depending on the state.
|
* Turn attempt's state into a readable state, including some extra data depending on the state.
|
||||||
*
|
*
|
||||||
|
@ -2189,6 +2295,7 @@ export type AddonModQuizQuizWSData = {
|
||||||
questiondecimalpoints?: number; // Number of decimal points to use when displaying question grades.
|
questiondecimalpoints?: number; // Number of decimal points to use when displaying question grades.
|
||||||
reviewattempt?: number; // Whether users are allowed to review their quiz attempts at various times.
|
reviewattempt?: number; // Whether users are allowed to review their quiz attempts at various times.
|
||||||
reviewcorrectness?: number; // Whether users are allowed to review their quiz attempts at various times.
|
reviewcorrectness?: number; // Whether users are allowed to review their quiz attempts at various times.
|
||||||
|
reviewmaxmarks?: number; // @since 4.3. Whether users are allowed to review their quiz attempts at various times.
|
||||||
reviewmarks?: number; // Whether users are allowed to review their quiz attempts at various times.
|
reviewmarks?: number; // Whether users are allowed to review their quiz attempts at various times.
|
||||||
reviewspecificfeedback?: number; // Whether users are allowed to review their quiz attempts at various times.
|
reviewspecificfeedback?: number; // Whether users are allowed to review their quiz attempts at various times.
|
||||||
reviewgeneralfeedback?: number; // Whether users are allowed to review their quiz attempts at various times.
|
reviewgeneralfeedback?: number; // Whether users are allowed to review their quiz attempts at various times.
|
||||||
|
@ -2383,3 +2490,24 @@ export type AddonModQuizAttemptFinishedData = {
|
||||||
attemptId: number;
|
attemptId: number;
|
||||||
synced: boolean;
|
synced: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quiz display option value.
|
||||||
|
*/
|
||||||
|
export type AddonModQuizDisplayOptionValue = QuestionDisplayOptionsMarks | QuestionDisplayOptionsValues | boolean;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Quiz display options, it can be used to determine which options to display.
|
||||||
|
*/
|
||||||
|
export type AddonModQuizDisplayOptions = {
|
||||||
|
attempt: boolean;
|
||||||
|
correctness: QuestionDisplayOptionsValues;
|
||||||
|
marks: QuestionDisplayOptionsMarks | QuestionDisplayOptionsValues;
|
||||||
|
feedback: QuestionDisplayOptionsValues;
|
||||||
|
generalfeedback: QuestionDisplayOptionsValues;
|
||||||
|
rightanswer: QuestionDisplayOptionsValues;
|
||||||
|
overallfeedback: QuestionDisplayOptionsValues;
|
||||||
|
numpartscorrect: QuestionDisplayOptionsValues;
|
||||||
|
manualcomment: QuestionDisplayOptionsValues;
|
||||||
|
markdp: number;
|
||||||
|
};
|
||||||
|
|
|
@ -20,7 +20,20 @@ export const QUESTION_FINISHED_STATE_CLASSES = ['complete'] as const;
|
||||||
export const QUESTION_GAVE_UP_STATE_CLASSES = ['notanswered'] 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 QUESTION_GRADED_STATE_CLASSES = ['complete', 'incorrect', 'partiallycorrect', 'correct'] as const;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible values to display marks in a question.
|
||||||
|
*/
|
||||||
export const enum QuestionDisplayOptionsMarks {
|
export const enum QuestionDisplayOptionsMarks {
|
||||||
MAX_ONLY = 1,
|
MAX_ONLY = 1,
|
||||||
MARK_AND_MAX = 2,
|
MARK_AND_MAX = 2,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Possible values that most of the display options take.
|
||||||
|
*/
|
||||||
|
export const enum QuestionDisplayOptionsValues {
|
||||||
|
SHOW_ALL = -1,
|
||||||
|
HIDDEN = 0,
|
||||||
|
VISIBLE = 1,
|
||||||
|
EDITABLE = 2,
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in New Issue