diff --git a/src/assets/wise5/directives/dynamic-prompt/DynamicPromptEvaluator.ts b/src/assets/wise5/directives/dynamic-prompt/DynamicPromptEvaluator.ts new file mode 100644 index 00000000000..bb7aa1df0c7 --- /dev/null +++ b/src/assets/wise5/directives/dynamic-prompt/DynamicPromptEvaluator.ts @@ -0,0 +1,78 @@ +import { Observable, concatMap, map } from 'rxjs'; +import { Component } from '../../common/Component'; +import { PeerGroupStudentData } from '../../../../app/domain/peerGroupStudentData'; +import { PeerGroup } from '../../components/peerChat/PeerGroup'; +import { DynamicPromptComponent } from './dynamic-prompt.component'; +import { FeedbackRuleEvaluator } from '../../components/common/feedbackRule/FeedbackRuleEvaluator'; +import { Response } from '../../components/common/feedbackRule/Response'; +import { FeedbackRuleEvaluatorMultipleStudents } from '../../components/common/feedbackRule/FeedbackRuleEvaluatorMultipleStudents'; +import { FeedbackRuleComponent } from '../../components/feedbackRule/FeedbackRuleComponent'; +import { FeedbackRule } from '../../components/common/feedbackRule/FeedbackRule'; + +export abstract class DynamicPromptEvaluator { + constructor(protected component: DynamicPromptComponent) {} + + evaluate(referenceComponent: Component): void { + if (this.component.dynamicPrompt.isPeerGroupingTagSpecified()) { + this.evaluatePeerGroup(referenceComponent); + } else { + this.evaluatePersonal(referenceComponent); + } + } + + protected getFeedbackRuleEvaluator( + referenceComponent: Component + ): FeedbackRuleEvaluator { + const evaluator = this.component.dynamicPrompt.isPeerGroupingTagSpecified() + ? new FeedbackRuleEvaluatorMultipleStudents( + new FeedbackRuleComponent( + this.component.dynamicPrompt.getRules(), + referenceComponent.content.maxSubmitCount, + false + ), + this.component.constraintService + ) + : new FeedbackRuleEvaluator( + new FeedbackRuleComponent( + this.component.dynamicPrompt.getRules(), + referenceComponent.content.maxSubmitCount, + false + ), + this.component.constraintService + ); + evaluator.setReferenceComponent(referenceComponent); + return evaluator; + } + + protected getPeerGroupData(): Observable { + return this.component.peerGroupService + .retrievePeerGroup(this.component.dynamicPrompt.getPeerGroupingTag()) + .pipe( + concatMap((peerGroup: PeerGroup) => { + return this.component.peerGroupService + .retrieveDynamicPromptStudentData( + peerGroup.id, + this.component.nodeId, + this.component.componentId + ) + .pipe( + map((peerGroupStudentData: PeerGroupStudentData[]) => { + return peerGroupStudentData; + }) + ); + }) + ); + } + + protected getSubmitCounter(componentState: any): number { + return componentState.studentData.submitCounter; + } + + protected setPromptAndEmitRule(feedbackRule: FeedbackRule): void { + this.component.prompt = feedbackRule.prompt; + this.component.dynamicPromptChanged.emit(feedbackRule); // TODO: change to two-way binding variable + } + + abstract evaluatePeerGroup(referenceComponent: Component): void; + abstract evaluatePersonal(referenceComponent: Component): void; +} diff --git a/src/assets/wise5/directives/dynamic-prompt/DynamicPromptMultipleChoiceEvaluator.ts b/src/assets/wise5/directives/dynamic-prompt/DynamicPromptMultipleChoiceEvaluator.ts new file mode 100644 index 00000000000..0d552382bb2 --- /dev/null +++ b/src/assets/wise5/directives/dynamic-prompt/DynamicPromptMultipleChoiceEvaluator.ts @@ -0,0 +1,32 @@ +import { PeerGroupStudentData } from '../../../../app/domain/peerGroupStudentData'; +import { Component } from '../../common/Component'; +import { Response } from '../../components/common/feedbackRule/Response'; +import { DynamicPromptEvaluator } from './DynamicPromptEvaluator'; + +export class DynamicPromptMultipleChoiceEvaluator extends DynamicPromptEvaluator { + evaluatePeerGroup(referenceComponent: Component): void { + this.getPeerGroupData().subscribe((peerGroupStudentData: PeerGroupStudentData[]) => { + const responses = peerGroupStudentData.map((peerMemberData: PeerGroupStudentData) => { + return new Response({ + submitCounter: this.getSubmitCounter(peerMemberData.studentWork) + }); + }); + const feedbackRuleEvaluator = this.getFeedbackRuleEvaluator(referenceComponent); + this.setPromptAndEmitRule(feedbackRuleEvaluator.getFeedbackRule(responses)); + }); + } + + evaluatePersonal(referenceComponent: Component): void { + const feedbackRuleEvaluator = this.getFeedbackRuleEvaluator(referenceComponent); + const nodeId = this.component.dynamicPrompt.getReferenceNodeId(); + const componentId = referenceComponent.content.id; + const latestComponentState = this.component.dataService.getLatestComponentStateByNodeIdAndComponentId( + nodeId, + componentId + ); + const response = new Response({ + submitCounter: this.getSubmitCounter(latestComponentState) + }); + this.setPromptAndEmitRule(feedbackRuleEvaluator.getFeedbackRule([response])); + } +} diff --git a/src/assets/wise5/directives/dynamic-prompt/DynamicPromptOpenResponseEvaluator.ts b/src/assets/wise5/directives/dynamic-prompt/DynamicPromptOpenResponseEvaluator.ts new file mode 100644 index 00000000000..310de2b0c84 --- /dev/null +++ b/src/assets/wise5/directives/dynamic-prompt/DynamicPromptOpenResponseEvaluator.ts @@ -0,0 +1,44 @@ +import { PeerGroupStudentData } from '../../../../app/domain/peerGroupStudentData'; +import { Component } from '../../common/Component'; +import { CRaterResponse } from '../../components/common/cRater/CRaterResponse'; +import { DynamicPromptEvaluator } from './DynamicPromptEvaluator'; + +export class DynamicPromptOpenResponseEvaluator extends DynamicPromptEvaluator { + evaluatePeerGroup(referenceComponent: Component): void { + this.getPeerGroupData().subscribe((peerGroupStudentData: PeerGroupStudentData[]) => { + const cRaterResponses = peerGroupStudentData.map((peerMemberData: PeerGroupStudentData) => { + return new CRaterResponse({ + ideas: peerMemberData.annotation.data.ideas, + scores: peerMemberData.annotation.data.scores, + submitCounter: this.getSubmitCounter(peerMemberData.studentWork) + }); + }); + const feedbackRuleEvaluator = this.getFeedbackRuleEvaluator(referenceComponent); + this.setPromptAndEmitRule(feedbackRuleEvaluator.getFeedbackRule(cRaterResponses)); + }); + } + + evaluatePersonal(referenceComponent: Component): void { + const nodeId = this.component.dynamicPrompt.getReferenceNodeId(); + const componentId = referenceComponent.content.id; + const latestComponentState = this.component.dataService.getLatestComponentStateByNodeIdAndComponentId( + nodeId, + componentId + ); + const latestAutoScoreAnnotation = this.component.annotationService.getLatestScoreAnnotation( + nodeId, + componentId, + this.component.configService.getWorkgroupId(), + 'autoScore' + ); + if (latestComponentState != null && latestAutoScoreAnnotation != null) { + const cRaterResponse = new CRaterResponse({ + ideas: latestAutoScoreAnnotation.data.ideas, + scores: latestAutoScoreAnnotation.data.scores, + submitCounter: this.getSubmitCounter(latestComponentState) + }); + const feedbackRuleEvaluator = this.getFeedbackRuleEvaluator(referenceComponent); + this.setPromptAndEmitRule(feedbackRuleEvaluator.getFeedbackRule([cRaterResponse])); + } + } +} diff --git a/src/assets/wise5/directives/dynamic-prompt/dynamic-prompt.component.spec.ts b/src/assets/wise5/directives/dynamic-prompt/dynamic-prompt.component.spec.ts index 0900c20b968..d5d016d5f1c 100644 --- a/src/assets/wise5/directives/dynamic-prompt/dynamic-prompt.component.spec.ts +++ b/src/assets/wise5/directives/dynamic-prompt/dynamic-prompt.component.spec.ts @@ -62,9 +62,9 @@ function peerGroupingTagDisabled(): void { }); it('should display the post prompt', () => { - const prompts = fixture.debugElement.nativeElement.querySelectorAll('.prompt'); - const lastPrompt = prompts[prompts.length - 1]; - expect(lastPrompt.textContent).toEqual(postPrompt); + expect(fixture.debugElement.nativeElement.querySelectorAll('.prompt')[2].textContent).toEqual( + postPrompt + ); }); it('should display the dynamic prompt', () => { @@ -114,9 +114,9 @@ function createPeerGroupStudentData( } function expectDynamicPromptToEqual(text: string): void { - const prompts = fixture.debugElement.nativeElement.querySelectorAll('.prompt'); - const dynamicPrompt = prompts[prompts.length - 2]; - expect(dynamicPrompt.textContent).toEqual(text); + expect(fixture.debugElement.nativeElement.querySelectorAll('.prompt')[1].textContent).toEqual( + text + ); } function createDynamicPrompt(): DynamicPrompt { diff --git a/src/assets/wise5/directives/dynamic-prompt/dynamic-prompt.component.ts b/src/assets/wise5/directives/dynamic-prompt/dynamic-prompt.component.ts index 7a490f2be81..4d8e34350bb 100644 --- a/src/assets/wise5/directives/dynamic-prompt/dynamic-prompt.component.ts +++ b/src/assets/wise5/directives/dynamic-prompt/dynamic-prompt.component.ts @@ -1,23 +1,15 @@ import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core'; -import { Observable } from 'rxjs'; -import { concatMap, map } from 'rxjs/operators'; -import { PeerGroupStudentData } from '../../../../app/domain/peerGroupStudentData'; -import { CRaterResponse } from '../../components/common/cRater/CRaterResponse'; import { FeedbackRule } from '../../components/common/feedbackRule/FeedbackRule'; -import { FeedbackRuleEvaluator } from '../../components/common/feedbackRule/FeedbackRuleEvaluator'; -import { FeedbackRuleComponent } from '../../components/feedbackRule/FeedbackRuleComponent'; -import { OpenResponseContent } from '../../components/openResponse/OpenResponseContent'; -import { PeerGroup } from '../../components/peerChat/PeerGroup'; import { AnnotationService } from '../../services/annotationService'; import { ConfigService } from '../../services/configService'; import { PeerGroupService } from '../../services/peerGroupService'; import { ProjectService } from '../../services/projectService'; import { StudentDataService } from '../../services/studentDataService'; import { DynamicPrompt } from './DynamicPrompt'; -import { FeedbackRuleEvaluatorMultipleStudents } from '../../components/common/feedbackRule/FeedbackRuleEvaluatorMultipleStudents'; import { ConstraintService } from '../../services/constraintService'; import { Component as WISEComponent } from '../../common/Component'; -import { Response } from '../../components/common/feedbackRule/Response'; +import { DynamicPromptOpenResponseEvaluator } from './DynamicPromptOpenResponseEvaluator'; +import { DynamicPromptMultipleChoiceEvaluator } from './DynamicPromptMultipleChoiceEvaluator'; @Component({ selector: 'dynamic-prompt', @@ -25,27 +17,38 @@ import { Response } from '../../components/common/feedbackRule/Response'; styleUrls: ['./dynamic-prompt.component.scss'] }) export class DynamicPromptComponent implements OnInit { + annotationService: AnnotationService; @Input() componentId: string; + configService: ConfigService; + constraintService: ConstraintService; + dataService: StudentDataService; @Input() dynamicPrompt: DynamicPrompt; @Output() dynamicPromptChanged: EventEmitter = new EventEmitter(); @Input() nodeId: string; prompt: string; + peerGroupService: PeerGroupService; constructor( - private annotationService: AnnotationService, - private configService: ConfigService, - private constraintService: ConstraintService, - private peerGroupService: PeerGroupService, - private projectService: ProjectService, - private dataService: StudentDataService - ) {} + annotationService: AnnotationService, + configService: ConfigService, + constraintService: ConstraintService, + dataService: StudentDataService, + peerGroupService: PeerGroupService, + private projectService: ProjectService + ) { + this.annotationService = annotationService; + this.configService = configService; + this.constraintService = constraintService; + this.dataService = dataService; + this.peerGroupService = peerGroupService; + } ngOnInit(): void { const referenceComponent = this.getReferenceComponent(this.dynamicPrompt); if (referenceComponent.content.type === 'OpenResponse') { - this.evaluateOpenResponseComponent(referenceComponent.content as OpenResponseContent); + new DynamicPromptOpenResponseEvaluator(this).evaluate(referenceComponent); } else if (referenceComponent.content.type === 'MultipleChoice') { - this.evaluateMultipleChoiceComponent(referenceComponent); + new DynamicPromptMultipleChoiceEvaluator(this).evaluate(referenceComponent); } } @@ -54,152 +57,4 @@ export class DynamicPromptComponent implements OnInit { const componentId = dynamicPrompt.getReferenceComponentId(); return new WISEComponent(this.projectService.getComponent(nodeId, componentId), nodeId); } - - private evaluateOpenResponseComponent(referenceComponentContent: OpenResponseContent): void { - if (this.dynamicPrompt.isPeerGroupingTagSpecified()) { - this.evaluatePeerGroupOpenResponse(referenceComponentContent); - } else { - this.evaluatePersonalOpenResponse(referenceComponentContent); - } - } - - private evaluatePeerGroupOpenResponse(referenceComponentContent: OpenResponseContent): void { - this.getPeerGroupData( - this.dynamicPrompt.getPeerGroupingTag(), - this.nodeId, - this.componentId - ).subscribe((peerGroupStudentData: PeerGroupStudentData[]) => { - const cRaterResponses = peerGroupStudentData.map((peerMemberData: PeerGroupStudentData) => { - return new CRaterResponse({ - ideas: peerMemberData.annotation.data.ideas, - scores: peerMemberData.annotation.data.scores, - submitCounter: this.getSubmitCounter(peerMemberData.studentWork) - }); - }); - const feedbackRuleEvaluator = new FeedbackRuleEvaluatorMultipleStudents( - new FeedbackRuleComponent( - this.dynamicPrompt.getRules(), - referenceComponentContent.maxSubmitCount, - false - ), - this.constraintService - ); - const feedbackRule: FeedbackRule = feedbackRuleEvaluator.getFeedbackRule(cRaterResponses); - this.prompt = feedbackRule.prompt; - this.dynamicPromptChanged.emit(feedbackRule); // TODO: change to two-way binding variable - }); - } - - private getPeerGroupData( - peerGroupingTag: string, - nodeId: string, - componentId: string - ): Observable { - return this.peerGroupService.retrievePeerGroup(peerGroupingTag).pipe( - concatMap((peerGroup: PeerGroup) => { - return this.peerGroupService - .retrieveDynamicPromptStudentData(peerGroup.id, nodeId, componentId) - .pipe( - map((peerGroupStudentData: PeerGroupStudentData[]) => { - return peerGroupStudentData; - }) - ); - }) - ); - } - - private evaluatePersonalOpenResponse(referenceComponentContent: OpenResponseContent): void { - const nodeId = this.dynamicPrompt.getReferenceNodeId(); - const componentId = referenceComponentContent.id; - const latestComponentState = this.dataService.getLatestComponentStateByNodeIdAndComponentId( - nodeId, - componentId - ); - const latestAutoScoreAnnotation = this.annotationService.getLatestScoreAnnotation( - nodeId, - componentId, - this.configService.getWorkgroupId(), - 'autoScore' - ); - if (latestComponentState != null && latestAutoScoreAnnotation != null) { - const cRaterResponse = new CRaterResponse({ - ideas: latestAutoScoreAnnotation.data.ideas, - scores: latestAutoScoreAnnotation.data.scores, - submitCounter: this.getSubmitCounter(latestComponentState) - }); - const feedbackRuleEvaluator = new FeedbackRuleEvaluator( - new FeedbackRuleComponent( - this.dynamicPrompt.getRules(), - referenceComponentContent.maxSubmitCount, - false - ), - this.constraintService - ); - const feedbackRule: FeedbackRule = feedbackRuleEvaluator.getFeedbackRule([cRaterResponse]); - this.prompt = feedbackRule.prompt; - this.dynamicPromptChanged.emit(feedbackRule); - } - } - - private evaluateMultipleChoiceComponent(referenceComponent: WISEComponent): void { - if (this.dynamicPrompt.isPeerGroupingTagSpecified()) { - this.evaluatePeerGroupMultipleChoice(referenceComponent); - } else { - this.evaluatePersonalMultipleChoice(referenceComponent); - } - } - - private evaluatePeerGroupMultipleChoice(referenceComponent: WISEComponent): void { - this.getPeerGroupData( - this.dynamicPrompt.getPeerGroupingTag(), - this.nodeId, - this.componentId - ).subscribe((peerGroupStudentData: PeerGroupStudentData[]) => { - const responses = peerGroupStudentData.map((peerMemberData: PeerGroupStudentData) => { - return new Response({ - submitCounter: this.getSubmitCounter(peerMemberData.studentWork) - }); - }); - const feedbackRuleEvaluator = new FeedbackRuleEvaluatorMultipleStudents( - new FeedbackRuleComponent( - this.dynamicPrompt.getRules(), - referenceComponent.content.maxSubmitCount, - false - ), - this.constraintService - ); - feedbackRuleEvaluator.setReferenceComponent(referenceComponent); - const feedbackRule: FeedbackRule = feedbackRuleEvaluator.getFeedbackRule(responses); - this.prompt = feedbackRule.prompt; - this.dynamicPromptChanged.emit(feedbackRule); // TODO: change to two-way binding variable - }); - } - - private evaluatePersonalMultipleChoice(referenceComponent: WISEComponent): void { - const feedbackRuleEvaluator = new FeedbackRuleEvaluator( - new FeedbackRuleComponent( - this.dynamicPrompt.getRules(), - referenceComponent.content.maxSubmitCount, - false - ), - this.constraintService - ); - feedbackRuleEvaluator.setReferenceComponent(referenceComponent); - const nodeId = this.dynamicPrompt.getReferenceNodeId(); - const componentId = referenceComponent.content.id; - const latestComponentState = this.dataService.getLatestComponentStateByNodeIdAndComponentId( - nodeId, - componentId - ); - const response = new Response({ - submitCounter: this.getSubmitCounter(latestComponentState) - }); - const feedbackRule: FeedbackRule = feedbackRuleEvaluator.getFeedbackRule([response]); - this.prompt = feedbackRule.prompt; - this.dynamicPromptChanged.emit(feedbackRule); - } - - private getSubmitCounter(componentState: any): number { - return componentState.studentData.submitCounter; - } }