MOBILE-2272 quiz: Improve calculate essay state in offline
parent
65909073fa
commit
07af88d5d9
|
@ -142,7 +142,7 @@ export class AddonQbehaviourDeferredFeedbackHandler implements CoreQuestionBehav
|
|||
} else if (complete > 0) {
|
||||
newState = 'complete';
|
||||
} else {
|
||||
const gradable = this.questionDelegate.isGradableResponse(question, newBasicAnswers);
|
||||
const gradable = this.questionDelegate.isGradableResponse(question, newBasicAnswers, component, componentId);
|
||||
if (gradable < 0) {
|
||||
newState = 'cannotdeterminestatus';
|
||||
} else if (gradable > 0) {
|
||||
|
|
|
@ -17,32 +17,7 @@ import { Injectable } from '@angular/core';
|
|||
import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
|
||||
import { CoreQuestionDelegate } from '@core/question/providers/delegate';
|
||||
import { CoreQuestionProvider, CoreQuestionState } from '@core/question/providers/question';
|
||||
|
||||
/**
|
||||
* Check if a response is complete.
|
||||
*
|
||||
* @param question The question.
|
||||
* @param answers Object with the question answers (without prefix).
|
||||
* @param component The component the question is related to.
|
||||
* @param componentId Component ID.
|
||||
* @return 1 if complete, 0 if not complete, -1 if cannot determine.
|
||||
*/
|
||||
export type isCompleteResponseFunction = (question: any, answers: any, component: string, componentId: string | number) => number;
|
||||
|
||||
/**
|
||||
* Check if two responses are the same.
|
||||
*
|
||||
* @param question Question.
|
||||
* @param prevAnswers Object with the previous question answers.
|
||||
* @param prevBasicAnswers Object with the previous basic" answers (without sequencecheck, certainty, ...).
|
||||
* @param newAnswers Object with the new question answers.
|
||||
* @param newBasicAnswers Object with the previous basic" answers (without sequencecheck, certainty, ...).
|
||||
* @param component The component the question is related to.
|
||||
* @param componentId Component ID.
|
||||
* @return Whether they're the same.
|
||||
*/
|
||||
export type isSameResponseFunction = (question: any, prevAnswers: any, prevBasicAnswers: any, newAnswers: any,
|
||||
newBasicAnswers: any, component: string, componentId: string | number) => boolean;
|
||||
import { AddonQbehaviourDeferredFeedbackHandler } from '@addon/qbehaviour/deferredfeedback/providers/handler';
|
||||
|
||||
/**
|
||||
* Handler to support manual graded question behaviour.
|
||||
|
@ -52,7 +27,9 @@ export class AddonQbehaviourManualGradedHandler implements CoreQuestionBehaviour
|
|||
name = 'AddonQbehaviourManualGraded';
|
||||
type = 'manualgraded';
|
||||
|
||||
constructor(private questionDelegate: CoreQuestionDelegate, private questionProvider: CoreQuestionProvider) {
|
||||
constructor(protected questionDelegate: CoreQuestionDelegate,
|
||||
protected questionProvider: CoreQuestionProvider,
|
||||
protected deferredFeedbackHandler: AddonQbehaviourDeferredFeedbackHandler) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
|
@ -68,84 +45,8 @@ export class AddonQbehaviourManualGradedHandler implements CoreQuestionBehaviour
|
|||
*/
|
||||
determineNewState(component: string, attemptId: number, question: any, componentId: string | number, siteId?: string)
|
||||
: CoreQuestionState | Promise<CoreQuestionState> {
|
||||
return this.determineNewStateManualGraded(component, attemptId, question, componentId, siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine a question new state based on its answer(s) for manual graded question behaviour.
|
||||
*
|
||||
* @param component Component the question belongs to.
|
||||
* @param attemptId Attempt ID the question belongs to.
|
||||
* @param question The question.
|
||||
* @param componentId Component ID.
|
||||
* @param siteId Site ID. If not defined, current site.
|
||||
* @param isCompleteFn Function to override the default isCompleteResponse check.
|
||||
* @param isSameFn Function to override the default isSameResponse check.
|
||||
* @return Promise resolved with state.
|
||||
*/
|
||||
async determineNewStateManualGraded(component: string, attemptId: number, question: any, componentId: string | number,
|
||||
siteId?: string, isCompleteFn?: isCompleteResponseFunction, isSameFn?: isSameResponseFunction)
|
||||
: Promise<CoreQuestionState> {
|
||||
|
||||
// Check if we have local data for the question.
|
||||
let dbQuestion;
|
||||
try {
|
||||
dbQuestion = await this.questionProvider.getQuestion(component, attemptId, question.slot, siteId);
|
||||
} catch (error) {
|
||||
// No entry found, use the original data.
|
||||
dbQuestion = question;
|
||||
}
|
||||
|
||||
const state = this.questionProvider.getState(dbQuestion.state);
|
||||
|
||||
if (state.finished || !state.active) {
|
||||
// Question is finished, it cannot change.
|
||||
return state;
|
||||
}
|
||||
|
||||
const newBasicAnswers = this.questionProvider.getBasicAnswers(question.answers);
|
||||
|
||||
if (dbQuestion.state) {
|
||||
// Question already has a state stored. Check if answer has changed.
|
||||
let prevAnswers = await this.questionProvider.getQuestionAnswers(component, attemptId, question.slot, false, siteId);
|
||||
|
||||
prevAnswers = this.questionProvider.convertAnswersArrayToObject(prevAnswers, true);
|
||||
const prevBasicAnswers = this.questionProvider.getBasicAnswers(prevAnswers);
|
||||
|
||||
// If answers haven't changed the state is the same.
|
||||
if (isSameFn) {
|
||||
if (isSameFn(question, prevAnswers, prevBasicAnswers, question.answers, newBasicAnswers,
|
||||
component, componentId)) {
|
||||
return state;
|
||||
}
|
||||
} else {
|
||||
if (this.questionDelegate.isSameResponse(question, prevBasicAnswers, newBasicAnswers, component, componentId)) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Check if the response is complete and calculate the new state.
|
||||
let complete: number;
|
||||
let newState: string;
|
||||
|
||||
if (isCompleteFn) {
|
||||
// Pass all the answers since some behaviours might need the extra data.
|
||||
complete = isCompleteFn(question, question.answers, component, componentId);
|
||||
} else {
|
||||
// Only pass the basic answers since questions should be independent of extra data.
|
||||
complete = this.questionDelegate.isCompleteResponse(question, newBasicAnswers, component, componentId);
|
||||
}
|
||||
|
||||
if (complete < 0) {
|
||||
newState = 'cannotdeterminestatus';
|
||||
} else if (complete > 0) {
|
||||
newState = 'complete';
|
||||
} else {
|
||||
newState = 'todo';
|
||||
}
|
||||
|
||||
return this.questionProvider.getState(newState);
|
||||
// Same implementation as the deferred feedback. Use that function instead of replicating it.
|
||||
return this.deferredFeedbackHandler.determineNewStateDeferred(component, attemptId, question, componentId, siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -76,7 +76,7 @@ export class AddonQtypeCalculatedHandler implements CoreQuestionHandler {
|
|||
* @return 1 if complete, 0 if not complete, -1 if cannot determine.
|
||||
*/
|
||||
isCompleteResponse(question: any, answers: any, component: string, componentId: string | number): number {
|
||||
if (!this.isGradableResponse(question, answers)) {
|
||||
if (!this.isGradableResponse(question, answers, component, componentId)) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -129,9 +129,11 @@ export class AddonQtypeCalculatedHandler implements CoreQuestionHandler {
|
|||
*
|
||||
* @param question The question.
|
||||
* @param answers Object with the question answers (without prefix).
|
||||
* @param component The component the question is related to.
|
||||
* @param componentId Component ID.
|
||||
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
|
||||
*/
|
||||
isGradableResponse(question: any, answers: any): number {
|
||||
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
|
||||
return this.isValidValue(answers['answer']) ? 1 : 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -70,9 +70,11 @@ export class AddonQtypeCalculatedMultiHandler implements CoreQuestionHandler {
|
|||
*
|
||||
* @param question The question.
|
||||
* @param answers Object with the question answers (without prefix).
|
||||
* @param component The component the question is related to.
|
||||
* @param componentId Component ID.
|
||||
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
|
||||
*/
|
||||
isGradableResponse(question: any, answers: any): number {
|
||||
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
|
||||
// This question type depends on multichoice.
|
||||
return this.multichoiceHandler.isGradableResponseSingle(answers);
|
||||
}
|
||||
|
|
|
@ -70,11 +70,13 @@ export class AddonQtypeCalculatedSimpleHandler implements CoreQuestionHandler {
|
|||
*
|
||||
* @param question The question.
|
||||
* @param answers Object with the question answers (without prefix).
|
||||
* @param component The component the question is related to.
|
||||
* @param componentId Component ID.
|
||||
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
|
||||
*/
|
||||
isGradableResponse(question: any, answers: any): number {
|
||||
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
|
||||
// This question type depends on calculated.
|
||||
return this.calculatedHandler.isGradableResponse(question, answers);
|
||||
return this.calculatedHandler.isGradableResponse(question, answers, component, componentId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -93,9 +93,11 @@ export class AddonQtypeDdImageOrTextHandler implements CoreQuestionHandler {
|
|||
*
|
||||
* @param question The question.
|
||||
* @param answers Object with the question answers (without prefix).
|
||||
* @param component The component the question is related to.
|
||||
* @param componentId Component ID.
|
||||
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
|
||||
*/
|
||||
isGradableResponse(question: any, answers: any): number {
|
||||
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
|
||||
for (const name in answers) {
|
||||
const value = answers[name];
|
||||
if (value && value !== '0') {
|
||||
|
|
|
@ -92,10 +92,12 @@ export class AddonQtypeDdMarkerHandler implements CoreQuestionHandler {
|
|||
*
|
||||
* @param question The question.
|
||||
* @param answers Object with the question answers (without prefix).
|
||||
* @param component The component the question is related to.
|
||||
* @param componentId Component ID.
|
||||
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
|
||||
*/
|
||||
isGradableResponse(question: any, answers: any): number {
|
||||
return this.isCompleteResponse(question, answers, null, null);
|
||||
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
|
||||
return this.isCompleteResponse(question, answers, component, componentId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -91,9 +91,11 @@ export class AddonQtypeDdwtosHandler implements CoreQuestionHandler {
|
|||
*
|
||||
* @param question The question.
|
||||
* @param answers Object with the question answers (without prefix).
|
||||
* @param component The component the question is related to.
|
||||
* @param componentId Component ID.
|
||||
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
|
||||
*/
|
||||
isGradableResponse(question: any, answers: any): number {
|
||||
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
|
||||
for (const name in answers) {
|
||||
const value = answers[name];
|
||||
if (value && value !== '0') {
|
||||
|
|
|
@ -162,11 +162,11 @@ export class AddonQtypeEssayHandler implements CoreQuestionHandler {
|
|||
const attachments = CoreFileSession.instance.getFiles(component, questionComponentId);
|
||||
|
||||
if (!allowedOptions.text) {
|
||||
return attachments && attachments.length > 0 ? 1 : 0;
|
||||
return attachments && attachments.length >= Number(question.displayoptions.attachmentsrequired) ? 1 : 0;
|
||||
}
|
||||
|
||||
return (hasTextAnswer || question.displayoptions.responserequired == '0') &&
|
||||
((attachments && attachments.length > 0) || question.displayoptions.attachmentsrequired == '0') ? 1 : 0;
|
||||
(attachments && attachments.length > Number(question.displayoptions.attachmentsrequired)) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -184,10 +184,20 @@ export class AddonQtypeEssayHandler implements CoreQuestionHandler {
|
|||
*
|
||||
* @param question The question.
|
||||
* @param answers Object with the question answers (without prefix).
|
||||
* @param component The component the question is related to.
|
||||
* @param componentId Component ID.
|
||||
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
|
||||
*/
|
||||
isGradableResponse(question: any, answers: any): number {
|
||||
return 0;
|
||||
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
|
||||
if (typeof question.responsefileareas == 'undefined') {
|
||||
return -1;
|
||||
}
|
||||
|
||||
const questionComponentId = CoreQuestion.instance.getQuestionComponentId(question, componentId);
|
||||
const attachments = CoreFileSession.instance.getFiles(component, questionComponentId);
|
||||
|
||||
// Determine if the given response has online text or attachments.
|
||||
return (answers['answer'] && answers['answer'] !== '') || (attachments && attachments.length > 0) ? 1 : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -92,9 +92,11 @@ export class AddonQtypeGapSelectHandler implements CoreQuestionHandler {
|
|||
*
|
||||
* @param question The question.
|
||||
* @param answers Object with the question answers (without prefix).
|
||||
* @param component The component the question is related to.
|
||||
* @param componentId Component ID.
|
||||
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
|
||||
*/
|
||||
isGradableResponse(question: any, answers: any): number {
|
||||
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
|
||||
// We should always get a value for each select so we can assume we receive all the possible answers.
|
||||
for (const name in answers) {
|
||||
const value = answers[name];
|
||||
|
|
|
@ -92,9 +92,11 @@ export class AddonQtypeMatchHandler implements CoreQuestionHandler {
|
|||
*
|
||||
* @param question The question.
|
||||
* @param answers Object with the question answers (without prefix).
|
||||
* @param component The component the question is related to.
|
||||
* @param componentId Component ID.
|
||||
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
|
||||
*/
|
||||
isGradableResponse(question: any, answers: any): number {
|
||||
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
|
||||
// We should always get a value for each select so we can assume we receive all the possible answers.
|
||||
for (const name in answers) {
|
||||
const value = answers[name];
|
||||
|
|
|
@ -94,9 +94,11 @@ export class AddonQtypeMultiAnswerHandler implements CoreQuestionHandler {
|
|||
*
|
||||
* @param question The question.
|
||||
* @param answers Object with the question answers (without prefix).
|
||||
* @param component The component the question is related to.
|
||||
* @param componentId Component ID.
|
||||
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
|
||||
*/
|
||||
isGradableResponse(question: any, answers: any): number {
|
||||
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
|
||||
// We should always get a value for each select so we can assume we receive all the possible answers.
|
||||
for (const name in answers) {
|
||||
const value = answers[name];
|
||||
|
|
|
@ -97,10 +97,12 @@ export class AddonQtypeMultichoiceHandler implements CoreQuestionHandler {
|
|||
*
|
||||
* @param question The question.
|
||||
* @param answers Object with the question answers (without prefix).
|
||||
* @param component The component the question is related to.
|
||||
* @param componentId Component ID.
|
||||
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
|
||||
*/
|
||||
isGradableResponse(question: any, answers: any): number {
|
||||
return this.isCompleteResponse(question, answers, null, null);
|
||||
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
|
||||
return this.isCompleteResponse(question, answers, component, componentId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -70,11 +70,13 @@ export class AddonQtypeRandomSaMatchHandler implements CoreQuestionHandler {
|
|||
*
|
||||
* @param question The question.
|
||||
* @param answers Object with the question answers (without prefix).
|
||||
* @param component The component the question is related to.
|
||||
* @param componentId Component ID.
|
||||
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
|
||||
*/
|
||||
isGradableResponse(question: any, answers: any): number {
|
||||
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
|
||||
// This question behaves like a match question.
|
||||
return this.matchHandler.isGradableResponse(question, answers);
|
||||
return this.matchHandler.isGradableResponse(question, answers, component, componentId);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
@ -68,9 +68,11 @@ export class AddonQtypeShortAnswerHandler implements CoreQuestionHandler {
|
|||
*
|
||||
* @param question The question.
|
||||
* @param answers Object with the question answers (without prefix).
|
||||
* @param component The component the question is related to.
|
||||
* @param componentId Component ID.
|
||||
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
|
||||
*/
|
||||
isGradableResponse(question: any, answers: any): number {
|
||||
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
|
||||
return this.isCompleteResponse(question, answers, null, null);
|
||||
}
|
||||
|
||||
|
|
|
@ -69,9 +69,11 @@ export class AddonQtypeTrueFalseHandler implements CoreQuestionHandler {
|
|||
*
|
||||
* @param question The question.
|
||||
* @param answers Object with the question answers (without prefix).
|
||||
* @param component The component the question is related to.
|
||||
* @param componentId Component ID.
|
||||
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
|
||||
*/
|
||||
isGradableResponse(question: any, answers: any): number {
|
||||
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
|
||||
return this.isCompleteResponse(question, answers, null, null);
|
||||
}
|
||||
|
||||
|
|
|
@ -1946,7 +1946,7 @@
|
|||
"core.question.complete": "Complete",
|
||||
"core.question.correct": "Correct",
|
||||
"core.question.errorattachmentsnotsupportedinsite": "Your site doesn't support attaching files to answers yet.",
|
||||
"core.question.errorinlinefilesnotsupportedinsite": "Your site doesn't support editing inline files yet.",
|
||||
"core.question.errorembeddedfilesnotsupportedinsite": "Your site doesn't support editing embedded files yet.",
|
||||
"core.question.errorquestionnotsupported": "This question type is not supported by the app: {{$a}}.",
|
||||
"core.question.feedback": "Feedback",
|
||||
"core.question.howtodraganddrop": "Tap to select then tap to drop.",
|
||||
|
|
|
@ -93,9 +93,11 @@ export class CoreQuestionBaseHandler implements CoreQuestionHandler {
|
|||
*
|
||||
* @param question The question.
|
||||
* @param answers Object with the question answers (without prefix).
|
||||
* @param component The component the question is related to.
|
||||
* @param componentId Component ID.
|
||||
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
|
||||
*/
|
||||
isGradableResponse(question: any, answers: any): number {
|
||||
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
|
||||
return -1;
|
||||
}
|
||||
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
"complete": "Complete",
|
||||
"correct": "Correct",
|
||||
"errorattachmentsnotsupportedinsite": "Your site doesn't support attaching files to answers yet.",
|
||||
"errorinlinefilesnotsupportedinsite": "Your site doesn't support editing inline files yet.",
|
||||
"errorembeddedfilesnotsupportedinsite": "Your site doesn't support editing embedded files yet.",
|
||||
"errorquestionnotsupported": "This question type is not supported by the app: {{$a}}.",
|
||||
"feedback": "Feedback",
|
||||
"howtodraganddrop": "Tap to select then tap to drop.",
|
||||
|
|
|
@ -74,9 +74,11 @@ export interface CoreQuestionHandler extends CoreDelegateHandler {
|
|||
*
|
||||
* @param question The question.
|
||||
* @param answers Object with the question answers (without prefix).
|
||||
* @param component The component the question is related to.
|
||||
* @param componentId Component ID.
|
||||
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
|
||||
*/
|
||||
isGradableResponse?(question: any, answers: any): number;
|
||||
isGradableResponse?(question: any, answers: any, component: string, componentId: string | number): number;
|
||||
|
||||
/**
|
||||
* Check if two responses are the same.
|
||||
|
@ -251,12 +253,14 @@ export class CoreQuestionDelegate extends CoreDelegate {
|
|||
*
|
||||
* @param question The question.
|
||||
* @param answers Object with the question answers (without prefix).
|
||||
* @param component The component the question is related to.
|
||||
* @param componentId Component ID.
|
||||
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
|
||||
*/
|
||||
isGradableResponse(question: any, answers: any): number {
|
||||
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
|
||||
const type = this.getTypeName(question);
|
||||
|
||||
return this.executeFunctionOnEnabled(type, 'isGradableResponse', [question, answers]);
|
||||
return this.executeFunctionOnEnabled(type, 'isGradableResponse', [question, answers, component, componentId]);
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
Loading…
Reference in New Issue