Skip to content

Commit

Permalink
feat(MatchStudent): import ideas detected in Dialog Guidance as choic…
Browse files Browse the repository at this point in the history
…es (#2054)

Co-authored-by:  Aaron Detre
  • Loading branch information
hirokiterashima authored Jan 23, 2025
1 parent 7ee06b3 commit 1467fd6
Show file tree
Hide file tree
Showing 9 changed files with 131 additions and 13 deletions.
40 changes: 37 additions & 3 deletions src/app/services/cRaterService.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,23 +6,36 @@ import { CRaterIdea } from '../../assets/wise5/components/common/cRater/CRaterId
import { CRaterScore } from '../../assets/wise5/components/common/cRater/CRaterScore';
import { RawCRaterResponse } from '../../assets/wise5/components/common/cRater/RawCRaterResponse';
import { provideHttpClient, withInterceptorsFromDi } from '@angular/common/http';
import { ProjectService } from '../../assets/wise5/services/projectService';
import { ComponentContent } from '../../assets/wise5/common/ComponentContent';
let service: CRaterService;
let configService: ConfigService;
let http: HttpTestingController;
class MockProjectService {
getComponent(): ComponentContent {
return {} as ComponentContent;
}
}

describe('CRaterService', () => {
beforeEach(() => {
TestBed.configureTestingModule({
imports: [],
providers: [ConfigService, CRaterService, provideHttpClient(withInterceptorsFromDi()), provideHttpClientTesting()]
});
providers: [
ConfigService,
CRaterService,
{ provide: ProjectService, useClass: MockProjectService },
provideHttpClient(withInterceptorsFromDi()),
provideHttpClientTesting()
]
});
http = TestBed.inject(HttpTestingController);
configService = TestBed.inject(ConfigService);
service = TestBed.inject(CRaterService);
});

makeCRaterScoringRequest();
getCRaterItemId();
getCRaterRubric();
isCRaterEnabled();
isCRaterScoreOnEvent();
getCRaterFeedbackTextByScore();
Expand Down Expand Up @@ -264,3 +277,24 @@ function getDataFromResponse() {
});
});
}

function getCRaterRubric() {
describe('getCRaterRubric', () => {
let projectService;
beforeEach(() => {
projectService = TestBed.inject(ProjectService);
});
it('should get CRater rubric when rubric exists on the component', () => {
spyOn(projectService, 'getComponent').and.returnValue({
cRaterRubric: { ideas: [{ name: '1', studentText: 'Idea 1' }] }
} as unknown as ComponentContent);
const cRaterRubric = service.getCRaterRubric('nodeId', 'componentId');
expect(cRaterRubric.getStudentTextForIdea('1')).toEqual('Idea 1');
});
it('should get an empty CRater rubric when rubric does not exists on the component', () => {
spyOn(projectService, 'getComponent').and.returnValue({} as unknown as ComponentContent);
const cRaterRubric = service.getCRaterRubric('nodeId', 'componentId');
expect(cRaterRubric.getStudentTextForIdea('1')).toEqual('1');
});
});
}
2 changes: 2 additions & 0 deletions src/assets/wise5/common/ComponentContent.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
import { CRaterRubric } from '../components/common/cRater/CRaterRubric';
import { QuestionBank } from '../components/peerChat/peer-chat-question-bank/QuestionBank';
import { DynamicPrompt } from '../directives/dynamic-prompt/DynamicPrompt';

export interface ComponentContent {
id: string;
connectedComponents?: any[];
constraints?: any[];
cRaterRubric?: CRaterRubric;
dynamicPrompt?: DynamicPrompt;
excludeFromTotalScore?: boolean;
maxScore?: number;
Expand Down
1 change: 1 addition & 0 deletions src/assets/wise5/components/common/cRater/CRaterIdea.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export class CRaterIdea {
name: string;
detected: boolean;
characterOffsets: any[];
studentText?: string;

constructor(name: string, detected: boolean) {
this.name = name;
Expand Down
13 changes: 13 additions & 0 deletions src/assets/wise5/components/common/cRater/CRaterRubric.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { CRaterIdea } from './CRaterIdea';

export class CRaterRubric {
private ideas: CRaterIdea[] = [];

constructor(rubric: any = { ideas: [] }) {
this.ideas = rubric.ideas;
}

getStudentTextForIdea(ideaId: string): string {
return this.ideas.find((idea) => idea.name === ideaId)?.studentText ?? ideaId;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ import { MatchContent } from '../MatchContent';
styleUrls: ['edit-match-advanced.component.scss']
})
export class EditMatchAdvancedComponent extends EditAdvancedComponentComponent {
allowedConnectedComponentTypes = ['Match'];
allowedConnectedComponentTypes = ['DialogGuidance', 'Match'];
componentContent: MatchContent;
}
Original file line number Diff line number Diff line change
Expand Up @@ -134,6 +134,7 @@ describe('MatchStudentDefaultComponent', () => {
checkAnswer();
checkAnswerAndDisplayFeedback();
createComponentStateObject();
createMergedComponentState();
isAuthorHasSpecifiedACorrectPosition();
getFeedbackObject();
getCleanedValue();
Expand Down Expand Up @@ -445,6 +446,39 @@ function createComponentStateObject() {
});
}

function createMergedComponentState() {
describe('createMergedComponentState()', () => {
it('should add detected ideas to source bucket if componentType is DialogGuidance', () => {
const componentState = {
componentType: 'DialogGuidance',
studentData: {
responses: [
{
ideas: [
{ name: '1', detected: true },
{ name: '2', detected: false },
{ name: '3', detected: false }
]
},
{
ideas: [
{ name: '1', detected: false },
{ name: '2', detected: false },
{ name: '3', detected: true }
]
}
]
}
};
expect(component.buckets[0].items.length).toEqual(3);
component.createMergedComponentState([componentState]);
expect(component.buckets[0].items.length).toEqual(5);
expect(component.buckets[0].items[3].value).toEqual('1');
expect(component.buckets[0].items[4].value).toEqual('3');
});
});
}

function isAuthorHasSpecifiedACorrectPosition() {
describe('isAuthorHasSpecifiedACorrectPosition', () => {
it(`should check if author has specified the choice has a correct position when it does
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ import { Container } from '../container';
import { Item } from '../item';
import { hasConnectedComponent } from '../../../../common/ComponentContent';
import { Bucket, mergeBucket } from '../../bucket';
import { CRaterService } from '../../../../services/cRaterService';
import { CRaterRubric } from '../../../common/cRater/CRaterRubric';

@Component({
templateUrl: 'match-student-default.component.html',
Expand All @@ -50,6 +52,7 @@ export class MatchStudentDefault extends ComponentStudent {
protected assetService: StudentAssetService,
protected componentService: ComponentService,
protected configService: ConfigService,
private craterService: CRaterService,
protected dataService: StudentDataService,
protected dialog: MatDialog,
protected matchService: MatchService,
Expand Down Expand Up @@ -567,18 +570,39 @@ export class MatchStudentDefault extends ComponentStudent {

createMergedComponentState(componentStates: any[]): any[] {
const mergedBuckets = [];
for (const componentState of componentStates) {
for (const bucket of componentState.studentData.buckets) {
mergeBucket(mergedBuckets, bucket);
componentStates.forEach((componentState) => {
if (componentState.componentType === 'Match') {
for (const bucket of componentState.studentData.buckets) {
mergeBucket(mergedBuckets, bucket);
}
} else if (componentState.componentType === 'DialogGuidance') {
this.addIdeasToSourceBucket(
componentState.studentData.responses,
this.craterService.getCRaterRubric(componentState.nodeId, componentState.componentId)
);
}
}
});
const mergedComponentState: any = this.createNewComponentState();
mergedComponentState.studentData = {
buckets: mergedBuckets
};
return mergedComponentState;
}

private addIdeasToSourceBucket(responses: any[], rubric: CRaterRubric): void {
responses.forEach((response) => {
response.ideas
?.filter((idea) => idea.detected)
.forEach((idea) => {
if (!this.choices.some((c) => c.id === idea.name)) {
const choice = new Choice(idea.name, rubric.getStudentTextForIdea(idea.name));
this.choices.push(choice);
this.getBucketById(this.sourceBucketId).items.push(choice);
}
});
});
}

protected addChoice(): void {
this.dialog
.open(AddMatchChoiceDialogComponent, {
Expand Down
12 changes: 11 additions & 1 deletion src/assets/wise5/services/cRaterService.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,16 @@ import { CRaterIdea } from '../components/common/cRater/CRaterIdea';
import { CRaterScore } from '../components/common/cRater/CRaterScore';
import { CRaterResponse } from '../components/common/cRater/CRaterResponse';
import { RawCRaterResponse } from '../components/common/cRater/RawCRaterResponse';
import { CRaterRubric } from '../components/common/cRater/CRaterRubric';
import { ProjectService } from './projectService';

@Injectable()
export class CRaterService {
constructor(protected http: HttpClient, protected configService: ConfigService) {}
constructor(
protected http: HttpClient,
protected configService: ConfigService,
private projectService: ProjectService
) {}

/**
* Make a CRater request to score student response
Expand Down Expand Up @@ -260,4 +266,8 @@ export class CRaterService {
}
return ideas;
}

getCRaterRubric(nodeId: string, componentId: string): CRaterRubric {
return new CRaterRubric(this.projectService.getComponent(nodeId, componentId).cRaterRubric);
}
}
8 changes: 4 additions & 4 deletions src/messages.xlf
Original file line number Diff line number Diff line change
Expand Up @@ -10642,7 +10642,7 @@ Click &quot;Cancel&quot; to keep the invalid JSON open so you can fix it.</sourc
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/assets/wise5/components/match/match-student/match-student-default/match-student-default.component.ts</context>
<context context-type="linenumber">104</context>
<context context-type="linenumber">107</context>
</context-group>
</trans-unit>
<trans-unit id="1944363997751399240" datatype="html">
Expand Down Expand Up @@ -19582,14 +19582,14 @@ Warning: This will delete all existing choices and buckets in this component.</s
<source>Correct bucket but wrong position</source>
<context-group purpose="location">
<context context-type="sourcefile">src/assets/wise5/components/match/match-student/match-student-default/match-student-default.component.ts</context>
<context context-type="linenumber">431</context>
<context context-type="linenumber">434</context>
</context-group>
</trans-unit>
<trans-unit id="7841430672042223267" datatype="html">
<source>Correct</source>
<context-group purpose="location">
<context context-type="sourcefile">src/assets/wise5/components/match/match-student/match-student-default/match-student-default.component.ts</context>
<context context-type="linenumber">438</context>
<context context-type="linenumber">441</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/assets/wise5/directives/summary-display/summary-display.component.ts</context>
Expand All @@ -19604,7 +19604,7 @@ Warning: This will delete all existing choices and buckets in this component.</s
<source>Incorrect</source>
<context-group purpose="location">
<context context-type="sourcefile">src/assets/wise5/components/match/match-student/match-student-default/match-student-default.component.ts</context>
<context context-type="linenumber">438</context>
<context context-type="linenumber">441</context>
</context-group>
<context-group purpose="location">
<context context-type="sourcefile">src/assets/wise5/directives/summary-display/summary-display.component.ts</context>
Expand Down

0 comments on commit 1467fd6

Please sign in to comment.