Merge pull request #1285 from dpalou/MOBILE-2388
MOBILE-2388 qbehaviour: Implement all behaviour addonsmain
commit
f3ea09c7ea
|
@ -0,0 +1,30 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { AddonQbehaviourAdaptiveHandler } from './providers/handler';
|
||||
import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
providers: [
|
||||
AddonQbehaviourAdaptiveHandler
|
||||
]
|
||||
})
|
||||
export class AddonQbehaviourAdaptiveModule {
|
||||
constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourAdaptiveHandler) {
|
||||
behaviourDelegate.registerHandler(handler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
|
||||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
|
||||
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
|
||||
|
||||
/**
|
||||
* Handler to support adaptive question behaviour.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonQbehaviourAdaptiveHandler implements CoreQuestionBehaviourHandler {
|
||||
name = 'AddonQbehaviourAdaptive';
|
||||
type = 'adaptive';
|
||||
|
||||
constructor(private questionHelper: CoreQuestionHelperProvider) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a question behaviour.
|
||||
* If the behaviour requires a submit button, it should add it to question.behaviourButtons.
|
||||
* If the behaviour requires to show some extra data, it should return the components to render it.
|
||||
*
|
||||
* @param {any} question The question.
|
||||
* @return {any[]|Promise<any[]>} Components (or promise resolved with components) to render some extra data in the question
|
||||
* (e.g. certainty options). Don't return anything if no extra data is required.
|
||||
*/
|
||||
handleQuestion(question: any): any[] | Promise<any[]> {
|
||||
// Just extract the button, it doesn't need any specific component.
|
||||
this.questionHelper.extractQbehaviourButtons(question);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the handler is enabled on a site level.
|
||||
*
|
||||
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
|
||||
*/
|
||||
isEnabled(): boolean | Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { AddonQbehaviourAdaptiveNoPenaltyHandler } from './providers/handler';
|
||||
import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
providers: [
|
||||
AddonQbehaviourAdaptiveNoPenaltyHandler
|
||||
]
|
||||
})
|
||||
export class AddonQbehaviourAdaptiveNoPenaltyModule {
|
||||
constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourAdaptiveNoPenaltyHandler) {
|
||||
behaviourDelegate.registerHandler(handler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
|
||||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
|
||||
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
|
||||
|
||||
/**
|
||||
* Handler to support adaptive no penalty question behaviour.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonQbehaviourAdaptiveNoPenaltyHandler implements CoreQuestionBehaviourHandler {
|
||||
name = 'AddonQbehaviourAdaptiveNoPenalty';
|
||||
type = 'adaptivenopenalty';
|
||||
|
||||
constructor(private questionHelper: CoreQuestionHelperProvider) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a question behaviour.
|
||||
* If the behaviour requires a submit button, it should add it to question.behaviourButtons.
|
||||
* If the behaviour requires to show some extra data, it should return the components to render it.
|
||||
*
|
||||
* @param {any} question The question.
|
||||
* @return {any[]|Promise<any[]>} Components (or promise resolved with components) to render some extra data in the question
|
||||
* (e.g. certainty options). Don't return anything if no extra data is required.
|
||||
*/
|
||||
handleQuestion(question: any): any[] | Promise<any[]> {
|
||||
// Just extract the button, it doesn't need any specific component.
|
||||
this.questionHelper.extractQbehaviourButtons(question);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the handler is enabled on a site level.
|
||||
*
|
||||
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
|
||||
*/
|
||||
isEnabled(): boolean | Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,6 @@
|
|||
<ion-item text-wrap class="addon-qbehaviour-deferredcbm-certainty-title" *ngIf="question.behaviourCertaintyOptions && question.behaviourCertaintyOptions.length">
|
||||
<p>{{ 'core.question.certainty' | translate }}</p>
|
||||
</ion-item>
|
||||
<ion-radio *ngFor="let option of question.behaviourCertaintyOptions" id="{{option.id}}" name="{{option.name}}" [ngModel]="question.behaviourCertaintySelected" [value]="option.value" [disabled]="option.disabled">
|
||||
<p><core-format-text [component]="component" [componentId]="componentId" [text]="option.text"></core-format-text></p>
|
||||
</ion-radio>
|
|
@ -0,0 +1,36 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, Input, EventEmitter } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Component to render the deferred CBM in a question.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'addon-qbehaviour-deferredcbm',
|
||||
templateUrl: 'deferredcbm.html'
|
||||
})
|
||||
export class AddonQbehaviourDeferredCBMComponent {
|
||||
@Input() question: any; // The question.
|
||||
@Input() component: string; // The component the question belongs to.
|
||||
@Input() componentId: number; // ID of the component the question belongs to.
|
||||
@Input() attemptId: number; // Attempt ID.
|
||||
@Input() offlineEnabled?: boolean | string; // Whether the question can be answered in offline.
|
||||
@Input() buttonClicked: EventEmitter<any>; // Should emit an event when a behaviour button is clicked.
|
||||
@Input() onAbort: EventEmitter<void>; // Should emit an event if the question should be aborted.
|
||||
|
||||
constructor() {
|
||||
// Nothing to do.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { IonicModule } from 'ionic-angular';
|
||||
import { TranslateModule } from '@ngx-translate/core';
|
||||
import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate';
|
||||
import { CoreDirectivesModule } from '@directives/directives.module';
|
||||
import { AddonQbehaviourDeferredCBMHandler } from './providers/handler';
|
||||
import { AddonQbehaviourDeferredCBMComponent } from './component/deferredcbm';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AddonQbehaviourDeferredCBMComponent
|
||||
],
|
||||
imports: [
|
||||
IonicModule,
|
||||
TranslateModule.forChild(),
|
||||
CoreDirectivesModule
|
||||
],
|
||||
providers: [
|
||||
AddonQbehaviourDeferredCBMHandler
|
||||
],
|
||||
exports: [
|
||||
AddonQbehaviourDeferredCBMComponent
|
||||
],
|
||||
entryComponents: [
|
||||
AddonQbehaviourDeferredCBMComponent
|
||||
]
|
||||
})
|
||||
export class AddonQbehaviourDeferredCBMModule {
|
||||
constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourDeferredCBMHandler) {
|
||||
behaviourDelegate.registerHandler(handler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,116 @@
|
|||
|
||||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
|
||||
import { CoreQuestionDelegate } from '@core/question/providers/delegate';
|
||||
import { CoreQuestionState } from '@core/question/providers/question';
|
||||
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
|
||||
import { AddonQbehaviourDeferredFeedbackHandler } from '@addon/qbehaviour/deferredfeedback/providers/handler';
|
||||
import { AddonQbehaviourDeferredCBMComponent } from '../component/deferredcbm';
|
||||
|
||||
/**
|
||||
* Handler to support deferred CBM question behaviour.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonQbehaviourDeferredCBMHandler implements CoreQuestionBehaviourHandler {
|
||||
name = 'AddonQbehaviourDeferredCBM';
|
||||
type = 'deferredcbm';
|
||||
|
||||
constructor(private questionDelegate: CoreQuestionDelegate, private questionHelper: CoreQuestionHelperProvider,
|
||||
private deferredFeedbackHandler: AddonQbehaviourDeferredFeedbackHandler) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine a question new state based on its answer(s).
|
||||
*
|
||||
* @param {string} component Component the question belongs to.
|
||||
* @param {number} attemptId Attempt ID the question belongs to.
|
||||
* @param {any} question The question.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {CoreQuestionState|Promise<CoreQuestionState>} New state (or promise resolved with state).
|
||||
*/
|
||||
determineNewState(component: string, attemptId: number, question: any, siteId?: string)
|
||||
: CoreQuestionState | Promise<CoreQuestionState> {
|
||||
// Depends on deferredfeedback.
|
||||
return this.deferredFeedbackHandler.determineNewStateDeferred(component, attemptId, question, siteId,
|
||||
this.isCompleteResponse.bind(this), this.isSameResponse.bind(this));
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a question behaviour.
|
||||
* If the behaviour requires a submit button, it should add it to question.behaviourButtons.
|
||||
* If the behaviour requires to show some extra data, it should return the components to render it.
|
||||
*
|
||||
* @param {any} question The question.
|
||||
* @return {any[]|Promise<any[]>} Components (or promise resolved with components) to render some extra data in the question
|
||||
* (e.g. certainty options). Don't return anything if no extra data is required.
|
||||
*/
|
||||
handleQuestion(question: any): any[] | Promise<any[]> {
|
||||
if (this.questionHelper.extractQbehaviourCBM(question)) {
|
||||
return [AddonQbehaviourDeferredCBMComponent];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if a response is complete.
|
||||
*
|
||||
* @param {any} question The question.
|
||||
* @param {any} answers Object with the question answers (without prefix).
|
||||
* @return {number} 1 if complete, 0 if not complete, -1 if cannot determine.
|
||||
*/
|
||||
protected isCompleteResponse(question: any, answers: any): number {
|
||||
// First check if the question answer is complete.
|
||||
const complete = this.questionDelegate.isCompleteResponse(question, answers);
|
||||
if (complete > 0) {
|
||||
// Answer is complete, check the user answered CBM too.
|
||||
return answers['-certainty'] ? 1 : 0;
|
||||
}
|
||||
|
||||
return complete;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the handler is enabled on a site level.
|
||||
*
|
||||
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
|
||||
*/
|
||||
isEnabled(): boolean | Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if two responses are the same.
|
||||
*
|
||||
* @param {any} question Question.
|
||||
* @param {any} prevAnswers Object with the previous question answers.
|
||||
* @param {any} prevBasicAnswers Object with the previous basic" answers (without sequencecheck, certainty, ...).
|
||||
* @param {any} newAnswers Object with the new question answers.
|
||||
* @param {any} newBasicAnswers Object with the previous basic" answers (without sequencecheck, certainty, ...).
|
||||
* @return {boolean} Whether they're the same.
|
||||
*/
|
||||
protected isSameResponse(question: any, prevAnswers: any, prevBasicAnswers: any, newAnswers: any, newBasicAnswers: any)
|
||||
: boolean {
|
||||
// First check if the question answer is the same.
|
||||
const same = this.questionDelegate.isSameResponse(question, prevBasicAnswers, newBasicAnswers);
|
||||
if (same) {
|
||||
// Same response, check the CBM is the same too.
|
||||
return prevAnswers['-certainty'] == newAnswers['-certainty'];
|
||||
}
|
||||
|
||||
return same;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { AddonQbehaviourDeferredFeedbackHandler } from './providers/handler';
|
||||
import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
providers: [
|
||||
AddonQbehaviourDeferredFeedbackHandler
|
||||
]
|
||||
})
|
||||
export class AddonQbehaviourDeferredFeedbackModule {
|
||||
constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourDeferredFeedbackHandler) {
|
||||
behaviourDelegate.registerHandler(handler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,154 @@
|
|||
|
||||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
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 {any} question The question.
|
||||
* @param {any} answers Object with the question answers (without prefix).
|
||||
* @return {number} 1 if complete, 0 if not complete, -1 if cannot determine.
|
||||
*/
|
||||
export type isCompleteResponseFunction = (question: any, answers: any) => number;
|
||||
|
||||
/**
|
||||
* Check if two responses are the same.
|
||||
*
|
||||
* @param {any} question Question.
|
||||
* @param {any} prevAnswers Object with the previous question answers.
|
||||
* @param {any} prevBasicAnswers Object with the previous basic" answers (without sequencecheck, certainty, ...).
|
||||
* @param {any} newAnswers Object with the new question answers.
|
||||
* @param {any} newBasicAnswers Object with the previous basic" answers (without sequencecheck, certainty, ...).
|
||||
* @return {boolean} Whether they're the same.
|
||||
*/
|
||||
export type isSameResponseFunction = (question: any, prevAnswers: any, prevBasicAnswers: any, newAnswers: any,
|
||||
newBasicAnswers: any) => boolean;
|
||||
|
||||
/**
|
||||
* Handler to support deferred feedback question behaviour.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonQbehaviourDeferredFeedbackHandler implements CoreQuestionBehaviourHandler {
|
||||
name = 'AddonQbehaviourDeferredFeedback';
|
||||
type = 'deferredfeedback';
|
||||
|
||||
constructor(private questionDelegate: CoreQuestionDelegate, private questionProvider: CoreQuestionProvider) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine a question new state based on its answer(s).
|
||||
*
|
||||
* @param {string} component Component the question belongs to.
|
||||
* @param {number} attemptId Attempt ID the question belongs to.
|
||||
* @param {any} question The question.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {CoreQuestionState|Promise<CoreQuestionState>} New state (or promise resolved with state).
|
||||
*/
|
||||
determineNewState(component: string, attemptId: number, question: any, siteId?: string)
|
||||
: CoreQuestionState | Promise<CoreQuestionState> {
|
||||
return this.determineNewStateDeferred(component, attemptId, question, siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine a question new state based on its answer(s) for deferred question behaviour.
|
||||
*
|
||||
* @param {string} component Component the question belongs to.
|
||||
* @param {number} attemptId Attempt ID the question belongs to.
|
||||
* @param {any} question The question.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @param {isCompleteResponseFunction} [isCompleteFn] Function to override the default isCompleteResponse check.
|
||||
* @param {isSameResponseFunction} [isSameFn] Function to override the default isSameResponse check.
|
||||
* @return {Promise<CoreQuestionState>} Promise resolved with state.
|
||||
*/
|
||||
determineNewStateDeferred(component: string, attemptId: number, question: any, siteId?: string,
|
||||
isCompleteFn?: isCompleteResponseFunction, isSameFn?: isSameResponseFunction): Promise<CoreQuestionState> {
|
||||
|
||||
// Check if we have local data for the question.
|
||||
return this.questionProvider.getQuestion(component, attemptId, question.slot, siteId).catch(() => {
|
||||
// No entry found, use the original data.
|
||||
return question;
|
||||
}).then((dbQuestion) => {
|
||||
const state = this.questionProvider.getState(dbQuestion.state);
|
||||
|
||||
if (state.finished || !state.active) {
|
||||
// Question is finished, it cannot change.
|
||||
return state;
|
||||
}
|
||||
|
||||
// We need to check if the answers have changed. Retrieve current stored answers.
|
||||
return this.questionProvider.getQuestionAnswers(component, attemptId, question.slot, false, siteId)
|
||||
.then((prevAnswers) => {
|
||||
|
||||
const newBasicAnswers = this.questionProvider.getBasicAnswers(question.answers);
|
||||
|
||||
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)) {
|
||||
return state;
|
||||
}
|
||||
} else {
|
||||
if (this.questionDelegate.isSameResponse(question, prevBasicAnswers, newBasicAnswers)) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
// Answers have changed. Now check if the response is complete and calculate the new state.
|
||||
let complete: number,
|
||||
newState: string;
|
||||
if (isCompleteFn) {
|
||||
// Pass all the answers since some behaviours might need the extra data.
|
||||
complete = isCompleteFn(question, question.answers);
|
||||
} else {
|
||||
// Only pass the basic answers since questions should be independent of extra data.
|
||||
complete = this.questionDelegate.isCompleteResponse(question, newBasicAnswers);
|
||||
}
|
||||
|
||||
if (complete < 0) {
|
||||
newState = 'unknown';
|
||||
} else if (complete > 0) {
|
||||
newState = 'complete';
|
||||
} else {
|
||||
const gradable = this.questionDelegate.isGradableResponse(question, newBasicAnswers);
|
||||
if (gradable < 0) {
|
||||
newState = 'unknown';
|
||||
} else if (gradable > 0) {
|
||||
newState = 'invalid';
|
||||
} else {
|
||||
newState = 'todo';
|
||||
}
|
||||
}
|
||||
|
||||
return this.questionProvider.getState(newState);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the handler is enabled on a site level.
|
||||
*
|
||||
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
|
||||
*/
|
||||
isEnabled(): boolean | Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { AddonQbehaviourImmediateCBMHandler } from './providers/handler';
|
||||
import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
providers: [
|
||||
AddonQbehaviourImmediateCBMHandler
|
||||
]
|
||||
})
|
||||
export class AddonQbehaviourImmediateCBMModule {
|
||||
constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourImmediateCBMHandler) {
|
||||
behaviourDelegate.registerHandler(handler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,59 @@
|
|||
|
||||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
|
||||
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
|
||||
import { AddonQbehaviourDeferredCBMComponent } from '@addon/qbehaviour/deferredcbm/component/deferredcbm';
|
||||
|
||||
/**
|
||||
* Handler to support immediate CBM question behaviour.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonQbehaviourImmediateCBMHandler implements CoreQuestionBehaviourHandler {
|
||||
name = 'AddonQbehaviourImmediateCBM';
|
||||
type = 'immediatecbm';
|
||||
|
||||
constructor(private questionHelper: CoreQuestionHelperProvider) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a question behaviour.
|
||||
* If the behaviour requires a submit button, it should add it to question.behaviourButtons.
|
||||
* If the behaviour requires to show some extra data, it should return the components to render it.
|
||||
*
|
||||
* @param {any} question The question.
|
||||
* @return {any[]|Promise<any[]>} Components (or promise resolved with components) to render some extra data in the question
|
||||
* (e.g. certainty options). Don't return anything if no extra data is required.
|
||||
*/
|
||||
handleQuestion(question: any): any[] | Promise<any[]> {
|
||||
// Just extract the button, it doesn't need any specific component.
|
||||
this.questionHelper.extractQbehaviourButtons(question);
|
||||
if (this.questionHelper.extractQbehaviourCBM(question)) {
|
||||
// Depends on deferredcbm.
|
||||
return [AddonQbehaviourDeferredCBMComponent];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the handler is enabled on a site level.
|
||||
*
|
||||
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
|
||||
*/
|
||||
isEnabled(): boolean | Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { AddonQbehaviourImmediateFeedbackHandler } from './providers/handler';
|
||||
import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
providers: [
|
||||
AddonQbehaviourImmediateFeedbackHandler
|
||||
]
|
||||
})
|
||||
export class AddonQbehaviourImmediateFeedbackModule {
|
||||
constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourImmediateFeedbackHandler) {
|
||||
behaviourDelegate.registerHandler(handler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
|
||||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
|
||||
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
|
||||
|
||||
/**
|
||||
* Handler to support immediate feedback question behaviour.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonQbehaviourImmediateFeedbackHandler implements CoreQuestionBehaviourHandler {
|
||||
name = 'AddonQbehaviourImmediateFeedback';
|
||||
type = 'immediatefeedback';
|
||||
|
||||
constructor(private questionHelper: CoreQuestionHelperProvider) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a question behaviour.
|
||||
* If the behaviour requires a submit button, it should add it to question.behaviourButtons.
|
||||
* If the behaviour requires to show some extra data, it should return the components to render it.
|
||||
*
|
||||
* @param {any} question The question.
|
||||
* @return {any[]|Promise<any[]>} Components (or promise resolved with components) to render some extra data in the question
|
||||
* (e.g. certainty options). Don't return anything if no extra data is required.
|
||||
*/
|
||||
handleQuestion(question: any): any[] | Promise<any[]> {
|
||||
// Just extract the button, it doesn't need any specific component.
|
||||
this.questionHelper.extractQbehaviourButtons(question);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the handler is enabled on a site level.
|
||||
*
|
||||
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
|
||||
*/
|
||||
isEnabled(): boolean | Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1 @@
|
|||
<input *ngIf="question.behaviourSeenInput" type="hidden" name="{{question.behaviourSeenInput.name}}" value="{{question.behaviourSeenInput.value}}" >
|
|
@ -0,0 +1,36 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Component, Input, EventEmitter } from '@angular/core';
|
||||
|
||||
/**
|
||||
* Component to render a "seen" hidden input for informationitem question behaviour.
|
||||
*/
|
||||
@Component({
|
||||
selector: 'addon-qbehaviour-informationitem',
|
||||
templateUrl: 'informationitem.html'
|
||||
})
|
||||
export class AddonQbehaviourInformationItemComponent {
|
||||
@Input() question: any; // The question.
|
||||
@Input() component: string; // The component the question belongs to.
|
||||
@Input() componentId: number; // ID of the component the question belongs to.
|
||||
@Input() attemptId: number; // Attempt ID.
|
||||
@Input() offlineEnabled?: boolean | string; // Whether the question can be answered in offline.
|
||||
@Input() buttonClicked: EventEmitter<any>; // Should emit an event when a behaviour button is clicked.
|
||||
@Input() onAbort: EventEmitter<void>; // Should emit an event if the question should be aborted.
|
||||
|
||||
constructor() {
|
||||
// Nothing to do.
|
||||
}
|
||||
}
|
|
@ -0,0 +1,42 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { IonicModule } from 'ionic-angular';
|
||||
import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate';
|
||||
import { AddonQbehaviourInformationItemHandler } from './providers/handler';
|
||||
import { AddonQbehaviourInformationItemComponent } from './component/informationitem';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
AddonQbehaviourInformationItemComponent
|
||||
],
|
||||
imports: [
|
||||
IonicModule
|
||||
],
|
||||
providers: [
|
||||
AddonQbehaviourInformationItemHandler
|
||||
],
|
||||
exports: [
|
||||
AddonQbehaviourInformationItemComponent
|
||||
],
|
||||
entryComponents: [
|
||||
AddonQbehaviourInformationItemComponent
|
||||
]
|
||||
})
|
||||
export class AddonQbehaviourInformationItemModule {
|
||||
constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourInformationItemHandler) {
|
||||
behaviourDelegate.registerHandler(handler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,73 @@
|
|||
|
||||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
|
||||
import { CoreQuestionProvider, CoreQuestionState } from '@core/question/providers/question';
|
||||
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
|
||||
import { AddonQbehaviourInformationItemComponent } from '../component/informationitem';
|
||||
|
||||
/**
|
||||
* Handler to support information item question behaviour.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonQbehaviourInformationItemHandler implements CoreQuestionBehaviourHandler {
|
||||
name = 'AddonQbehaviourInformationItem';
|
||||
type = 'informationitem';
|
||||
|
||||
constructor(private questionHelper: CoreQuestionHelperProvider, private questionProvider: CoreQuestionProvider) { }
|
||||
|
||||
/**
|
||||
* Determine a question new state based on its answer(s).
|
||||
*
|
||||
* @param {string} component Component the question belongs to.
|
||||
* @param {number} attemptId Attempt ID the question belongs to.
|
||||
* @param {any} question The question.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {CoreQuestionState|Promise<CoreQuestionState>} New state (or promise resolved with state).
|
||||
*/
|
||||
determineNewState(component: string, attemptId: number, question: any, siteId?: string)
|
||||
: CoreQuestionState | Promise<CoreQuestionState> {
|
||||
if (question.answers['-seen']) {
|
||||
return this.questionProvider.getState('complete');
|
||||
}
|
||||
|
||||
return this.questionProvider.getState(question.state || 'todo');
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a question behaviour.
|
||||
* If the behaviour requires a submit button, it should add it to question.behaviourButtons.
|
||||
* If the behaviour requires to show some extra data, it should return the components to render it.
|
||||
*
|
||||
* @param {any} question The question.
|
||||
* @return {any[]|Promise<any[]>} Components (or promise resolved with components) to render some extra data in the question
|
||||
* (e.g. certainty options). Don't return anything if no extra data is required.
|
||||
*/
|
||||
handleQuestion(question: any): any[] | Promise<any[]> {
|
||||
if (this.questionHelper.extractQbehaviourSeenInput(question)) {
|
||||
return [AddonQbehaviourInformationItemComponent];
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the handler is enabled on a site level.
|
||||
*
|
||||
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
|
||||
*/
|
||||
isEnabled(): boolean | Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { AddonQbehaviourInteractiveHandler } from './providers/handler';
|
||||
import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
providers: [
|
||||
AddonQbehaviourInteractiveHandler
|
||||
]
|
||||
})
|
||||
export class AddonQbehaviourInteractiveModule {
|
||||
constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourInteractiveHandler) {
|
||||
behaviourDelegate.registerHandler(handler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
|
||||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
|
||||
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
|
||||
|
||||
/**
|
||||
* Handler to support interactive question behaviour.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonQbehaviourInteractiveHandler implements CoreQuestionBehaviourHandler {
|
||||
name = 'AddonQbehaviourInteractive';
|
||||
type = 'interactive';
|
||||
|
||||
constructor(private questionHelper: CoreQuestionHelperProvider) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a question behaviour.
|
||||
* If the behaviour requires a submit button, it should add it to question.behaviourButtons.
|
||||
* If the behaviour requires to show some extra data, it should return the components to render it.
|
||||
*
|
||||
* @param {any} question The question.
|
||||
* @return {any[]|Promise<any[]>} Components (or promise resolved with components) to render some extra data in the question
|
||||
* (e.g. certainty options). Don't return anything if no extra data is required.
|
||||
*/
|
||||
handleQuestion(question: any): any[] | Promise<any[]> {
|
||||
// Just extract the button, it doesn't need any specific component.
|
||||
this.questionHelper.extractQbehaviourButtons(question);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the handler is enabled on a site level.
|
||||
*
|
||||
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
|
||||
*/
|
||||
isEnabled(): boolean | Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { AddonQbehaviourInteractiveCountbackHandler } from './providers/handler';
|
||||
import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
providers: [
|
||||
AddonQbehaviourInteractiveCountbackHandler
|
||||
]
|
||||
})
|
||||
export class AddonQbehaviourInteractiveCountbackModule {
|
||||
constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourInteractiveCountbackHandler) {
|
||||
behaviourDelegate.registerHandler(handler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
|
||||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { Injectable } from '@angular/core';
|
||||
import { CoreQuestionBehaviourHandler } from '@core/question/providers/behaviour-delegate';
|
||||
import { CoreQuestionHelperProvider } from '@core/question/providers/helper';
|
||||
|
||||
/**
|
||||
* Handler to support interactive countback question behaviour.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonQbehaviourInteractiveCountbackHandler implements CoreQuestionBehaviourHandler {
|
||||
name = 'AddonQbehaviourInteractiveCountback';
|
||||
type = 'interactivecountback';
|
||||
|
||||
constructor(private questionHelper: CoreQuestionHelperProvider) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle a question behaviour.
|
||||
* If the behaviour requires a submit button, it should add it to question.behaviourButtons.
|
||||
* If the behaviour requires to show some extra data, it should return the components to render it.
|
||||
*
|
||||
* @param {any} question The question.
|
||||
* @return {any[]|Promise<any[]>} Components (or promise resolved with components) to render some extra data in the question
|
||||
* (e.g. certainty options). Don't return anything if no extra data is required.
|
||||
*/
|
||||
handleQuestion(question: any): any[] | Promise<any[]> {
|
||||
// Just extract the button, it doesn't need any specific component.
|
||||
this.questionHelper.extractQbehaviourButtons(question);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the handler is enabled on a site level.
|
||||
*
|
||||
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
|
||||
*/
|
||||
isEnabled(): boolean | Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,30 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
import { NgModule } from '@angular/core';
|
||||
import { AddonQbehaviourManualGradedHandler } from './providers/handler';
|
||||
import { CoreQuestionBehaviourDelegate } from '@core/question/providers/behaviour-delegate';
|
||||
|
||||
@NgModule({
|
||||
declarations: [
|
||||
],
|
||||
providers: [
|
||||
AddonQbehaviourManualGradedHandler
|
||||
]
|
||||
})
|
||||
export class AddonQbehaviourManualGradedModule {
|
||||
constructor(behaviourDelegate: CoreQuestionBehaviourDelegate, handler: AddonQbehaviourManualGradedHandler) {
|
||||
behaviourDelegate.registerHandler(handler);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,147 @@
|
|||
|
||||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
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 {any} question The question.
|
||||
* @param {any} answers Object with the question answers (without prefix).
|
||||
* @return {number} 1 if complete, 0 if not complete, -1 if cannot determine.
|
||||
*/
|
||||
export type isCompleteResponseFunction = (question: any, answers: any) => number;
|
||||
|
||||
/**
|
||||
* Check if two responses are the same.
|
||||
*
|
||||
* @param {any} question Question.
|
||||
* @param {any} prevAnswers Object with the previous question answers.
|
||||
* @param {any} prevBasicAnswers Object with the previous basic" answers (without sequencecheck, certainty, ...).
|
||||
* @param {any} newAnswers Object with the new question answers.
|
||||
* @param {any} newBasicAnswers Object with the previous basic" answers (without sequencecheck, certainty, ...).
|
||||
* @return {boolean} Whether they're the same.
|
||||
*/
|
||||
export type isSameResponseFunction = (question: any, prevAnswers: any, prevBasicAnswers: any, newAnswers: any,
|
||||
newBasicAnswers: any) => boolean;
|
||||
|
||||
/**
|
||||
* Handler to support manual graded question behaviour.
|
||||
*/
|
||||
@Injectable()
|
||||
export class AddonQbehaviourManualGradedHandler implements CoreQuestionBehaviourHandler {
|
||||
name = 'AddonQbehaviourManualGraded';
|
||||
type = 'manualgraded';
|
||||
|
||||
constructor(private questionDelegate: CoreQuestionDelegate, private questionProvider: CoreQuestionProvider) {
|
||||
// Nothing to do.
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine a question new state based on its answer(s).
|
||||
*
|
||||
* @param {string} component Component the question belongs to.
|
||||
* @param {number} attemptId Attempt ID the question belongs to.
|
||||
* @param {any} question The question.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @return {CoreQuestionState|Promise<CoreQuestionState>} New state (or promise resolved with state).
|
||||
*/
|
||||
determineNewState(component: string, attemptId: number, question: any, siteId?: string)
|
||||
: CoreQuestionState | Promise<CoreQuestionState> {
|
||||
return this.determineNewStateManualGraded(component, attemptId, question, siteId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Determine a question new state based on its answer(s) for manual graded question behaviour.
|
||||
*
|
||||
* @param {string} component Component the question belongs to.
|
||||
* @param {number} attemptId Attempt ID the question belongs to.
|
||||
* @param {any} question The question.
|
||||
* @param {string} [siteId] Site ID. If not defined, current site.
|
||||
* @param {isCompleteResponseFunction} [isCompleteFn] Function to override the default isCompleteResponse check.
|
||||
* @param {isSameResponseFunction} [isSameFn] Function to override the default isSameResponse check.
|
||||
* @return {Promise<CoreQuestionState>} Promise resolved with state.
|
||||
*/
|
||||
determineNewStateManualGraded(component: string, attemptId: number, question: any, siteId?: string,
|
||||
isCompleteFn?: isCompleteResponseFunction, isSameFn?: isSameResponseFunction): Promise<CoreQuestionState> {
|
||||
|
||||
// Check if we have local data for the question.
|
||||
return this.questionProvider.getQuestion(component, attemptId, question.slot, siteId).catch(() => {
|
||||
// No entry found, use the original data.
|
||||
return question;
|
||||
}).then((dbQuestion) => {
|
||||
const state = this.questionProvider.getState(dbQuestion.state);
|
||||
|
||||
if (state.finished || !state.active) {
|
||||
// Question is finished, it cannot change.
|
||||
return state;
|
||||
}
|
||||
|
||||
// We need to check if the answers have changed. Retrieve current stored answers.
|
||||
return this.questionProvider.getQuestionAnswers(component, attemptId, question.slot, false, siteId)
|
||||
.then((prevAnswers) => {
|
||||
|
||||
const newBasicAnswers = this.questionProvider.getBasicAnswers(question.answers);
|
||||
|
||||
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)) {
|
||||
return state;
|
||||
}
|
||||
} else {
|
||||
if (this.questionDelegate.isSameResponse(question, prevBasicAnswers, newBasicAnswers)) {
|
||||
return state;
|
||||
}
|
||||
}
|
||||
|
||||
// Answers have changed. Now check if the response is complete and calculate the new state.
|
||||
let complete: number,
|
||||
newState: string;
|
||||
if (isCompleteFn) {
|
||||
// Pass all the answers since some behaviours might need the extra data.
|
||||
complete = isCompleteFn(question, question.answers);
|
||||
} else {
|
||||
// Only pass the basic answers since questions should be independent of extra data.
|
||||
complete = this.questionDelegate.isCompleteResponse(question, newBasicAnswers);
|
||||
}
|
||||
|
||||
if (complete < 0) {
|
||||
newState = 'unknown';
|
||||
} else if (complete > 0) {
|
||||
newState = 'complete';
|
||||
} else {
|
||||
newState = 'todo';
|
||||
}
|
||||
|
||||
return this.questionProvider.getState(newState);
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Whether or not the handler is enabled on a site level.
|
||||
*
|
||||
* @return {boolean|Promise<boolean>} True or promise resolved with true if enabled.
|
||||
*/
|
||||
isEnabled(): boolean | Promise<boolean> {
|
||||
return true;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,44 @@
|
|||
// (C) Copyright 2015 Martin Dougiamas
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
import { NgModule } from '@angular/core';
|
||||
import { AddonQbehaviourAdaptiveModule } from './adaptive/adaptive.module';
|
||||
import { AddonQbehaviourAdaptiveNoPenaltyModule } from './adaptivenopenalty/adaptivenopenalty.module';
|
||||
import { AddonQbehaviourDeferredCBMModule } from './deferredcbm/deferredcbm.module';
|
||||
import { AddonQbehaviourDeferredFeedbackModule } from './deferredfeedback/deferredfeedback.module';
|
||||
import { AddonQbehaviourImmediateCBMModule } from './immediatecbm/immediatecbm.module';
|
||||
import { AddonQbehaviourImmediateFeedbackModule } from './immediatefeedback/immediatefeedback.module';
|
||||
import { AddonQbehaviourInformationItemModule } from './informationitem/informationitem.module';
|
||||
import { AddonQbehaviourInteractiveModule } from './interactive/interactive.module';
|
||||
import { AddonQbehaviourInteractiveCountbackModule } from './interactivecountback/interactivecountback.module';
|
||||
import { AddonQbehaviourManualGradedModule } from './manualgraded/manualgraded.module';
|
||||
|
||||
@NgModule({
|
||||
declarations: [],
|
||||
imports: [
|
||||
AddonQbehaviourAdaptiveModule,
|
||||
AddonQbehaviourAdaptiveNoPenaltyModule,
|
||||
AddonQbehaviourDeferredCBMModule,
|
||||
AddonQbehaviourDeferredFeedbackModule,
|
||||
AddonQbehaviourImmediateCBMModule,
|
||||
AddonQbehaviourImmediateFeedbackModule,
|
||||
AddonQbehaviourInformationItemModule,
|
||||
AddonQbehaviourInteractiveModule,
|
||||
AddonQbehaviourInteractiveCountbackModule,
|
||||
AddonQbehaviourManualGradedModule
|
||||
],
|
||||
providers: [
|
||||
],
|
||||
exports: []
|
||||
})
|
||||
export class AddonQbehaviourModule { }
|
|
@ -86,6 +86,7 @@ import { AddonMessagesModule } from '@addon/messages/messages.module';
|
|||
import { AddonNotesModule } from '../addon/notes/notes.module';
|
||||
import { AddonPushNotificationsModule } from '@addon/pushnotifications/pushnotifications.module';
|
||||
import { AddonRemoteThemesModule } from '@addon/remotethemes/remotethemes.module';
|
||||
import { AddonQbehaviourModule } from '@addon/qbehaviour/qbehaviour.module';
|
||||
|
||||
// For translate loader. AoT requires an exported function for factories.
|
||||
export function createTranslateLoader(http: HttpClient): TranslateHttpLoader {
|
||||
|
@ -172,7 +173,8 @@ export const CORE_PROVIDERS: any[] = [
|
|||
AddonMessagesModule,
|
||||
AddonNotesModule,
|
||||
AddonPushNotificationsModule,
|
||||
AddonRemoteThemesModule
|
||||
AddonRemoteThemesModule,
|
||||
AddonQbehaviourModule
|
||||
],
|
||||
bootstrap: [IonicApp],
|
||||
entryComponents: [
|
||||
|
|
|
@ -52,6 +52,68 @@ export class CoreQuestionHelperProvider {
|
|||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Extract question behaviour submit buttons from the question's HTML and add them to "behaviourButtons" property.
|
||||
* The buttons aren't deleted from the content because all the im-controls block will be removed afterwards.
|
||||
*
|
||||
* @param {any} question Question to treat.
|
||||
* @param {string} [selector] Selector to search the buttons. By default, '.im-controls input[type="submit"]'.
|
||||
*/
|
||||
extractQbehaviourButtons(question: any, selector?: string): void {
|
||||
selector = selector || '.im-controls input[type="submit"]';
|
||||
|
||||
this.div.innerHTML = question.html;
|
||||
|
||||
// Search the buttons.
|
||||
const buttons = <HTMLInputElement[]> Array.from(this.div.querySelectorAll(selector));
|
||||
buttons.forEach((button) => {
|
||||
this.addBehaviourButton(question, button);
|
||||
});
|
||||
|
||||
question.html = this.div.innerHTML;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the question has CBM and, if so, extract the certainty options and add them to a new
|
||||
* "behaviourCertaintyOptions" property.
|
||||
* The value of the selected option is stored in question.behaviourCertaintySelected.
|
||||
* We don't remove them from HTML because the whole im-controls block will be removed afterwards.
|
||||
*
|
||||
* @param {any} question Question to treat.
|
||||
* @return {boolean} Wether the certainty is found.
|
||||
*/
|
||||
extractQbehaviourCBM(question: any): boolean {
|
||||
this.div.innerHTML = question.html;
|
||||
|
||||
const labels = Array.from(this.div.querySelectorAll('.im-controls .certaintychoices label[for*="certainty"]'));
|
||||
question.behaviourCertaintyOptions = [];
|
||||
|
||||
labels.forEach((label) => {
|
||||
// Search the radio button inside this certainty and add its data to the options array.
|
||||
const input = <HTMLInputElement> label.querySelector('input[type="radio"]');
|
||||
if (input) {
|
||||
question.behaviourCertaintyOptions.push({
|
||||
id: input.id,
|
||||
name: input.name,
|
||||
value: input.value,
|
||||
text: this.textUtils.cleanTags(label.innerHTML),
|
||||
disabled: input.disabled
|
||||
});
|
||||
|
||||
if (input.checked) {
|
||||
question.behaviourCertaintySelected = input.value;
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
// If we have a certainty value stored in local we'll use that one.
|
||||
if (question.localAnswers && typeof question.localAnswers['-certainty'] != 'undefined') {
|
||||
question.behaviourCertaintySelected = question.localAnswers['-certainty'];
|
||||
}
|
||||
|
||||
return labels.length > 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the question has a redo button and, if so, add it to "behaviourButtons" property
|
||||
* and remove it from the HTML.
|
||||
|
@ -80,6 +142,33 @@ export class CoreQuestionHelperProvider {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Check if the question contains a "seen" input.
|
||||
* If so, add the name and value to a "behaviourSeenInput" property and remove the input.
|
||||
*
|
||||
* @param {any} question Question to treat.
|
||||
* @return {boolean} Whether the seen input is found.
|
||||
*/
|
||||
extractQbehaviourSeenInput(question: any): boolean {
|
||||
this.div.innerHTML = question.html;
|
||||
|
||||
// Search the "seen" input.
|
||||
const seenInput = <HTMLInputElement> this.div.querySelector('input[type="hidden"][name*=seen]');
|
||||
if (seenInput) {
|
||||
// Get the data and remove the input.
|
||||
question.behaviourSeenInput = {
|
||||
name: seenInput.name,
|
||||
value: seenInput.value
|
||||
};
|
||||
seenInput.parentElement.removeChild(seenInput);
|
||||
question.html = this.div.innerHTML;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the comment from the question HTML code and adds it in a new "commentHtml" property.
|
||||
*
|
||||
|
|
Loading…
Reference in New Issue