Skip to content

Commit 07af88d

Browse files
committed
MOBILE-2272 quiz: Improve calculate essay state in offline
1 parent 6590907 commit 07af88d

File tree

20 files changed

+77
-134
lines changed

20 files changed

+77
-134
lines changed

src/addon/qbehaviour/deferredfeedback/providers/handler.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -142,7 +142,7 @@ export class AddonQbehaviourDeferredFeedbackHandler implements CoreQuestionBehav
142142
} else if (complete > 0) {
143143
newState = 'complete';
144144
} else {
145-
const gradable = this.questionDelegate.isGradableResponse(question, newBasicAnswers);
145+
const gradable = this.questionDelegate.isGradableResponse(question, newBasicAnswers, component, componentId);
146146
if (gradable < 0) {
147147
newState = 'cannotdeterminestatus';
148148
} else if (gradable > 0) {

src/addon/qbehaviour/manualgraded/providers/handler.ts

Lines changed: 6 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -17,32 +17,7 @@ import { Injectable } from '@angular/core';
1717
import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
1818
import { CoreQuestionDelegate } from '@core/question/providers/delegate';
1919
import { CoreQuestionProvider, CoreQuestionState } from '@core/question/providers/question';
20-
21-
/**
22-
* Check if a response is complete.
23-
*
24-
* @param question The question.
25-
* @param answers Object with the question answers (without prefix).
26-
* @param component The component the question is related to.
27-
* @param componentId Component ID.
28-
* @return 1 if complete, 0 if not complete, -1 if cannot determine.
29-
*/
30-
export type isCompleteResponseFunction = (question: any, answers: any, component: string, componentId: string | number) => number;
31-
32-
/**
33-
* Check if two responses are the same.
34-
*
35-
* @param question Question.
36-
* @param prevAnswers Object with the previous question answers.
37-
* @param prevBasicAnswers Object with the previous basic" answers (without sequencecheck, certainty, ...).
38-
* @param newAnswers Object with the new question answers.
39-
* @param newBasicAnswers Object with the previous basic" answers (without sequencecheck, certainty, ...).
40-
* @param component The component the question is related to.
41-
* @param componentId Component ID.
42-
* @return Whether they're the same.
43-
*/
44-
export type isSameResponseFunction = (question: any, prevAnswers: any, prevBasicAnswers: any, newAnswers: any,
45-
newBasicAnswers: any, component: string, componentId: string | number) => boolean;
20+
import { AddonQbehaviourDeferredFeedbackHandler } from '@addon/qbehaviour/deferredfeedback/providers/handler';
4621

4722
/**
4823
* Handler to support manual graded question behaviour.
@@ -52,7 +27,9 @@ export class AddonQbehaviourManualGradedHandler implements CoreQuestionBehaviour
5227
name = 'AddonQbehaviourManualGraded';
5328
type = 'manualgraded';
5429

55-
constructor(private questionDelegate: CoreQuestionDelegate, private questionProvider: CoreQuestionProvider) {
30+
constructor(protected questionDelegate: CoreQuestionDelegate,
31+
protected questionProvider: CoreQuestionProvider,
32+
protected deferredFeedbackHandler: AddonQbehaviourDeferredFeedbackHandler) {
5633
// Nothing to do.
5734
}
5835

@@ -68,84 +45,8 @@ export class AddonQbehaviourManualGradedHandler implements CoreQuestionBehaviour
6845
*/
6946
determineNewState(component: string, attemptId: number, question: any, componentId: string | number, siteId?: string)
7047
: CoreQuestionState | Promise<CoreQuestionState> {
71-
return this.determineNewStateManualGraded(component, attemptId, question, componentId, siteId);
72-
}
73-
74-
/**
75-
* Determine a question new state based on its answer(s) for manual graded question behaviour.
76-
*
77-
* @param component Component the question belongs to.
78-
* @param attemptId Attempt ID the question belongs to.
79-
* @param question The question.
80-
* @param componentId Component ID.
81-
* @param siteId Site ID. If not defined, current site.
82-
* @param isCompleteFn Function to override the default isCompleteResponse check.
83-
* @param isSameFn Function to override the default isSameResponse check.
84-
* @return Promise resolved with state.
85-
*/
86-
async determineNewStateManualGraded(component: string, attemptId: number, question: any, componentId: string | number,
87-
siteId?: string, isCompleteFn?: isCompleteResponseFunction, isSameFn?: isSameResponseFunction)
88-
: Promise<CoreQuestionState> {
89-
90-
// Check if we have local data for the question.
91-
let dbQuestion;
92-
try {
93-
dbQuestion = await this.questionProvider.getQuestion(component, attemptId, question.slot, siteId);
94-
} catch (error) {
95-
// No entry found, use the original data.
96-
dbQuestion = question;
97-
}
98-
99-
const state = this.questionProvider.getState(dbQuestion.state);
100-
101-
if (state.finished || !state.active) {
102-
// Question is finished, it cannot change.
103-
return state;
104-
}
105-
106-
const newBasicAnswers = this.questionProvider.getBasicAnswers(question.answers);
107-
108-
if (dbQuestion.state) {
109-
// Question already has a state stored. Check if answer has changed.
110-
let prevAnswers = await this.questionProvider.getQuestionAnswers(component, attemptId, question.slot, false, siteId);
111-
112-
prevAnswers = this.questionProvider.convertAnswersArrayToObject(prevAnswers, true);
113-
const prevBasicAnswers = this.questionProvider.getBasicAnswers(prevAnswers);
114-
115-
// If answers haven't changed the state is the same.
116-
if (isSameFn) {
117-
if (isSameFn(question, prevAnswers, prevBasicAnswers, question.answers, newBasicAnswers,
118-
component, componentId)) {
119-
return state;
120-
}
121-
} else {
122-
if (this.questionDelegate.isSameResponse(question, prevBasicAnswers, newBasicAnswers, component, componentId)) {
123-
return state;
124-
}
125-
}
126-
}
127-
128-
// Check if the response is complete and calculate the new state.
129-
let complete: number;
130-
let newState: string;
131-
132-
if (isCompleteFn) {
133-
// Pass all the answers since some behaviours might need the extra data.
134-
complete = isCompleteFn(question, question.answers, component, componentId);
135-
} else {
136-
// Only pass the basic answers since questions should be independent of extra data.
137-
complete = this.questionDelegate.isCompleteResponse(question, newBasicAnswers, component, componentId);
138-
}
139-
140-
if (complete < 0) {
141-
newState = 'cannotdeterminestatus';
142-
} else if (complete > 0) {
143-
newState = 'complete';
144-
} else {
145-
newState = 'todo';
146-
}
147-
148-
return this.questionProvider.getState(newState);
48+
// Same implementation as the deferred feedback. Use that function instead of replicating it.
49+
return this.deferredFeedbackHandler.determineNewStateDeferred(component, attemptId, question, componentId, siteId);
14950
}
15051

15152
/**

src/addon/qtype/calculated/providers/handler.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -76,7 +76,7 @@ export class AddonQtypeCalculatedHandler implements CoreQuestionHandler {
7676
* @return 1 if complete, 0 if not complete, -1 if cannot determine.
7777
*/
7878
isCompleteResponse(question: any, answers: any, component: string, componentId: string | number): number {
79-
if (!this.isGradableResponse(question, answers)) {
79+
if (!this.isGradableResponse(question, answers, component, componentId)) {
8080
return 0;
8181
}
8282

@@ -129,9 +129,11 @@ export class AddonQtypeCalculatedHandler implements CoreQuestionHandler {
129129
*
130130
* @param question The question.
131131
* @param answers Object with the question answers (without prefix).
132+
* @param component The component the question is related to.
133+
* @param componentId Component ID.
132134
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
133135
*/
134-
isGradableResponse(question: any, answers: any): number {
136+
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
135137
return this.isValidValue(answers['answer']) ? 1 : 0;
136138
}
137139

src/addon/qtype/calculatedmulti/providers/handler.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -70,9 +70,11 @@ export class AddonQtypeCalculatedMultiHandler implements CoreQuestionHandler {
7070
*
7171
* @param question The question.
7272
* @param answers Object with the question answers (without prefix).
73+
* @param component The component the question is related to.
74+
* @param componentId Component ID.
7375
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
7476
*/
75-
isGradableResponse(question: any, answers: any): number {
77+
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
7678
// This question type depends on multichoice.
7779
return this.multichoiceHandler.isGradableResponseSingle(answers);
7880
}

src/addon/qtype/calculatedsimple/providers/handler.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -70,11 +70,13 @@ export class AddonQtypeCalculatedSimpleHandler implements CoreQuestionHandler {
7070
*
7171
* @param question The question.
7272
* @param answers Object with the question answers (without prefix).
73+
* @param component The component the question is related to.
74+
* @param componentId Component ID.
7375
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
7476
*/
75-
isGradableResponse(question: any, answers: any): number {
77+
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
7678
// This question type depends on calculated.
77-
return this.calculatedHandler.isGradableResponse(question, answers);
79+
return this.calculatedHandler.isGradableResponse(question, answers, component, componentId);
7880
}
7981

8082
/**

src/addon/qtype/ddimageortext/providers/handler.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -93,9 +93,11 @@ export class AddonQtypeDdImageOrTextHandler implements CoreQuestionHandler {
9393
*
9494
* @param question The question.
9595
* @param answers Object with the question answers (without prefix).
96+
* @param component The component the question is related to.
97+
* @param componentId Component ID.
9698
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
9799
*/
98-
isGradableResponse(question: any, answers: any): number {
100+
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
99101
for (const name in answers) {
100102
const value = answers[name];
101103
if (value && value !== '0') {

src/addon/qtype/ddmarker/providers/handler.ts

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -92,10 +92,12 @@ export class AddonQtypeDdMarkerHandler implements CoreQuestionHandler {
9292
*
9393
* @param question The question.
9494
* @param answers Object with the question answers (without prefix).
95+
* @param component The component the question is related to.
96+
* @param componentId Component ID.
9597
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
9698
*/
97-
isGradableResponse(question: any, answers: any): number {
98-
return this.isCompleteResponse(question, answers, null, null);
99+
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
100+
return this.isCompleteResponse(question, answers, component, componentId);
99101
}
100102

101103
/**

src/addon/qtype/ddwtos/providers/handler.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,9 +91,11 @@ export class AddonQtypeDdwtosHandler implements CoreQuestionHandler {
9191
*
9292
* @param question The question.
9393
* @param answers Object with the question answers (without prefix).
94+
* @param component The component the question is related to.
95+
* @param componentId Component ID.
9496
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
9597
*/
96-
isGradableResponse(question: any, answers: any): number {
98+
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
9799
for (const name in answers) {
98100
const value = answers[name];
99101
if (value && value !== '0') {

src/addon/qtype/essay/providers/handler.ts

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -162,11 +162,11 @@ export class AddonQtypeEssayHandler implements CoreQuestionHandler {
162162
const attachments = CoreFileSession.instance.getFiles(component, questionComponentId);
163163

164164
if (!allowedOptions.text) {
165-
return attachments && attachments.length > 0 ? 1 : 0;
165+
return attachments && attachments.length >= Number(question.displayoptions.attachmentsrequired) ? 1 : 0;
166166
}
167167

168168
return (hasTextAnswer || question.displayoptions.responserequired == '0') &&
169-
((attachments && attachments.length > 0) || question.displayoptions.attachmentsrequired == '0') ? 1 : 0;
169+
(attachments && attachments.length > Number(question.displayoptions.attachmentsrequired)) ? 1 : 0;
170170
}
171171

172172
/**
@@ -184,10 +184,20 @@ export class AddonQtypeEssayHandler implements CoreQuestionHandler {
184184
*
185185
* @param question The question.
186186
* @param answers Object with the question answers (without prefix).
187+
* @param component The component the question is related to.
188+
* @param componentId Component ID.
187189
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
188190
*/
189-
isGradableResponse(question: any, answers: any): number {
190-
return 0;
191+
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
192+
if (typeof question.responsefileareas == 'undefined') {
193+
return -1;
194+
}
195+
196+
const questionComponentId = CoreQuestion.instance.getQuestionComponentId(question, componentId);
197+
const attachments = CoreFileSession.instance.getFiles(component, questionComponentId);
198+
199+
// Determine if the given response has online text or attachments.
200+
return (answers['answer'] && answers['answer'] !== '') || (attachments && attachments.length > 0) ? 1 : 0;
191201
}
192202

193203
/**

src/addon/qtype/gapselect/providers/handler.ts

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -92,9 +92,11 @@ export class AddonQtypeGapSelectHandler implements CoreQuestionHandler {
9292
*
9393
* @param question The question.
9494
* @param answers Object with the question answers (without prefix).
95+
* @param component The component the question is related to.
96+
* @param componentId Component ID.
9597
* @return 1 if gradable, 0 if not gradable, -1 if cannot determine.
9698
*/
97-
isGradableResponse(question: any, answers: any): number {
99+
isGradableResponse(question: any, answers: any, component: string, componentId: string | number): number {
98100
// We should always get a value for each select so we can assume we receive all the possible answers.
99101
for (const name in answers) {
100102
const value = answers[name];

0 commit comments

Comments
 (0)