Skip to content

Commit

Permalink
feat(Dialog Guidance): Show different feedback for same FeedbackRule (#…
Browse files Browse the repository at this point in the history
…581)

* feat(Dialog Guidance): Show different feedback for same FeedbackRule

Add check for version in content. V2 assumes that each feedback rule has
an id, and uses the id to determine if student hit the same FeedbackRule
before, and which feedback within that rule to show next (use mod).

#578

* fix(Dialog Guidance): Properly handle default feedback in version 2

#578

Co-authored-by: Geoffrey Kwan <geoffreykwan@gmail.com>
  • Loading branch information
hirokiterashima and geoffreykwan authored May 6, 2022
1 parent c8ebb8a commit ccac2bf
Show file tree
Hide file tree
Showing 9 changed files with 155 additions and 15 deletions.
76 changes: 76 additions & 0 deletions src/app/services/dialogGuidanceFeedbackService.spec.ts
Original file line number Diff line number Diff line change
@@ -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');
});
});
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { CRaterIdea } from './CRaterIdea';
import { DialogResponse } from './DialogResponse';

export class ComputerDialogResponse extends DialogResponse {
feedbackRuleId?: string;
ideas: CRaterIdea[];
user: string = 'Computer';

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -124,6 +125,7 @@ describe('DialogGuidanceFeedbackRuleEvaluator', () => {
ComputerAvatarService,
CRaterService,
ConfigService,
DialogGuidanceFeedbackService,
DialogGuidanceService,
{ provide: NodeService, useClass: MockNodeService },
{ provide: NotebookService, useClass: MockService },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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]
})
);
}
Expand Down
3 changes: 2 additions & 1 deletion src/assets/wise5/components/dialogGuidance/FeedbackRule.ts
Original file line number Diff line number Diff line change
@@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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';
Expand Down Expand Up @@ -65,6 +66,7 @@ describe('DialogGuidanceStudentComponent', () => {
ComputerAvatarService,
CRaterService,
ConfigService,
DialogGuidanceFeedbackService,
DialogGuidanceService,
{ provide: NodeService, useClass: MockNodeService },
{ provide: NotebookService, useClass: MockService },
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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',
Expand All @@ -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,
Expand Down Expand Up @@ -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() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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 {}
40 changes: 40 additions & 0 deletions src/assets/wise5/services/dialogGuidanceFeedbackService.ts
Original file line number Diff line number Diff line change
@@ -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()
? <string>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;
}
}

0 comments on commit ccac2bf

Please sign in to comment.