diff --git a/src/app/services/dialogGuidanceFeedbackService.spec.ts b/src/app/services/dialogGuidanceFeedbackService.spec.ts new file mode 100644 index 00000000000..c8f156188ad --- /dev/null +++ b/src/app/services/dialogGuidanceFeedbackService.spec.ts @@ -0,0 +1,76 @@ +import { TestBed } from '@angular/core/testing'; +import { DialogGuidanceStudentComponent } from '../../assets/wise5/components/dialogGuidance/dialog-guidance-student/dialog-guidance-student.component'; +import { FeedbackRule } from '../../assets/wise5/components/dialogGuidance/FeedbackRule'; +import { DialogGuidanceFeedbackService } from '../../assets/wise5/services/dialogGuidanceFeedbackService'; +import { StudentDataService } from '../../assets/wise5/services/studentDataService'; + +let service: DialogGuidanceFeedbackService; +let studentDataService: StudentDataService; +let component: DialogGuidanceStudentComponent; +let feedbackRuleV1: FeedbackRule; +let feedbackRuleV2_1: FeedbackRule; + +class StudentDataServiceMock { + getLatestSubmitComponentState() {} +} + +class DialogGuidanceStudentComponentMock { + nodeId = 'node1'; + componentId = 'componentA'; + isVersion1() {} + isVersion2() {} +} + +describe('DialogGuidanceFeedbackService', () => { + beforeEach(() => { + TestBed.configureTestingModule({ + imports: [], + providers: [ + DialogGuidanceFeedbackService, + { provide: StudentDataService, useClass: StudentDataServiceMock }, + { provide: DialogGuidanceStudentComponent, useClass: DialogGuidanceStudentComponentMock } + ] + }); + component = TestBed.inject(DialogGuidanceStudentComponent); + service = TestBed.inject(DialogGuidanceFeedbackService); + studentDataService = TestBed.inject(StudentDataService); + feedbackRuleV1 = new FeedbackRule(); + feedbackRuleV1.feedback = 'feedbackRuleV1'; + feedbackRuleV2_1 = new FeedbackRule(); + feedbackRuleV2_1.id = 'feedbackRuleV2_1'; + feedbackRuleV2_1.feedback = ['feedbackRuleV2_1 1', 'feedbackRuleV2_1 2']; + }); + getFeedbackTextV1(); + getFeedbackTextV2(); +}); + +function getFeedbackTextV1() { + describe('getFeedbackText() in version 1 one feedbacks per each idea', () => { + beforeEach(() => { + spyOn(component, 'isVersion1').and.returnValue(true); + }); + it('should return feedback in the feedback rule', () => { + expect(service.getFeedbackText(component, feedbackRuleV1)).toEqual('feedbackRuleV1'); + }); + }); +} + +function getFeedbackTextV2() { + describe('getFeedbackText() in version 2 (multiple feedbacks per same idea)', () => { + beforeEach(() => { + spyOn(component, 'isVersion1').and.returnValue(false); + }); + it('should return first feedback the first time', () => { + spyOn(studentDataService, 'getLatestSubmitComponentState').and.returnValue({ + studentData: { responses: [] } + }); + expect(service.getFeedbackText(component, feedbackRuleV2_1)).toEqual('feedbackRuleV2_1 1'); + }); + it('should return second feedback the second time', () => { + spyOn(studentDataService, 'getLatestSubmitComponentState').and.returnValue({ + studentData: { responses: [{ feedbackRuleId: 'feedbackRuleV2_1' }] } + }); + expect(service.getFeedbackText(component, feedbackRuleV2_1)).toEqual('feedbackRuleV2_1 2'); + }); + }); +} diff --git a/src/assets/wise5/components/dialogGuidance/ComputerDialogResponse.ts b/src/assets/wise5/components/dialogGuidance/ComputerDialogResponse.ts index aec5d23201b..8a098a552dd 100644 --- a/src/assets/wise5/components/dialogGuidance/ComputerDialogResponse.ts +++ b/src/assets/wise5/components/dialogGuidance/ComputerDialogResponse.ts @@ -2,6 +2,7 @@ import { CRaterIdea } from './CRaterIdea'; import { DialogResponse } from './DialogResponse'; export class ComputerDialogResponse extends DialogResponse { + feedbackRuleId?: string; ideas: CRaterIdea[]; user: string = 'Computer'; diff --git a/src/assets/wise5/components/dialogGuidance/DialogGuidanceFeedbackRuleEvaluator.spec.ts b/src/assets/wise5/components/dialogGuidance/DialogGuidanceFeedbackRuleEvaluator.spec.ts index 66e49b3280a..5fa729d48fc 100644 --- a/src/assets/wise5/components/dialogGuidance/DialogGuidanceFeedbackRuleEvaluator.spec.ts +++ b/src/assets/wise5/components/dialogGuidance/DialogGuidanceFeedbackRuleEvaluator.spec.ts @@ -14,6 +14,7 @@ import { AnnotationService } from '../../services/annotationService'; import { ComputerAvatarService } from '../../services/computerAvatarService'; import { ConfigService } from '../../services/configService'; import { CRaterService } from '../../services/cRaterService'; +import { DialogGuidanceFeedbackService } from '../../services/dialogGuidanceFeedbackService'; import { NodeService } from '../../services/nodeService'; import { NotebookService } from '../../services/notebookService'; import { ProjectService } from '../../services/projectService'; @@ -124,6 +125,7 @@ describe('DialogGuidanceFeedbackRuleEvaluator', () => { ComputerAvatarService, CRaterService, ConfigService, + DialogGuidanceFeedbackService, DialogGuidanceService, { provide: NodeService, useClass: MockNodeService }, { provide: NotebookService, useClass: MockService }, diff --git a/src/assets/wise5/components/dialogGuidance/DialogGuidanceFeedbackRuleEvaluator.ts b/src/assets/wise5/components/dialogGuidance/DialogGuidanceFeedbackRuleEvaluator.ts index 9448854cc9d..9228ed2249f 100644 --- a/src/assets/wise5/components/dialogGuidance/DialogGuidanceFeedbackRuleEvaluator.ts +++ b/src/assets/wise5/components/dialogGuidance/DialogGuidanceFeedbackRuleEvaluator.ts @@ -127,7 +127,7 @@ export class DialogGuidanceFeedbackRuleEvaluator { feedbackRules.find((rule) => FeedbackRule.isDefaultRule(rule)) || Object.assign(new FeedbackRule(), { expression: 'isDefault', - feedback: this.defaultFeedback + feedback: this.component.isVersion1() ? this.defaultFeedback : [this.defaultFeedback] }) ); } diff --git a/src/assets/wise5/components/dialogGuidance/FeedbackRule.ts b/src/assets/wise5/components/dialogGuidance/FeedbackRule.ts index 9de2b8c5efc..9ea6793a5a2 100644 --- a/src/assets/wise5/components/dialogGuidance/FeedbackRule.ts +++ b/src/assets/wise5/components/dialogGuidance/FeedbackRule.ts @@ -1,6 +1,7 @@ export class FeedbackRule { + id?: string; expression: string; - feedback: string; + feedback: string | string[]; static operatorPrecedences = { '!': 2, '&&': 1, '||': 1 }; static isSecondToLastSubmitRule(feedbackRule: FeedbackRule): boolean { diff --git a/src/assets/wise5/components/dialogGuidance/dialog-guidance-student/dialog-guidance-student.component.spec.ts b/src/assets/wise5/components/dialogGuidance/dialog-guidance-student/dialog-guidance-student.component.spec.ts index 1992a85713b..103ecf77d50 100644 --- a/src/assets/wise5/components/dialogGuidance/dialog-guidance-student/dialog-guidance-student.component.spec.ts +++ b/src/assets/wise5/components/dialogGuidance/dialog-guidance-student/dialog-guidance-student.component.spec.ts @@ -15,6 +15,7 @@ import { AnnotationService } from '../../../services/annotationService'; import { ComputerAvatarService } from '../../../services/computerAvatarService'; import { ConfigService } from '../../../services/configService'; import { CRaterService } from '../../../services/cRaterService'; +import { DialogGuidanceFeedbackService } from '../../../services/dialogGuidanceFeedbackService'; import { NodeService } from '../../../services/nodeService'; import { NotebookService } from '../../../services/notebookService'; import { ProjectService } from '../../../services/projectService'; @@ -65,6 +66,7 @@ describe('DialogGuidanceStudentComponent', () => { ComputerAvatarService, CRaterService, ConfigService, + DialogGuidanceFeedbackService, DialogGuidanceService, { provide: NodeService, useClass: MockNodeService }, { provide: NotebookService, useClass: MockService }, diff --git a/src/assets/wise5/components/dialogGuidance/dialog-guidance-student/dialog-guidance-student.component.ts b/src/assets/wise5/components/dialogGuidance/dialog-guidance-student/dialog-guidance-student.component.ts index 7a60e3b55d1..006a4c84226 100644 --- a/src/assets/wise5/components/dialogGuidance/dialog-guidance-student/dialog-guidance-student.component.ts +++ b/src/assets/wise5/components/dialogGuidance/dialog-guidance-student/dialog-guidance-student.component.ts @@ -22,6 +22,7 @@ import { MatDialog } from '@angular/material/dialog'; import { ComputerAvatar } from '../../../common/ComputerAvatar'; import { ComputerAvatarService } from '../../../services/computerAvatarService'; import { StudentStatusService } from '../../../services/studentStatusService'; +import { DialogGuidanceFeedbackService } from '../../../services/dialogGuidanceFeedbackService'; @Component({ selector: 'dialog-guidance-student', @@ -47,6 +48,7 @@ export class DialogGuidanceStudentComponent extends ComponentStudent { protected ConfigService: ConfigService, protected CRaterService: CRaterService, protected dialog: MatDialog, + protected dialogGuidanceFeedbackService: DialogGuidanceFeedbackService, protected NodeService: NodeService, protected NotebookService: NotebookService, protected StudentAssetService: StudentAssetService, @@ -249,19 +251,33 @@ export class DialogGuidanceStudentComponent extends ComponentStudent { createComputerDialogResponse(response: CRaterResponse): ComputerDialogResponse { const feedbackRule: FeedbackRule = this.feedbackRuleEvaluator.getFeedbackRule(response); - return response.scores != null - ? new ComputerDialogResponseMultipleScores( - feedbackRule.feedback, - response.scores, - response.ideas, - new Date().getTime() - ) - : new ComputerDialogResponseSingleScore( - feedbackRule.feedback, - response.score, - response.ideas, - new Date().getTime() - ); + const feedbackText = this.dialogGuidanceFeedbackService.getFeedbackText(this, feedbackRule); + const computerResponse = + response.scores != null + ? new ComputerDialogResponseMultipleScores( + feedbackText, + response.scores, + response.ideas, + new Date().getTime() + ) + : new ComputerDialogResponseSingleScore( + feedbackText, + response.score, + response.ideas, + new Date().getTime() + ); + if (this.isVersion2()) { + computerResponse.feedbackRuleId = feedbackRule.id; + } + return computerResponse; + } + + isVersion1(): boolean { + return this.componentContent.version == null; + } + + isVersion2(): boolean { + return this.componentContent.version === 2; } cRaterErrorResponse() { diff --git a/src/assets/wise5/components/dialogGuidance/dialogGuidanceStudentModule.ts b/src/assets/wise5/components/dialogGuidance/dialogGuidanceStudentModule.ts index f63a4f4ec88..03fb053ee87 100644 --- a/src/assets/wise5/components/dialogGuidance/dialogGuidanceStudentModule.ts +++ b/src/assets/wise5/components/dialogGuidance/dialogGuidanceStudentModule.ts @@ -4,10 +4,12 @@ import { StudentComponentModule } from '../../../../app/student/student.componen import { ComputerAvatarSelectorComponent } from '../../vle/computer-avatar-selector/computer-avatar-selector.component'; import { DialogGuidanceStudentComponent } from './dialog-guidance-student/dialog-guidance-student.component'; import { MatButtonToggleModule } from '@angular/material/button-toggle'; +import { DialogGuidanceFeedbackService } from '../../services/dialogGuidanceFeedbackService'; @NgModule({ declarations: [ComputerAvatarSelectorComponent, DialogGuidanceStudentComponent], imports: [AngularJSModule, MatButtonToggleModule, StudentComponentModule], + providers: [DialogGuidanceFeedbackService], exports: [DialogGuidanceStudentComponent] }) export class DialogGuidanceStudentModule {} diff --git a/src/assets/wise5/services/dialogGuidanceFeedbackService.ts b/src/assets/wise5/services/dialogGuidanceFeedbackService.ts new file mode 100644 index 00000000000..458c63b4b03 --- /dev/null +++ b/src/assets/wise5/services/dialogGuidanceFeedbackService.ts @@ -0,0 +1,40 @@ +import { Injectable } from '@angular/core'; +import { DialogGuidanceStudentComponent } from '../components/dialogGuidance/dialog-guidance-student/dialog-guidance-student.component'; +import { FeedbackRule } from '../components/dialogGuidance/FeedbackRule'; +import { StudentDataService } from './studentDataService'; + +@Injectable() +export class DialogGuidanceFeedbackService { + constructor(private studentDataService: StudentDataService) {} + + getFeedbackText(component: DialogGuidanceStudentComponent, feedbackRule: FeedbackRule): string { + return component.isVersion1() + ? feedbackRule.feedback + : this.getFeedbackTextVersion2(component, feedbackRule); + } + + private getFeedbackTextVersion2( + component: DialogGuidanceStudentComponent, + feedbackRule: FeedbackRule + ) { + const latestSubmitComponentState = this.studentDataService.getLatestSubmitComponentState( + component.nodeId, + component.componentId + ); + return feedbackRule.feedback[ + this.getCountFeedbackRuleShown(latestSubmitComponentState, feedbackRule) % + feedbackRule.feedback.length + ]; + } + + private getCountFeedbackRuleShown( + latestSubmitComponentState: any, + feedbackRule: FeedbackRule + ): number { + return latestSubmitComponentState == null + ? 0 + : latestSubmitComponentState.studentData.responses.filter( + (response) => response.feedbackRuleId === feedbackRule.id + ).length; + } +}