From 6c13206c801ca56cbb02a052e9e3d2e9c90903eb Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Wed, 12 Mar 2025 08:40:37 -0700 Subject: [PATCH 1/6] feat(SummaryDisplay): Dialog guidance summary display for teachers --- .../shared/node-info/node-info.component.html | 11 +++ .../shared/node-info/node-info.component.ts | 44 +++++----- .../components/summary/summaryService.ts | 4 +- ...nce-teacher-summary-display.component.html | 20 +++++ ...dance-teacher-summary-display.component.ts | 85 +++++++++++++++++++ 5 files changed, 141 insertions(+), 23 deletions(-) create mode 100644 src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html create mode 100644 src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.ts diff --git a/src/assets/wise5/classroomMonitor/classroomMonitorComponents/shared/node-info/node-info.component.html b/src/assets/wise5/classroomMonitor/classroomMonitorComponents/shared/node-info/node-info.component.html index 906d9eac220..5dc126493c4 100644 --- a/src/assets/wise5/classroomMonitor/classroomMonitorComponents/shared/node-info/node-info.component.html +++ b/src/assets/wise5/classroomMonitor/classroomMonitorComponents/shared/node-info/node-info.component.html @@ -55,6 +55,17 @@

[doRender]="true" /> } + @if (component.hasResponsesSummary && component.type === 'DialogGuidance') { + + } } diff --git a/src/assets/wise5/classroomMonitor/classroomMonitorComponents/shared/node-info/node-info.component.ts b/src/assets/wise5/classroomMonitor/classroomMonitorComponents/shared/node-info/node-info.component.ts index 2abf08445e9..8ea03190e0c 100644 --- a/src/assets/wise5/classroomMonitor/classroomMonitorComponents/shared/node-info/node-info.component.ts +++ b/src/assets/wise5/classroomMonitor/classroomMonitorComponents/shared/node-info/node-info.component.ts @@ -1,34 +1,36 @@ -import { Component, Input } from '@angular/core'; -import { SummaryService } from '../../../../components/summary/summaryService'; import { AnnotationService } from '../../../../services/annotationService'; +import { CommonModule } from '@angular/common'; +import { Component, Input } from '@angular/core'; +import { ComponentFactory } from '../../../../common/ComponentFactory'; import { ComponentServiceLookupService } from '../../../../services/componentServiceLookupService'; import { ComponentTypeService } from '../../../../services/componentTypeService'; -import { TeacherDataService } from '../../../../services/teacherDataService'; -import { TeacherProjectService } from '../../../../services/teacherProjectService'; -import { ComponentFactory } from '../../../../common/ComponentFactory'; +import { DialogGuidanceTeacherSummaryDisplayComponent } from '../../../../directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component'; +import { FlexLayoutModule } from '@angular/flex-layout'; import { isMatchingPeriods } from '../../../../common/period/period'; -import { Node } from '../../../../common/Node'; import { MatCardModule } from '@angular/material/card'; -import { MatIconModule } from '@angular/material/icon'; import { MatDividerModule } from '@angular/material/divider'; -import { FlexLayoutModule } from '@angular/flex-layout'; +import { MatIconModule } from '@angular/material/icon'; +import { Node } from '../../../../common/Node'; import { PreviewComponentComponent } from '../../../../authoringTool/components/preview-component/preview-component.component'; +import { SummaryService } from '../../../../components/summary/summaryService'; +import { TeacherDataService } from '../../../../services/teacherDataService'; +import { TeacherProjectService } from '../../../../services/teacherProjectService'; import { TeacherSummaryDisplayComponent } from '../../../../directives/teacher-summary-display/teacher-summary-display.component'; -import { CommonModule } from '@angular/common'; @Component({ - imports: [ - CommonModule, - MatCardModule, - MatIconModule, - MatDividerModule, - FlexLayoutModule, - PreviewComponentComponent, - TeacherSummaryDisplayComponent - ], - selector: 'node-info', - styleUrl: 'node-info.component.scss', - templateUrl: 'node-info.component.html' + imports: [ + DialogGuidanceTeacherSummaryDisplayComponent, + CommonModule, + MatCardModule, + MatIconModule, + MatDividerModule, + FlexLayoutModule, + PreviewComponentComponent, + TeacherSummaryDisplayComponent + ], + selector: 'node-info', + styleUrl: 'node-info.component.scss', + templateUrl: 'node-info.component.html' }) export class NodeInfoComponent { protected node: Node; diff --git a/src/assets/wise5/components/summary/summaryService.ts b/src/assets/wise5/components/summary/summaryService.ts index 8951287bc03..1efe9d3a591 100644 --- a/src/assets/wise5/components/summary/summaryService.ts +++ b/src/assets/wise5/components/summary/summaryService.ts @@ -1,8 +1,8 @@ 'use strict'; import { ComponentService } from '../componentService'; -import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; +import { Injectable } from '@angular/core'; import { Observable } from 'rxjs'; @Injectable() @@ -26,7 +26,7 @@ export class SummaryService extends ComponentService { 'OpenResponse', 'Table' ]; - this.componentsWithResponsesSummary = ['MultipleChoice', 'Table']; + this.componentsWithResponsesSummary = ['MultipleChoice', 'Table', 'DialogGuidance']; } getComponentTypeLabel(): string { diff --git a/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html b/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html new file mode 100644 index 00000000000..c888f9b1c9e --- /dev/null +++ b/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html @@ -0,0 +1,20 @@ + + + @if (hasWarning) { +

{{ warningMessage }}

+ } + @if (doRender) { +

Most Common Ideas Detected:

+ @for (idea of seeAllIdeas ? sortedIdeas : topIdeas; let i = $index; track idea.id) { +

{{ i + 1 }}. {{ idea.text }} (person{{ idea.count }})

+ } + @if (sortedIdeas.length > 5) { + @if (seeAllIdeas) { + See fewer ideas + } @else { + See all ideas + } + } + } +
+
diff --git a/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.ts b/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.ts new file mode 100644 index 00000000000..f4db0955ca7 --- /dev/null +++ b/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.ts @@ -0,0 +1,85 @@ +import { AnnotationService } from '../../services/annotationService'; +import { CommonModule } from '@angular/common'; +import { Component } from '@angular/core'; +import { ComponentState } from '../../../../app/domain/componentState'; +import { ConfigService } from '../../services/configService'; +import { CRaterIdea } from '../../components/common/cRater/CRaterIdea'; +import { CRaterService } from '../../services/cRaterService'; +import { MatCardModule } from '@angular/material/card'; +import { MatIconModule } from '@angular/material/icon'; +import { ProjectService } from '../../services/projectService'; +import { SummaryService } from '../../components/summary/summaryService'; +import { TeacherDataService } from '../../services/teacherDataService'; +import { TeacherSummaryDisplayComponent } from './teacher-summary-display.component'; + +@Component({ + imports: [CommonModule, MatCardModule, MatIconModule], + selector: 'dialog-guidance-teacher-summary-display', + templateUrl: 'dialog-guidance-teacher-summary-display.component.html' +}) +export class DialogGuidanceTeacherSummaryDisplayComponent extends TeacherSummaryDisplayComponent { + protected ideaCountMap: Map>; + protected sortedIdeas: { id: string; count: number }[] = []; + protected topIdeas: { id: string; count: number }[] = []; + protected seeAllIdeas: boolean = false; + + constructor( + protected annotationService: AnnotationService, + protected configService: ConfigService, + private cRaterService: CRaterService, + protected dataService: TeacherDataService, + protected projectService: ProjectService, + protected summaryService: SummaryService + ) { + super(annotationService, configService, dataService, projectService, summaryService); + this.ideaCountMap = new Map>(); + } + + ngOnInit() { + this.getLatestWork().subscribe((componentStates) => { + this.extractIdeasFromComponentStates(componentStates); + this.sortIdeas(); + }); + } + + private extractIdeasFromComponentStates(componentStates: ComponentState[]): void { + componentStates.forEach((componentState) => { + const detectedIdeas = this.getDetectedIdeasFromWorkgroup(componentState); + if (detectedIdeas !== undefined) { + detectedIdeas.forEach((idea) => { + if (this.ideaCountMap.has(idea.name)) { + this.ideaCountMap.get(idea.name).add(componentState.workgroupId); + } else { + this.ideaCountMap.set(idea.name, new Set([componentState.workgroupId])); + } + }); + } + }); + } + + private getDetectedIdeasFromWorkgroup(componentState: ComponentState): CRaterIdea[] { + const detectedIdeas = []; + componentState.studentData.responses.forEach((response) => { + response.ideas + ?.filter((idea) => idea.detected) + .forEach((idea) => detectedIdeas.push(new CRaterIdea(idea.name, idea.detected))); + }); + return detectedIdeas; + } + + private sortIdeas(): void { + const rubric = this.cRaterService.getCRaterRubric(this.nodeId, this.componentId); + this.sortedIdeas = [...this.ideaCountMap.entries()] + .sort((a, b) => b[1].size - a[1].size) + .map((mapIterator) => ({ + id: mapIterator[0], + text: rubric.getIdea(mapIterator[0])?.text ?? mapIterator[0], + count: mapIterator[1].size + })); + this.topIdeas = [...this.sortedIdeas].splice(0, 5); + } + + protected toggleSeeAllIdeas(): void { + this.seeAllIdeas = !this.seeAllIdeas; + } +} From bcd4c15dcc521224f398038037d76b1bd77971cb Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Tue, 18 Mar 2025 17:03:59 -0700 Subject: [PATCH 2/6] Added sections for least common ideas and all ideas --- .../components/common/cRater/CRaterRubric.ts | 4 + ...nce-teacher-summary-display.component.html | 32 ++++-- ...dance-teacher-summary-display.component.ts | 104 ++++++++++++------ 3 files changed, 97 insertions(+), 43 deletions(-) diff --git a/src/assets/wise5/components/common/cRater/CRaterRubric.ts b/src/assets/wise5/components/common/cRater/CRaterRubric.ts index 59b118a166e..ca7992affe9 100644 --- a/src/assets/wise5/components/common/cRater/CRaterRubric.ts +++ b/src/assets/wise5/components/common/cRater/CRaterRubric.ts @@ -10,6 +10,10 @@ export class CRaterRubric { getIdea(ideaId: string): CRaterIdea { return this.ideas.find((idea) => idea.name === ideaId); } + + getAllIdeas(): CRaterIdea[] { + return this.ideas; + } } export function getUniqueIdeas(responses: any[], rubric: CRaterRubric): CRaterIdea[] { diff --git a/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html b/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html index c888f9b1c9e..6b1db95c862 100644 --- a/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html +++ b/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html @@ -4,17 +4,33 @@

{{ warningMessage }}

} @if (doRender) { -

Most Common Ideas Detected:

- @for (idea of seeAllIdeas ? sortedIdeas : topIdeas; let i = $index; track idea.id) { -

{{ i + 1 }}. {{ idea.text }} (person{{ idea.count }})

- } - @if (sortedIdeas.length > 5) { +
+
+

Most Common Ideas Detected:

+ @for (idea of mostCommonIdeas; track idea.id) { +

{{ idea.text }} (person{{ idea.count }})

+ } +
+
+

Least Common Ideas Detected:

+ @for (idea of leastCommonIdeas; track idea.id) { +

{{ idea.text }} (person{{ idea.count }})

+ } +
+
+
@if (seeAllIdeas) { - See fewer ideas +

All Ideas:

+ @for (idea of allIdeas; track idea.id) { +

{{ idea.text }} (person{{ idea.count }})

+ } + Hide all ideas } @else { - See all ideas + Show all ideas } - } +
+ } @else { +

Your students' ideas will show up here as they are detected in the dialog.

} diff --git a/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.ts b/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.ts index f4db0955ca7..c4009a32f6a 100644 --- a/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.ts +++ b/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.ts @@ -4,79 +4,113 @@ import { Component } from '@angular/core'; import { ComponentState } from '../../../../app/domain/componentState'; import { ConfigService } from '../../services/configService'; import { CRaterIdea } from '../../components/common/cRater/CRaterIdea'; +import { CRaterRubric } from '../../components/common/cRater/CRaterRubric'; import { CRaterService } from '../../services/cRaterService'; import { MatCardModule } from '@angular/material/card'; import { MatIconModule } from '@angular/material/icon'; -import { ProjectService } from '../../services/projectService'; import { SummaryService } from '../../components/summary/summaryService'; import { TeacherDataService } from '../../services/teacherDataService'; +import { TeacherProjectService } from '../../services/teacherProjectService'; import { TeacherSummaryDisplayComponent } from './teacher-summary-display.component'; @Component({ imports: [CommonModule, MatCardModule, MatIconModule], selector: 'dialog-guidance-teacher-summary-display', + styles: ` + .wrapper { + display: grid; + grid-template-columns: 400px 400px; + } + .idea-group { + padding-left: 20px; + } + #all-ideas { + padding-top: 20px; + } + `, templateUrl: 'dialog-guidance-teacher-summary-display.component.html' }) export class DialogGuidanceTeacherSummaryDisplayComponent extends TeacherSummaryDisplayComponent { - protected ideaCountMap: Map>; - protected sortedIdeas: { id: string; count: number }[] = []; - protected topIdeas: { id: string; count: number }[] = []; - protected seeAllIdeas: boolean = false; + protected ideaCountMap: Map> = new Map>(); + protected seeAllIdeas: boolean; + protected allIdeas: { id: string; text: string; count: number }[] = []; + protected mostCommonIdeas: { id: string; text: string; count: number }[] = []; + protected leastCommonIdeas: { id: string; text: string; count: number }[] = []; + private rubric: CRaterRubric; constructor( protected annotationService: AnnotationService, protected configService: ConfigService, private cRaterService: CRaterService, protected dataService: TeacherDataService, - protected projectService: ProjectService, + protected projectService: TeacherProjectService, protected summaryService: SummaryService ) { super(annotationService, configService, dataService, projectService, summaryService); - this.ideaCountMap = new Map>(); } - ngOnInit() { + ngOnInit(): void { + this.rubric = this.cRaterService.getCRaterRubric(this.nodeId, this.componentId); this.getLatestWork().subscribe((componentStates) => { - this.extractIdeasFromComponentStates(componentStates); - this.sortIdeas(); + this.extractIdeas(componentStates); + const sortedIdeas = this.sortIdeas(); + this.mostCommonIdeas = [...sortedIdeas].splice(0, 3); + this.leastCommonIdeas = [...sortedIdeas] + .splice(sortedIdeas.length - 3, sortedIdeas.length) + .reverse(); + this.allIdeas = this.getAllIdeas(); + if (!this.allIdeas.some((idea) => this.ideaCountMap.get(idea.id)?.size > 0)) { + this.doRender = false; + } }); } - private extractIdeasFromComponentStates(componentStates: ComponentState[]): void { - componentStates.forEach((componentState) => { - const detectedIdeas = this.getDetectedIdeasFromWorkgroup(componentState); - if (detectedIdeas !== undefined) { - detectedIdeas.forEach((idea) => { - if (this.ideaCountMap.has(idea.name)) { - this.ideaCountMap.get(idea.name).add(componentState.workgroupId); - } else { - this.ideaCountMap.set(idea.name, new Set([componentState.workgroupId])); - } - }); - } - }); + private getAllIdeas(): { id: string; text: string; count: number }[] { + return this.rubric.getAllIdeas().map((idea) => ({ + id: idea.name, + text: this.useIdeaTextOrIdea(idea.name, idea.text), + count: this.ideaCountMap.get(idea.name)?.size ?? 0 + })); } - private getDetectedIdeasFromWorkgroup(componentState: ComponentState): CRaterIdea[] { - const detectedIdeas = []; - componentState.studentData.responses.forEach((response) => { - response.ideas - ?.filter((idea) => idea.detected) - .forEach((idea) => detectedIdeas.push(new CRaterIdea(idea.name, idea.detected))); - }); - return detectedIdeas; + private useIdeaTextOrIdea(id: string, text: string): string { + return text ?? 'idea ' + id; + } + + private extractIdeas(componentStates: ComponentState[]): void { + componentStates.forEach((componentState) => + this.getDetectedIdeas(componentState).forEach((idea) => { + if (this.ideaCountMap.has(idea.name)) { + this.ideaCountMap.get(idea.name).add(componentState.workgroupId); + } else { + this.ideaCountMap.set(idea.name, new Set([componentState.workgroupId])); + } + }) + ); + } + + private getDetectedIdeas(componentState: ComponentState): CRaterIdea[] { + return componentState.studentData.responses.flatMap( + (response) => + response.ideas + ?.filter((idea) => idea.detected) + .map((idea) => new CRaterIdea(idea.name, idea.detected)) ?? [] + ); } - private sortIdeas(): void { + private sortIdeas(): { id: string; text: string; count: number }[] { const rubric = this.cRaterService.getCRaterRubric(this.nodeId, this.componentId); - this.sortedIdeas = [...this.ideaCountMap.entries()] + return [...this.ideaCountMap.entries()] .sort((a, b) => b[1].size - a[1].size) .map((mapIterator) => ({ id: mapIterator[0], - text: rubric.getIdea(mapIterator[0])?.text ?? mapIterator[0], + text: this.getIdeaText(mapIterator[0]), count: mapIterator[1].size })); - this.topIdeas = [...this.sortedIdeas].splice(0, 5); + } + + private getIdeaText(id: string) { + return this.useIdeaTextOrIdea(id, this.rubric.getIdea(id).text); } protected toggleSeeAllIdeas(): void { From c8152ccf1dcd61f9ee84a70a595a0b7fb97c0e56 Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Mon, 24 Mar 2025 16:19:03 -0700 Subject: [PATCH 3/6] Added tests and made requested changes --- .../components/common/cRater/CRaterRubric.ts | 2 +- .../components/summary/summaryService.ts | 2 +- ...nce-teacher-summary-display.component.html | 12 +- ...-teacher-summary-display.component.spec.ts | 138 ++++++++++++++++++ ...dance-teacher-summary-display.component.ts | 28 ++-- src/messages.xlf | 68 +++++++-- 6 files changed, 215 insertions(+), 35 deletions(-) create mode 100644 src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.spec.ts diff --git a/src/assets/wise5/components/common/cRater/CRaterRubric.ts b/src/assets/wise5/components/common/cRater/CRaterRubric.ts index ca7992affe9..a93eea422b8 100644 --- a/src/assets/wise5/components/common/cRater/CRaterRubric.ts +++ b/src/assets/wise5/components/common/cRater/CRaterRubric.ts @@ -11,7 +11,7 @@ export class CRaterRubric { return this.ideas.find((idea) => idea.name === ideaId); } - getAllIdeas(): CRaterIdea[] { + getIdeas(): CRaterIdea[] { return this.ideas; } } diff --git a/src/assets/wise5/components/summary/summaryService.ts b/src/assets/wise5/components/summary/summaryService.ts index 1efe9d3a591..61d6139170b 100644 --- a/src/assets/wise5/components/summary/summaryService.ts +++ b/src/assets/wise5/components/summary/summaryService.ts @@ -26,7 +26,7 @@ export class SummaryService extends ComponentService { 'OpenResponse', 'Table' ]; - this.componentsWithResponsesSummary = ['MultipleChoice', 'Table', 'DialogGuidance']; + this.componentsWithResponsesSummary = ['DialogGuidance', 'MultipleChoice', 'Table']; } getComponentTypeLabel(): string { diff --git a/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html b/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html index 6b1db95c862..19cee5f59d8 100644 --- a/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html +++ b/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html @@ -6,13 +6,13 @@ @if (doRender) {
-

Most Common Ideas Detected:

+

Most Common Ideas Detected:

@for (idea of mostCommonIdeas; track idea.id) {

{{ idea.text }} (person{{ idea.count }})

}
-

Least Common Ideas Detected:

+

Least Common Ideas Detected:

@for (idea of leastCommonIdeas; track idea.id) {

{{ idea.text }} (person{{ idea.count }})

} @@ -20,17 +20,17 @@

{{ idea.text }} (person{{ idea.count }})

@if (seeAllIdeas) { -

All Ideas:

+

All Ideas:

@for (idea of allIdeas; track idea.id) {

{{ idea.text }} (person{{ idea.count }})

} - Hide all ideas + Hide all ideas } @else { - Show all ideas + Show all ideas }
} @else { -

Your students' ideas will show up here as they are detected in the dialog.

+

Your students' ideas will show up here as they are detected in the dialog.

} diff --git a/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.spec.ts b/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.spec.ts new file mode 100644 index 00000000000..2328ff9dacc --- /dev/null +++ b/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.spec.ts @@ -0,0 +1,138 @@ +import { ComponentFixture } from '@angular/core/testing'; +import { DialogGuidanceTeacherSummaryDisplayComponent } from './dialog-guidance-teacher-summary-display.component'; +import { TestBed } from '@angular/core/testing'; +import { MockProviders } from 'ng-mocks'; +import { CRaterService } from '../../services/cRaterService'; +import { CRaterRubric } from '../../components/common/cRater/CRaterRubric'; +import { CRaterIdea } from '../../components/common/cRater/CRaterIdea'; +import { SummaryService } from '../../components/summary/summaryService'; +import { Observable, of } from 'rxjs'; +import { ComponentState } from '../../../../app/domain/componentState'; +import { AnnotationService } from '../../services/annotationService'; +import { ConfigService } from '../../services/configService'; +import { TeacherDataService } from '../../services/teacherDataService'; +import { TeacherProjectService } from '../../services/teacherProjectService'; +import { Config } from '../../../../app/domain/config'; + +let component: DialogGuidanceTeacherSummaryDisplayComponent; +let fixture: ComponentFixture; +fdescribe('DialogGuidanceTeacherSummaryDisplayComponent', () => { + beforeEach(async () => { + await TestBed.configureTestingModule({ + imports: [DialogGuidanceTeacherSummaryDisplayComponent], + providers: [ + MockProviders( + AnnotationService, + ConfigService, + CRaterService, + TeacherDataService, + TeacherProjectService, + SummaryService + ) + ] + }).compileComponents(); + + fixture = TestBed.createComponent(DialogGuidanceTeacherSummaryDisplayComponent); + component = fixture.componentInstance; + component.doRender = true; + // Set up component? + }); + beforeEach(() => { + spyOn(TestBed.inject(ConfigService), 'isPreview').and.returnValue(false); + spyOn(TestBed.inject(ConfigService), 'isAuthoring').and.returnValue(false); + spyOn(TestBed.inject(ConfigService), 'isStudentRun').and.returnValue(false); + spyOn(TestBed.inject(ConfigService), 'getNumberOfWorkgroupsInPeriod').and.returnValue(1); + }); + ngOnInit(); +}); + +function ngOnInit() { + describe('ngOnChanges()', () => { + ngInit_NoIdeasDetected_ShowMessage(); + ngInit_IdeasDetected_ShowSummary(); + ngInit_ManyIdeasDetected_ShowTopAndBottomThree(); + }); +} + +function ngInit_NoIdeasDetected_ShowMessage() { + describe('no ideas detected', () => { + beforeEach(() => { + spyOn(TestBed.inject(CRaterService), 'getCRaterRubric').and.returnValue( + generateMockRubric(3, 0) + ); + spyOn(TestBed.inject(SummaryService), 'getLatestClassmateStudentWork').and.returnValue( + generateMockStudentWork(0) + ); + }); + it('shows message to teacher', () => { + component.ngOnInit(); + fixture.detectChanges(); + expect(fixture.nativeElement.querySelector('h2').textContent).toEqual( + "Your students' ideas will show up here as they are detected in the dialog." + ); + }); + }); +} + +function ngInit_IdeasDetected_ShowSummary() { + describe('ideas detected', () => { + beforeEach(() => { + spyOn(TestBed.inject(CRaterService), 'getCRaterRubric').and.returnValue( + generateMockRubric(3, 1) + ); + spyOn(TestBed.inject(SummaryService), 'getLatestClassmateStudentWork').and.returnValue( + generateMockStudentWork(1) + ); + }); + it('shows summary display', () => { + component.ngOnInit(); + fixture.detectChanges(); + + expect(fixture.nativeElement.querySelector('h2').textContent).toEqual( + 'Most Common Ideas Detected:' + ); + }); + }); +} + +function ngInit_ManyIdeasDetected_ShowTopAndBottomThree() { + describe('more than 3 ideas detected', () => { + beforeEach(() => { + spyOn(TestBed.inject(CRaterService), 'getCRaterRubric').and.returnValue( + generateMockRubric(4, 4) + ); + spyOn(TestBed.inject(SummaryService), 'getLatestClassmateStudentWork').and.returnValue( + generateMockStudentWork(4) + ); + }); + it('shows only top and bottom three ideas', () => { + component.ngOnInit(); + fixture.detectChanges(); + expect(fixture.nativeElement.querySelectorAll('#most-common-ideas > h3').length).toEqual(3); + expect(fixture.nativeElement.querySelectorAll('#least-common-ideas > h3').length).toEqual(3); + }); + }); +} + +function generateMockRubric(numIdeas: number, numDetected: number): CRaterRubric { + const ideas = []; + for (let i = 0; i < numIdeas; i++) { + const idea = new CRaterIdea('idea ' + (i + 1), numDetected > 0 ? true : false); + ideas.push(idea); + numDetected--; + } + return new CRaterRubric({ ideas: ideas }); +} + +function generateMockStudentWork(numIdeasDetected: number): Observable { + const ideas = []; + for (let i = 0; i < numIdeasDetected; i++) { + ideas.push({ name: 'idea ' + (i + 1), detected: true }); + } + return of([ + new ComponentState({ + workgroupId: 1, + studentData: { responses: [{ ideas: ideas }] } + }) + ]); +} diff --git a/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.ts b/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.ts index c4009a32f6a..75f93056dc7 100644 --- a/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.ts +++ b/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.ts @@ -31,12 +31,12 @@ import { TeacherSummaryDisplayComponent } from './teacher-summary-display.compon templateUrl: 'dialog-guidance-teacher-summary-display.component.html' }) export class DialogGuidanceTeacherSummaryDisplayComponent extends TeacherSummaryDisplayComponent { - protected ideaCountMap: Map> = new Map>(); - protected seeAllIdeas: boolean; protected allIdeas: { id: string; text: string; count: number }[] = []; - protected mostCommonIdeas: { id: string; text: string; count: number }[] = []; + protected ideaCountMap: Map> = new Map>(); protected leastCommonIdeas: { id: string; text: string; count: number }[] = []; + protected mostCommonIdeas: { id: string; text: string; count: number }[] = []; private rubric: CRaterRubric; + protected seeAllIdeas: boolean; constructor( protected annotationService: AnnotationService, @@ -53,27 +53,28 @@ export class DialogGuidanceTeacherSummaryDisplayComponent extends TeacherSummary this.rubric = this.cRaterService.getCRaterRubric(this.nodeId, this.componentId); this.getLatestWork().subscribe((componentStates) => { this.extractIdeas(componentStates); - const sortedIdeas = this.sortIdeas(); - this.mostCommonIdeas = [...sortedIdeas].splice(0, 3); - this.leastCommonIdeas = [...sortedIdeas] - .splice(sortedIdeas.length - 3, sortedIdeas.length) - .reverse(); this.allIdeas = this.getAllIdeas(); if (!this.allIdeas.some((idea) => this.ideaCountMap.get(idea.id)?.size > 0)) { this.doRender = false; + } else { + const sortedIdeas = this.sortIdeas(); + this.mostCommonIdeas = [...sortedIdeas].splice(0, 3); + this.leastCommonIdeas = [...sortedIdeas] + .splice(sortedIdeas.length - 3, sortedIdeas.length) + .reverse(); } }); } private getAllIdeas(): { id: string; text: string; count: number }[] { - return this.rubric.getAllIdeas().map((idea) => ({ + return this.rubric.getIdeas().map((idea) => ({ id: idea.name, - text: this.useIdeaTextOrIdea(idea.name, idea.text), + text: this.useIdeaTextOrId(idea.name, idea.text), count: this.ideaCountMap.get(idea.name)?.size ?? 0 })); } - private useIdeaTextOrIdea(id: string, text: string): string { + private useIdeaTextOrId(id: string, text: string): string { return text ?? 'idea ' + id; } @@ -99,7 +100,6 @@ export class DialogGuidanceTeacherSummaryDisplayComponent extends TeacherSummary } private sortIdeas(): { id: string; text: string; count: number }[] { - const rubric = this.cRaterService.getCRaterRubric(this.nodeId, this.componentId); return [...this.ideaCountMap.entries()] .sort((a, b) => b[1].size - a[1].size) .map((mapIterator) => ({ @@ -109,8 +109,8 @@ export class DialogGuidanceTeacherSummaryDisplayComponent extends TeacherSummary })); } - private getIdeaText(id: string) { - return this.useIdeaTextOrIdea(id, this.rubric.getIdea(id).text); + private getIdeaText(id: string): string { + return this.useIdeaTextOrId(id, this.rubric.getIdea(id).text); } protected toggleSeeAllIdeas(): void { diff --git a/src/messages.xlf b/src/messages.xlf index 35cb9b8ebc0..e755bc07bf2 100644 --- a/src/messages.xlf +++ b/src/messages.xlf @@ -17168,7 +17168,7 @@ Are you ready to receive feedback on this answer? src/assets/wise5/directives/summary-display/summary-display.component.ts - 672 + 548 @@ -19359,11 +19359,11 @@ Warning: This will delete all existing choices and buckets in this component. src/assets/wise5/directives/summary-display/summary-display.component.ts - 378 + 293 src/assets/wise5/directives/summary-display/summary-display.component.ts - 480 + 356 @@ -19390,11 +19390,11 @@ Warning: This will delete all existing choices and buckets in this component. src/assets/wise5/directives/summary-display/summary-display.component.ts - 378 + 293 src/assets/wise5/directives/summary-display/summary-display.component.ts - 484 + 360 @@ -21245,56 +21245,98 @@ If this problem continues, let your teacher know and move on to the next activit Your Response src/assets/wise5/directives/summary-display/summary-display.component.ts - 509 + 385 Your Score src/assets/wise5/directives/summary-display/summary-display.component.ts - 511 + 387 Period Responses src/assets/wise5/directives/summary-display/summary-display.component.ts - 518 + 394 Period Scores src/assets/wise5/directives/summary-display/summary-display.component.ts - 523 + 399 Class Responses src/assets/wise5/directives/summary-display/summary-display.component.ts - 532 + 408 Class Scores src/assets/wise5/directives/summary-display/summary-display.component.ts - 537 + 413 % Responded (/) src/assets/wise5/directives/summary-display/summary-display.component.ts - 548 + 424 + + + + Most Common Ideas Detected: + + src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html + 9,11 + + + + Least Common Ideas Detected: + + src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html + 15,17 + + + + All Ideas: + + src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html + 23,25 + + + + Hide all ideas + + src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html + 27,29 + + + + Show all ideas + + src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html + 29,33 + + + + Your students' ideas will show up here as they are detected in the dialog. + + src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html + 33,37 The student will see a graph of their individual data here. src/assets/wise5/directives/teacher-summary-display/teacher-summary-display.component.ts - 50 + 46 From a444c10f1195d91c7d5fe00d72b95a850e848989 Mon Sep 17 00:00:00 2001 From: Jonathan Lim-Breitbart Date: Mon, 7 Apr 2025 16:56:17 -0700 Subject: [PATCH 4/6] Update detected ideas styles and layout --- .../shared/node-info/node-info.component.html | 3 + .../shared/node-info/node-info.component.scss | 5 ++ .../summary-display.component.html | 2 +- .../summary-display.component.scss | 4 -- ...nce-teacher-summary-display.component.html | 66 ++++++++++++------- ...-teacher-summary-display.component.spec.ts | 10 ++- ...dance-teacher-summary-display.component.ts | 18 ++--- src/messages.xlf | 31 +++++---- 8 files changed, 86 insertions(+), 53 deletions(-) diff --git a/src/assets/wise5/classroomMonitor/classroomMonitorComponents/shared/node-info/node-info.component.html b/src/assets/wise5/classroomMonitor/classroomMonitorComponents/shared/node-info/node-info.component.html index 5dc126493c4..5f200e63117 100644 --- a/src/assets/wise5/classroomMonitor/classroomMonitorComponents/shared/node-info/node-info.component.html +++ b/src/assets/wise5/classroomMonitor/classroomMonitorComponents/shared/node-info/node-info.component.html @@ -34,6 +34,7 @@

} @if (component.hasResponsesSummary && component.type === 'MultipleChoice') { } @if (component.hasScoresSummary && component.hasScoreAnnotation) { } @if (component.hasResponsesSummary && component.type === 'DialogGuidance') { + @if (hasWarning) {

{{ warningMessage }}

diff --git a/src/assets/wise5/directives/summary-display/summary-display.component.scss b/src/assets/wise5/directives/summary-display/summary-display.component.scss index be612a5974a..ec03009306b 100644 --- a/src/assets/wise5/directives/summary-display/summary-display.component.scss +++ b/src/assets/wise5/directives/summary-display/summary-display.component.scss @@ -1,7 +1,3 @@ -.summary-card { - margin: 16px 8px 8px; -} - .highcharts-chart { display: block; height: 400px; diff --git a/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html b/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html index 19cee5f59d8..a516bbdbeb5 100644 --- a/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html +++ b/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html @@ -1,36 +1,58 @@ - + +

Student Ideas Detected

@if (hasWarning) {

{{ warningMessage }}

} @if (doRender) { -
-
-

Most Common Ideas Detected:

- @for (idea of mostCommonIdeas; track idea.id) { -

{{ idea.text }} (person{{ idea.count }})

- } +
+
+

Most Common:

+
    + @for (idea of mostCommonIdeas; track idea.id) { +
  • +
    + {{ idea.id }}. {{ idea.text }} (person{{ idea.count }}) +
    +
  • + } +
-
-

Least Common Ideas Detected:

- @for (idea of leastCommonIdeas; track idea.id) { -

{{ idea.text }} (person{{ idea.count }})

- } +
+

Least Common:

+
    + @for (idea of leastCommonIdeas; track idea.id) { +
  • +
    + {{ idea.id }}. {{ idea.text }} (person{{ idea.count }}) +
    +
  • + } +
-
- @if (seeAllIdeas) { -

All Ideas:

+ @if (seeAllIdeas) { +

All Ideas:

+
    @for (idea of allIdeas; track idea.id) { -

    {{ idea.text }} (person{{ idea.count }})

    +
  • +
    + {{ idea.id }}. {{ idea.text }} (person{{ idea.count }}) +
    +
  • } - Hide all ideas - } @else { - Show all ideas - } -
+ + Hide all ideas + } @else { + Show all ideas + } } @else { -

Your students' ideas will show up here as they are detected in the dialog.

+
+ Your students' ideas will show up here as they are detected in the dialog. +
} diff --git a/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.spec.ts b/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.spec.ts index d4340f91562..76446da3640 100644 --- a/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.spec.ts +++ b/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.spec.ts @@ -66,7 +66,7 @@ function ngInit_NoIdeasDetected_ShowMessage() { it('shows message to teacher', () => { component.ngOnInit(); fixture.detectChanges(); - expect(fixture.nativeElement.querySelector('h2').textContent).toEqual( + expect(fixture.nativeElement.querySelector('.notice').textContent).toContain( "Your students' ideas will show up here as they are detected in the dialog." ); }); @@ -87,9 +87,7 @@ function ngInit_IdeasDetected_ShowSummary() { component.ngOnInit(); fixture.detectChanges(); - expect(fixture.nativeElement.querySelector('h2').textContent).toEqual( - 'Most Common Ideas Detected:' - ); + expect(fixture.nativeElement.querySelector('h3').textContent).toEqual('Most Common:'); }); }); } @@ -107,8 +105,8 @@ function ngInit_ManyIdeasDetected_ShowTopAndBottomThree() { it('shows only top and bottom three ideas', () => { component.ngOnInit(); fixture.detectChanges(); - expect(fixture.nativeElement.querySelectorAll('#most-common-ideas > h3').length).toEqual(3); - expect(fixture.nativeElement.querySelectorAll('#least-common-ideas > h3').length).toEqual(3); + expect(fixture.nativeElement.querySelectorAll('#most-common-ideas > li').length).toEqual(3); + expect(fixture.nativeElement.querySelectorAll('#least-common-ideas > li').length).toEqual(3); }); }); } diff --git a/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.ts b/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.ts index 75f93056dc7..c0307108509 100644 --- a/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.ts +++ b/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.ts @@ -17,15 +17,16 @@ import { TeacherSummaryDisplayComponent } from './teacher-summary-display.compon imports: [CommonModule, MatCardModule, MatIconModule], selector: 'dialog-guidance-teacher-summary-display', styles: ` - .wrapper { - display: grid; - grid-template-columns: 400px 400px; + h3 { + margin-bottom: 8px; } - .idea-group { - padding-left: 20px; + + .idea { + @apply px-2 py-1 rounded-md bg-gray-100 my-1 text-sm; } - #all-ideas { - padding-top: 20px; + + .mat-icon { + vertical-align: middle; } `, templateUrl: 'dialog-guidance-teacher-summary-display.component.html' @@ -113,7 +114,8 @@ export class DialogGuidanceTeacherSummaryDisplayComponent extends TeacherSummary return this.useIdeaTextOrId(id, this.rubric.getIdea(id).text); } - protected toggleSeeAllIdeas(): void { + protected toggleSeeAllIdeas(event: Event): void { + event.preventDefault(); this.seeAllIdeas = !this.seeAllIdeas; } } diff --git a/src/messages.xlf b/src/messages.xlf index 8c9d12e3f9b..8752b432d39 100644 --- a/src/messages.xlf +++ b/src/messages.xlf @@ -21290,46 +21290,53 @@ If this problem continues, let your teacher know and move on to the next activit 424 - - Most Common Ideas Detected: + + Student Ideas Detected src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html - 9,11 + 3,5 - - Least Common Ideas Detected: + + Most Common: src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html - 15,17 + 10,12 + + + + Least Common: + + src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html + 23,25 All Ideas: src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html - 23,25 + 37,39 Hide all ideas src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html - 27,29 + 48,50 Show all ideas src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html - 29,33 + 50,54 - - Your students' ideas will show up here as they are detected in the dialog. + + Your students' ideas will show up here as they are detected in the dialog. src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html - 33,37 + 54,59 From 58eb03fdae0ee7ed1e19d8b4655aaf92e0cd1cd9 Mon Sep 17 00:00:00 2001 From: Aaron Detre Date: Tue, 8 Apr 2025 11:30:52 -0700 Subject: [PATCH 5/6] Extracted duplicate idea HTML to ng-template --- ...nce-teacher-summary-display.component.html | 31 ++++++++++++------- 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html b/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html index a516bbdbeb5..c24dcac29d4 100644 --- a/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html +++ b/src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html @@ -1,3 +1,10 @@ + +
+ {{ idea.id }}. {{ idea.text }} (person{{ idea.count }}) +
+
+

Student Ideas Detected

@@ -11,10 +18,10 @@

Most Common:

    @for (idea of mostCommonIdeas; track idea.id) {
  • -
    - {{ idea.id }}. {{ idea.text }} (person{{ idea.count }}) -
    +
  • }
@@ -24,10 +31,10 @@

Least Common:

    @for (idea of leastCommonIdeas; track idea.id) {
  • -
    - {{ idea.id }}. {{ idea.text }} (person{{ idea.count }}) -
    +
  • }
@@ -38,10 +45,10 @@

All Ideas:

    @for (idea of allIdeas; track idea.id) {
  • -
    - {{ idea.id }}. {{ idea.text }} (person{{ idea.count }}) -
    +
  • }
From 5b5c9f5c624ec26c3d9dc607898bcbc0322bbba9 Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 8 Apr 2025 18:33:21 +0000 Subject: [PATCH 6/6] Updated messages --- src/messages.xlf | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/src/messages.xlf b/src/messages.xlf index 8752b432d39..b235ff3c415 100644 --- a/src/messages.xlf +++ b/src/messages.xlf @@ -21294,49 +21294,49 @@ If this problem continues, let your teacher know and move on to the next activit Student Ideas Detected src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html - 3,5 + 10,12
Most Common: src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html - 10,12 + 17,19 Least Common: src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html - 23,25 + 30,32 All Ideas: src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html - 37,39 + 44,46 Hide all ideas src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html - 48,50 + 55,57 Show all ideas src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html - 50,54 + 57,61 Your students' ideas will show up here as they are detected in the dialog. src/assets/wise5/directives/teacher-summary-display/dialog-guidance-teacher-summary-display.component.html - 54,59 + 61,66