MOBILE-4450 quiz: Calculate in app whether attempt can be reviewed
This commit is contained in:
		
							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…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user