Skip to content

Commit

Permalink
feat(QuestionBank): Display based on matched feedback rule (#910)
Browse files Browse the repository at this point in the history
  • Loading branch information
hirokiterashima authored Nov 22, 2022
1 parent 42fd777 commit 933c0f3
Show file tree
Hide file tree
Showing 18 changed files with 221 additions and 61 deletions.
2 changes: 2 additions & 0 deletions src/assets/wise5/common/ComponentContent.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { QuestionBank } from '../components/peerChat/peer-chat-question-bank/QuestionBank';
import { DynamicPrompt } from '../directives/dynamic-prompt/DynamicPrompt';

export interface ComponentContent {
Expand All @@ -7,6 +8,7 @@ export interface ComponentContent {
excludeFromTotalScore?: boolean;
maxScore?: number;
prompt?: string;
questionBank?: QuestionBank;
rubric?: string;
showSaveButton?: boolean;
showSubmitButton?: boolean;
Expand Down
37 changes: 37 additions & 0 deletions src/assets/wise5/components/common/ReferenceComponentRules.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { ReferenceComponent } from '../../../../app/domain/referenceComponent';
import { FeedbackRule } from './feedbackRule/FeedbackRule';

export abstract class ReferenceComponentRules {
enabled: boolean;
peerGroupingTag?: string;
referenceComponent: ReferenceComponent;
rules: FeedbackRule[];

constructor(jsonObject: any = {}) {
for (const key of Object.keys(jsonObject)) {
if (jsonObject[key] != null) {
this[key] = jsonObject[key];
}
}
}

getPeerGroupingTag(): string {
return this.peerGroupingTag;
}

getReferenceNodeId(): string {
return this.referenceComponent.nodeId;
}

getReferenceComponentId(): string {
return this.referenceComponent.componentId;
}

getRules(): FeedbackRule[] {
return this.rules;
}

isPeerGroupingTagSpecified(): boolean {
return this.peerGroupingTag != null && this.peerGroupingTag !== '';
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ export interface OpenResponseContent extends ComponentContent {
enableCRater: boolean;
enableNotifications: boolean;
isStudentAudioRecordingEnabled: boolean;
maxSubmitCount?: number;
notificationSettings: any;
starterSentence: any;
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,12 @@
<div *ngIf="dynamicPrompt != null" [innerHTML]="dynamicPrompt.prompt" class="dynamic-prompt">
</div>
<div fxLayout="column" fxLayout.gt-md="row" fxLayoutAlign.gt-md="start start" fxLayoutGap="16px">
<div *ngIf="componentContent.questionBank.length > 0"
<div *ngIf="questionBankRule != null"
fxFlex
fxFlex.gt-md="40"
fxFlexOrder="1"
fxFlexOrder.gt-md="2">
<peer-chat-question-bank [questions]="componentContent.questionBank"></peer-chat-question-bank>
<peer-chat-question-bank [displayedQuestionBankRule]="questionBankRule"></peer-chat-question-bank>
</div>
<div fxFlex
[fxFlex.gt-md]="componentContent.questionBank.length > 0 ? 60 : 100"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { ReferenceComponentRules } from '../../common/ReferenceComponentRules';
import { QuestionBankRule } from './QuestionBankRule';

export class QuestionBank extends ReferenceComponentRules {
rules: QuestionBankRule[];
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { QuestionBank } from './QuestionBank';

export class QuestionBankContent {
componentId: string;
nodeId: string;
questionBank: QuestionBank;

constructor(nodeId: string, componentId: string, questionBank: QuestionBank) {
this.nodeId = nodeId;
this.componentId = componentId;
this.questionBank = questionBank;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { FeedbackRule } from '../../common/feedbackRule/FeedbackRule';

export class QuestionBankRule extends FeedbackRule {
questions: string[];
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,28 @@
import { HttpClientTestingModule } from '@angular/common/http/testing';
import { ComponentFixture, TestBed } from '@angular/core/testing';
import { StudentTeacherCommonServicesModule } from '../../../../../app/student-teacher-common-services.module';
import { PeerChatQuestionBankComponent } from './peer-chat-question-bank.component';
import { ProjectService } from '../../../services/projectService';
import { QuestionBank } from './QuestionBank';
import { ComponentContent } from '../../../common/ComponentContent';

let component: PeerChatQuestionBankComponent;
let fixture: ComponentFixture<PeerChatQuestionBankComponent>;

describe('PeerChatQuestionBankComponent', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [HttpClientTestingModule, StudentTeacherCommonServicesModule],
declarations: [PeerChatQuestionBankComponent]
}).compileComponents();
spyOn(TestBed.inject(ProjectService), 'getComponent').and.returnValue({} as ComponentContent);
fixture = TestBed.createComponent(PeerChatQuestionBankComponent);
component = fixture.componentInstance;
component.content = {
componentId: 'abc',
nodeId: 'node1',
questionBank: new QuestionBank({ referenceComponent: {} })
};
fixture.detectChanges();
});

Expand Down
Original file line number Diff line number Diff line change
@@ -1,17 +1,98 @@
import { Component, Input, OnInit } from '@angular/core';
import { Component, EventEmitter, Input, OnInit, Output } from '@angular/core';
import { Observable } from 'rxjs';
import { PeerGroupService } from '../../../services/peerGroupService';
import { ProjectService } from '../../../services/projectService';
import { OpenResponseContent } from '../../openResponse/OpenResponseContent';
import { QuestionBank } from './QuestionBank';
import { Component as WISEComponent } from '../../../common/Component';
import { PeerGroupStudentData } from '../../../../../app/domain/peerGroupStudentData';
import { CRaterResponse } from '../../common/cRater/CRaterResponse';
import { FeedbackRuleEvaluator } from '../../common/feedbackRule/FeedbackRuleEvaluator';
import { FeedbackRuleComponent } from '../../feedbackRule/FeedbackRuleComponent';
import { QuestionBankRule } from './QuestionBankRule';
import { concatMap, map } from 'rxjs/operators';
import { PeerGroup } from '../PeerGroup';
import { QuestionBankContent } from './QuestionBankContent';

@Component({
selector: 'peer-chat-question-bank',
templateUrl: './peer-chat-question-bank.component.html',
styleUrls: ['./peer-chat-question-bank.component.scss']
})
export class PeerChatQuestionBankComponent implements OnInit {
@Input()
@Input() content: QuestionBankContent;
@Input() displayedQuestionBankRule: QuestionBankRule;
@Output() displayedQuestionBankRuleChange = new EventEmitter<QuestionBankRule>();
questions: string[];

constructor() { }
constructor(private peerGroupService: PeerGroupService, private projectService: ProjectService) {}

ngOnInit(): void {
if (this.displayedQuestionBankRule != null) {
this.questions = this.displayedQuestionBankRule.questions;
} else {
const referenceComponent = this.getReferenceComponent(this.content.questionBank);
if (referenceComponent.content.type === 'OpenResponse') {
this.evaluate(referenceComponent);
}
}
}

private getReferenceComponent(questionBank: QuestionBank): WISEComponent {
const nodeId = questionBank.getReferenceNodeId();
const componentId = questionBank.getReferenceComponentId();
return new WISEComponent(this.projectService.getComponent(nodeId, componentId), nodeId);
}

private evaluate(referenceComponent: WISEComponent): void {
if (this.content.questionBank.isPeerGroupingTagSpecified()) {
this.evaluatePeerGroup(referenceComponent);
}
}

private evaluatePeerGroup(referenceComponent: WISEComponent): void {
this.getPeerGroupData(
this.content.questionBank.getPeerGroupingTag(),
this.content.nodeId,
this.content.componentId
).subscribe((peerGroupStudentData: PeerGroupStudentData[]) => {
const cRaterResponses = peerGroupStudentData.map((peerMemberData: PeerGroupStudentData) => {
return new CRaterResponse({
ideas: peerMemberData.annotation.data.ideas,
scores: peerMemberData.annotation.data.scores,
submitCounter: peerMemberData.studentWork.studentData.submitCounter
});
});
const feedbackRuleEvaluator = new FeedbackRuleEvaluator(
new FeedbackRuleComponent(
this.content.questionBank.getRules(),
(referenceComponent.content as OpenResponseContent).maxSubmitCount,
false
)
);
const feedbackRule: QuestionBankRule = feedbackRuleEvaluator.getFeedbackRule(
cRaterResponses
) as QuestionBankRule;
this.questions = feedbackRule.questions;
this.displayedQuestionBankRuleChange.emit(feedbackRule);
});
}

private getPeerGroupData(
peerGroupingTag: string,
nodeId: string,
componentId: string
): Observable<PeerGroupStudentData[]> {
return this.peerGroupService.retrievePeerGroup(peerGroupingTag).pipe(
concatMap((peerGroup: PeerGroup) => {
return this.peerGroupService
.retrieveQuestionBankStudentData(peerGroup.id, nodeId, componentId)
.pipe(
map((peerGroupStudentData: PeerGroupStudentData[]) => {
return peerGroupStudentData;
})
);
})
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
fxFlex.gt-md="40"
fxFlexOrder="1"
fxFlexOrder.gt-md="2">
<peer-chat-question-bank [questions]="componentContent.questionBank"></peer-chat-question-bank>
<peer-chat-question-bank [displayedQuestionBankRule]="questionBankRule"></peer-chat-question-bank>
</div>
<div fxFlex fxFlex.gt-md="60" fxFlexOrder="2" fxFlexOrder.gt-md="1">
<peer-chat-chat-box [messages]="peerChatMessages"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ export class PeerChatShowWorkComponent extends ComponentShowWorkDirective {
peerChatWorkgroupIds: Set<number> = new Set<number>();
peerChatWorkgroupInfos: any = {};
peerGroup: PeerGroup;
questionBankRule: string[];
requestTimeout: number = 10000;

@Input() workgroupId: number;
Expand Down Expand Up @@ -84,6 +85,7 @@ export class PeerChatShowWorkComponent extends ComponentShowWorkDirective {
this.peerChatMessages = [];
this.peerChatService.setPeerChatMessages(this.peerChatMessages, componentStates);
this.dynamicPrompt = this.getDynamicPrompt(componentStates);
this.questionBankRule = this.getQuestionBankRule(componentStates);
}

private getDynamicPrompt(componentStates: any[]): FeedbackRule {
Expand All @@ -96,6 +98,16 @@ export class PeerChatShowWorkComponent extends ComponentShowWorkDirective {
return null;
}

private getQuestionBankRule(componentStates: any[]): string[] {
for (let c = componentStates.length - 1; c >= 0; c--) {
const questionBank = componentStates[c].studentData.questionBank;
if (questionBank != null) {
return questionBank;
}
}
return null;
}

private addWorkgroupIdsFromPeerChatMessages(
workgroupIds: Set<number>,
componentStates: any[]
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,12 +10,12 @@
fxLayout.gt-sm="row"
fxLayoutAlign.gt-sm="start start"
fxLayoutGap="16px">
<div *ngIf="componentContent.questionBank.length > 0"
<div *ngIf="questionBankContent != null"
fxFlex
fxFlex.gt-sm="40"
fxFlexOrder="1"
fxFlexOrder.gt-sm="2">
<peer-chat-question-bank [questions]="componentContent.questionBank"></peer-chat-question-bank>
<peer-chat-question-bank [content]="questionBankContent" [(displayedQuestionBankRule)]="displayedQuestionBankRule"></peer-chat-question-bank>
</div>
<div fxFlex
fxFlex.gt-sm="60"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -64,8 +64,7 @@ const componentContent = {
],
logicThresholdCount: 0,
logicThresholdPercent: 0,
maxMembershipCount: 2,
questionBank: ['What color is the sky?', 'How deep is the ocean?']
maxMembershipCount: 2
};

class ComponentState {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,9 @@ import { UtilService } from '../../../services/utilService';
import { FeedbackRule } from '../../common/feedbackRule/FeedbackRule';
import { ComponentStudent } from '../../component-student.component';
import { ComponentService } from '../../componentService';
import { QuestionBank } from '../peer-chat-question-bank/QuestionBank';
import { QuestionBankContent } from '../peer-chat-question-bank/QuestionBankContent';
import { QuestionBankRule } from '../peer-chat-question-bank/QuestionBankRule';
import { PeerChatMessage } from '../PeerChatMessage';
import { PeerChatService } from '../peerChatService';
import { PeerGroup } from '../PeerGroup';
Expand All @@ -24,6 +27,7 @@ import { PeerGroup } from '../PeerGroup';
styleUrls: ['./peer-chat-student.component.scss']
})
export class PeerChatStudentComponent extends ComponentStudent {
displayedQuestionBankRule: QuestionBankRule;
dynamicPrompt: FeedbackRule;
isPeerChatWorkgroupsResponseReceived: boolean;
isPeerChatWorkgroupsAvailable: boolean;
Expand All @@ -33,6 +37,7 @@ export class PeerChatStudentComponent extends ComponentStudent {
peerChatWorkgroupInfos: any = {};
peerGroup: PeerGroup;
peerGroupingTag: string;
questionBankContent: QuestionBankContent;
requestTimeout: number = 10000;
response: string;

Expand Down Expand Up @@ -70,6 +75,13 @@ export class PeerChatStudentComponent extends ComponentStudent {
this.peerGroupingTag = this.componentContent.peerGroupingTag;
this.requestChatWorkgroups();
this.registerStudentWorkReceivedListener();
if (this.component.content.questionBank != null) {
this.questionBankContent = new QuestionBankContent(
this.component.nodeId,
this.component.id,
new QuestionBank(this.component.content.questionBank)
);
}
}

private registerStudentWorkReceivedListener(): void {
Expand Down Expand Up @@ -166,6 +178,9 @@ export class PeerChatStudentComponent extends ComponentStudent {
if (this.dynamicPrompt != null) {
componentState.studentData.dynamicPrompt = this.dynamicPrompt;
}
if (this.displayedQuestionBankRule != null) {
componentState.studentData.questionBank = this.displayedQuestionBankRule;
}
componentState.componentType = 'PeerChat';
componentState.nodeId = this.nodeId;
componentState.componentId = this.componentId;
Expand Down
39 changes: 2 additions & 37 deletions src/assets/wise5/directives/dynamic-prompt/DynamicPrompt.ts
Original file line number Diff line number Diff line change
@@ -1,41 +1,6 @@
import { FeedbackRule } from '../../components/common/feedbackRule/FeedbackRule';
import { ReferenceComponentRules } from '../../components/common/ReferenceComponentRules';

export class DynamicPrompt {
enabled: boolean;
peerGroupingTag?: string;
export class DynamicPrompt extends ReferenceComponentRules {
postPrompt?: string;
prePrompt?: string;
referenceComponent: {
componentId: string;
nodeId: string;
};
rules: FeedbackRule[];

constructor(jsonObject: any = {}) {
for (const key of Object.keys(jsonObject)) {
if (jsonObject[key] != null) {
this[key] = jsonObject[key];
}
}
}

getReferenceNodeId(): string {
return this.referenceComponent.nodeId;
}

getReferenceComponentId(): string {
return this.referenceComponent.componentId;
}

getRules(): FeedbackRule[] {
return this.rules;
}

getPeerGroupingTag(): string {
return this.peerGroupingTag;
}

isPeerGroupingTagSpecified(): boolean {
return this.peerGroupingTag != null && this.peerGroupingTag !== '';
}
}
Loading

0 comments on commit 933c0f3

Please sign in to comment.