Skip to content

Commit

Permalink
Change token to MyChoiceChosen("choiceId"), which will only work with
Browse files Browse the repository at this point in the history
DynamicPrompt and QuestionBank, which use ReferenceComponent to look up
work for the component
#1409
  • Loading branch information
hirokiterashima committed Sep 13, 2023
1 parent 3d119a1 commit 442336e
Show file tree
Hide file tree
Showing 18 changed files with 163 additions and 77 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { DynamicPrompt } from '../../../assets/wise5/directives/dynamic-prompt/D
styleUrls: ['./edit-dynamic-prompt.component.scss']
})
export class EditDynamicPromptComponent implements OnInit {
allowedReferenceComponentTypes: string[] = ['OpenResponse'];
protected allowedReferenceComponentTypes: string[] = ['MultipleChoice', 'OpenResponse'];
@Input() componentContent: any;
@Output() dynamicPromptChangedEvent = new EventEmitter<void>();

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ import { TeacherProjectService } from '../../../assets/wise5/services/teacherPro
styleUrls: ['./edit-question-bank.component.scss']
})
export class EditQuestionBankComponent implements OnInit {
allowedReferenceComponentTypes: string[] = ['OpenResponse'];
allowedReferenceComponentTypes: string[] = ['MultipleChoice', 'OpenResponse'];
@Input() componentContent: any;

constructor(private projectService: TeacherProjectService) {}
Expand Down
11 changes: 4 additions & 7 deletions src/assets/wise5/components/common/cRater/CRaterResponse.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,15 @@
import { Response } from '../feedbackRule/Response';
import { CRaterIdea } from './CRaterIdea';
import { CRaterScore } from './CRaterScore';

export class CRaterResponse {
export class CRaterResponse extends Response {
ideas: CRaterIdea[] = [];
score: number;
scores: CRaterScore[];
submitCounter: number;

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

getDetectedIdeaCount(): number {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ export class FeedbackRule {
}

static isSpecialRule(feedbackRule: FeedbackRule): boolean {
return ['isFinalSubmit', 'isSecondToLastSubmit', 'isNonScorable'].includes(
return ['isDefault', 'isFinalSubmit', 'isSecondToLastSubmit', 'isNonScorable'].includes(
feedbackRule.expression
);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,17 @@
import { Component } from '../../../common/Component';
import { ConstraintService } from '../../../services/constraintService';
import { FeedbackRuleComponent } from '../../feedbackRule/FeedbackRuleComponent';
import { CRaterResponse } from '../cRater/CRaterResponse';
import { FeedbackRule } from './FeedbackRule';
import { FeedbackRuleExpression } from './FeedbackRuleExpression';
import { Response } from './Response';
import { TermEvaluator } from './TermEvaluator/TermEvaluator';
import { TermEvaluatorFactory } from './TermEvaluator/TermEvaluatorFactory';

export class FeedbackRuleEvaluator<T extends CRaterResponse[]> {
export class FeedbackRuleEvaluator<T extends Response[]> {
defaultFeedback = $localize`Thanks for submitting your response.`;
protected factory;
protected referenceComponent: Component;

constructor(
protected component: FeedbackRuleComponent,
Expand Down Expand Up @@ -44,7 +47,7 @@ export class FeedbackRuleEvaluator<T extends CRaterResponse[]> {
return (
this.hasMaxSubmitAndIsFinalSubmitRule(rule) &&
this.component.hasMaxSubmitCountAndUsedAllSubmits(
(responses[responses.length - 1] as CRaterResponse).submitCounter
responses[responses.length - 1].submitCounter
)
);
}
Expand All @@ -56,7 +59,7 @@ export class FeedbackRuleEvaluator<T extends CRaterResponse[]> {
protected satisfiesSecondToLastSubmitRule(responses: T, rule: FeedbackRule): boolean {
return (
this.hasMaxSubmitAndIsSecondToLastSubmitRule(rule) &&
this.isSecondToLastSubmit((responses[responses.length - 1] as CRaterResponse).submitCounter)
this.isSecondToLastSubmit(responses[responses.length - 1].submitCounter)
);
}

Expand Down Expand Up @@ -120,6 +123,7 @@ export class FeedbackRuleEvaluator<T extends CRaterResponse[]> {

protected evaluateTerm(term: string, responses: T): boolean {
const evaluator: TermEvaluator = this.factory.getTermEvaluator(term);
evaluator.setReferenceComponent(this.referenceComponent);
return TermEvaluator.requiresAllResponses(term)
? evaluator.evaluate(responses)
: evaluator.evaluate(responses[responses.length - 1]);
Expand All @@ -137,4 +141,8 @@ export class FeedbackRuleEvaluator<T extends CRaterResponse[]> {
})
);
}

setReferenceComponent(referenceComponent: Component): void {
this.referenceComponent = referenceComponent;
}
}
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { CRaterResponse } from '../cRater/CRaterResponse';
import { FeedbackRule } from './FeedbackRule';
import { FeedbackRuleEvaluator } from './FeedbackRuleEvaluator';
import { Response } from './Response';
import { TermEvaluator } from './TermEvaluator/TermEvaluator';

export class FeedbackRuleEvaluatorMultipleStudents extends FeedbackRuleEvaluator<CRaterResponse[]> {
getFeedbackRules(responses: CRaterResponse[]): FeedbackRule[] {
export class FeedbackRuleEvaluatorMultipleStudents extends FeedbackRuleEvaluator<Response[]> {
getFeedbackRules(responses: Response[]): FeedbackRule[] {
const matchedRules = this.component
.getFeedbackRules()
.filter((rule) => this.satisfiesRule(responses, Object.assign(new FeedbackRule(), rule)));
Expand All @@ -13,10 +14,7 @@ export class FeedbackRuleEvaluatorMultipleStudents extends FeedbackRuleEvaluator
: [this.getDefaultRule(this.component.getFeedbackRules())];
}

protected satisfiesFinalSubmitRule(
responses: CRaterResponse[],
feedbackRule: FeedbackRule
): boolean {
protected satisfiesFinalSubmitRule(responses: Response[], feedbackRule: FeedbackRule): boolean {
return (
this.hasMaxSubmitAndIsFinalSubmitRule(feedbackRule) &&
responses.some((response: CRaterResponse) => {
Expand All @@ -26,12 +24,12 @@ export class FeedbackRuleEvaluatorMultipleStudents extends FeedbackRuleEvaluator
}

protected satisfiesSecondToLastSubmitRule(
responses: CRaterResponse[],
responses: Response[],
feedbackRule: FeedbackRule
): boolean {
return (
this.hasMaxSubmitAndIsSecondToLastSubmitRule(feedbackRule) &&
responses.some((response: CRaterResponse) => {
responses.some((response: Response) => {
return this.isSecondToLastSubmit(response.submitCounter);
})
);
Expand All @@ -49,9 +47,10 @@ export class FeedbackRuleEvaluatorMultipleStudents extends FeedbackRuleEvaluator
);
}

protected evaluateTerm(term: string, responses: CRaterResponse[]): boolean {
const evaluator: TermEvaluator = this.factory.getTermEvaluator(term, this.constraintService);
return responses.some((response: CRaterResponse) => {
protected evaluateTerm(term: string, responses: Response[]): boolean {
const evaluator: TermEvaluator = this.factory.getTermEvaluator(term);
evaluator.setReferenceComponent(this.referenceComponent);
return responses.some((response: Response) => {
return evaluator.evaluate(response);
});
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ export class FeedbackRuleExpression {
return this.text
.replace(/ /g, '')
.split(
/(hasKIScore\(\d\)|accumulatedIdeaCountEquals\(\d\)|accumulatedIdeaCountLessThan\(\d\)|accumulatedIdeaCountMoreThan\(\d\)|ideaCountEquals\(\S+\)|ideaCountLessThan\(\S+\)|ideaCountMoreThan\(\S+\)|choseChoice\(\S+\)|isSubmitNumber\(\d+\)|&&|\|\||!|\(|\))/g
/(hasKIScore\(\d\)|accumulatedIdeaCountEquals\(\d\)|accumulatedIdeaCountLessThan\(\d\)|accumulatedIdeaCountMoreThan\(\d\)|ideaCountEquals\(\S+\)|ideaCountLessThan\(\S+\)|ideaCountMoreThan\(\S+\)|myChoiceChosen\(\S+\)|isSubmitNumber\(\d+\)|&&|\|\||!|\(|\))/g
)
.filter((el) => el !== '');
}
Expand Down
14 changes: 14 additions & 0 deletions src/assets/wise5/components/common/feedbackRule/Response.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
export class Response {
submitCounter: number;
constructor(jsonObject: any = {}) {
this.populateFields(jsonObject);
}

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

This file was deleted.

Original file line number Diff line number Diff line change
@@ -1,17 +1,22 @@
import { Component } from '../../../../common/Component';
import { ComponentContent } from '../../../../common/ComponentContent';
import { ConstraintService } from '../../../../services/constraintService';
import { CRaterResponse } from '../../cRater/CRaterResponse';
import { ChoseChoiceTermEvaluator } from './ChoseChoiceTermEvaluator';
import { MyChoiceChosenTermEvaluator } from './MyChoiceChosenTermEvaluator';

class ConstraintServiceStub {
evaluateCriteria(criteria: any): boolean {
return true;
}
}

describe('ChoseChoiceTermEvaluator', () => {
describe('MyChoiceChosenTermEvaluator', () => {
let evaluator1, mockConstraintService;
beforeEach(() => {
evaluator1 = new ChoseChoiceTermEvaluator('choseChoice("node1", "componentA", "choice1")');
evaluator1 = new MyChoiceChosenTermEvaluator('myChoiceChosen("choice1")');
evaluator1.setReferenceComponent(
new Component({ id: 'componentA' } as ComponentContent, 'node1')
);
mockConstraintService = new ConstraintServiceStub();
evaluator1.setConstraintService(mockConstraintService as ConstraintService);
});
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import { Response } from '../Response';
import { TermEvaluator } from './TermEvaluator';

export class MyChoiceChosenTermEvaluator extends TermEvaluator {
private choiceId: string;

constructor(term: string) {
super(term);
this.choiceId = term.match(/myChoiceChosen\("(\w+)"\)/)[1];
}

evaluate(response: Response | Response[]): boolean {
return this.constraintService.evaluateCriteria({
name: 'choiceChosen',
params: {
nodeId: this.referenceComponent.nodeId,
componentId: this.referenceComponent.id,
choiceIds: this.choiceId
}
});
}
}
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import { Component } from '../../../../common/Component';
import { ConstraintService } from '../../../../services/constraintService';
import { CRaterResponse } from '../../cRater/CRaterResponse';
import { Response } from '../Response';

export abstract class TermEvaluator {
protected referenceComponent: Component;
protected constraintService: ConstraintService;

constructor(protected term: string) {}
abstract evaluate(response: CRaterResponse | CRaterResponse[]): boolean;
abstract evaluate(response: Response | Response[]): boolean;

static isAccumulatedIdeaCountTerm(term: string): boolean {
return /accumulatedIdeaCount(MoreThan|Equals|LessThan)\([\d+]\)/.test(term);
}

static isChoseChoiceTerm(term: string): boolean {
return /choseChoice\("\w+",\s*"\w+",\s*"\w+"\)/.test(term);
static isMyChoiceChosenTerm(term: string): boolean {
return /myChoiceChosen\("\w+"\)/.test(term);
}

static isHasKIScoreTerm(term: string): boolean {
Expand All @@ -38,7 +40,11 @@ export abstract class TermEvaluator {
);
}

setConstraintService(service: ConstraintService) {
setConstraintService(service: ConstraintService): void {
this.constraintService = service;
}

setReferenceComponent(referenceComponent: Component): void {
this.referenceComponent = referenceComponent;
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { ChoseChoiceTermEvaluator } from './ChoseChoiceTermEvaluator';
import { MyChoiceChosenTermEvaluator } from './MyChoiceChosenTermEvaluator';
import { HasKIScoreTermEvaluator } from './HasKIScoreTermEvaluator';
import { IdeaCountTermEvaluator } from './IdeaCountTermEvaluator';
import { IdeaTermEvaluator } from './IdeaTermEvaluator';
Expand All @@ -11,8 +11,8 @@ describe('TermEvaluatorFactory', () => {
it('should return correct evaluator', () => {
[
{
term: 'choseChoice("node1", "componentA", "choice1")',
instanceType: ChoseChoiceTermEvaluator
term: 'myChoiceChosen("choice1")',
instanceType: MyChoiceChosenTermEvaluator
},
{ term: 'hasKIScore(3)', instanceType: HasKIScoreTermEvaluator },
{ term: 'ideaCountMoreThan(1)', instanceType: IdeaCountTermEvaluator },
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { ConstraintService } from '../../../../services/constraintService';
import { AccumulatedIdeaCountTermEvaluator } from './AccumulatedIdeaCountTermEvaluator';
import { ChoseChoiceTermEvaluator } from './ChoseChoiceTermEvaluator';
import { MyChoiceChosenTermEvaluator } from './MyChoiceChosenTermEvaluator';
import { HasKIScoreTermEvaluator } from './HasKIScoreTermEvaluator';
import { IdeaCountTermEvaluator } from './IdeaCountTermEvaluator';
import { IdeaCountWithResponseIndexTermEvaluator } from './IdeaCountWithResponseIndexTermEvaluator';
Expand All @@ -23,8 +23,8 @@ export class TermEvaluatorFactory {
evaluator = new IsSubmitNumberEvaluator(term);
} else if (TermEvaluator.isAccumulatedIdeaCountTerm(term)) {
evaluator = new AccumulatedIdeaCountTermEvaluator(term);
} else if (TermEvaluator.isChoseChoiceTerm(term)) {
evaluator = new ChoseChoiceTermEvaluator(term);
} else if (TermEvaluator.isMyChoiceChosenTerm(term)) {
evaluator = new MyChoiceChosenTermEvaluator(term);
} else {
evaluator = new IdeaTermEvaluator(term);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { AbstractIdeaCountTermEvaluator } from './AbstractIdeaCountTermEvaluator
const idea1 = new CRaterIdea('1', true);
const idea2 = new CRaterIdea('2', true);
const idea3 = new CRaterIdea('3', true);
const response_with_idea_1 = new CRaterResponse({ ideas: [idea1] });
const response_with_idea_1 = new CRaterResponse({ ideas: [idea1], ba: 1 });
const response_with_idea_2 = new CRaterResponse({ ideas: [idea2] });
const response_with_ideas_1_2 = new CRaterResponse({ ideas: [idea1, idea2] });
const response_with_ideas_1_2_3 = new CRaterResponse({ ideas: [idea1, idea2, idea3] });
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ export class PeerChatQuestionBankComponent implements OnInit {
const referenceComponent = this.getReferenceComponent(this.content.questionBank);
if (
this.content.questionBank.isPeerGroupingTagSpecified() &&
referenceComponent.content.type === 'OpenResponse'
['OpenResponse', 'MultipleChoice'].includes(referenceComponent.content.type)
) {
this.evaluatePeerGroup(referenceComponent);
}
Expand Down Expand Up @@ -75,8 +75,8 @@ export class PeerChatQuestionBankComponent implements OnInit {
): QuestionBankRule[] {
const cRaterResponses = peerGroupStudentData.map((peerMemberData: PeerGroupStudentData) => {
return new CRaterResponse({
ideas: peerMemberData.annotation.data.ideas,
scores: peerMemberData.annotation.data.scores,
ideas: peerMemberData.annotation?.data.ideas,
scores: peerMemberData.annotation?.data.scores,
submitCounter: peerMemberData.studentWork.studentData.submitCounter
});
});
Expand All @@ -88,6 +88,7 @@ export class PeerChatQuestionBankComponent implements OnInit {
),
this.constraintService
);
feedbackRuleEvaluator.setReferenceComponent(referenceComponent);
return this.filterQuestions(
feedbackRuleEvaluator.getFeedbackRules(cRaterResponses) as QuestionBankRule[],
this.content.questionBank.maxQuestionsToShow
Expand Down
Loading

0 comments on commit 442336e

Please sign in to comment.