Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/app/chatbot/awsBedRock.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import { ChatService } from './chat.service';
@Injectable({ providedIn: 'root' })
export class AwsBedRockService extends ChatService {
protected chatEndpoint = '/api/aws-bedrock/chat';
protected model: string = 'openai.gpt-oss-20b-1:0';
protected model: string = 'google.gemma-3-27b-it';

processResponse(response: string): string {
return response.replace(/<reasoning>.*?<\/reasoning>/gs, '');
Expand Down
Original file line number Diff line number Diff line change
@@ -1,39 +1,42 @@
import { IdeaData } from '../../assets/wise5/components/common/cRater/IdeaData';
import { IdeasSortingService } from '../../assets/wise5/services/ideasSortingService';
import {
IdeaData,
sortIdeasByCount,
sortIdeasById
} from '../../assets/wise5/components/common/cRater/IdeaData';
import { TestBed } from '@angular/core/testing';

let ideas: IdeaData[];
let service: IdeasSortingService;

describe('IdeasSortingService', () => {
describe('IdeaData', () => {
beforeEach(() => {
TestBed.configureTestingModule({
providers: [IdeasSortingService]
});
TestBed.configureTestingModule({});
ideas = [
createIdeaData('2', 'c', 3),
createIdeaData('1', 'b', 1),
createIdeaData('2b', 'a', 4),
createIdeaData('10a', 'abc', 2),
createIdeaData('11', 'cba', 5)
];
service = TestBed.inject(IdeasSortingService);
});

sortIdeasByCount();
sortIdeasById();
test_SortIdeasByCount();
test_SortIdeasById();
});

function sortIdeasByCount() {
function test_SortIdeasByCount() {
it('should sort ideas descending numerically by count', () => {
const sortedIdeas = service.sortByCount(ideas);
const sortedIdeas = sortIdeasByCount(ideas, 'desc');
expect(sortedIdeas.map((idea) => idea.id)).toEqual(['11', '2b', '2', '10a', '1']);
});

it('should sort ideas ascending numerically by count', () => {
const sortedIdeas = sortIdeasByCount(ideas, 'asc');
expect(sortedIdeas.map((idea) => idea.id)).toEqual(['1', '10a', '2', '2b', '11']);
});
}

function sortIdeasById() {
function test_SortIdeasById() {
it('should sort ideas alphanumerically by ID', () => {
const sortedIdeas = service.sortById(ideas);
const sortedIdeas = sortIdeasById(ideas);
expect(sortedIdeas.map((ideas) => ideas.id)).toEqual(['1', '2', '2b', '10a', '11']);
});
}
Expand Down
11 changes: 11 additions & 0 deletions src/assets/wise5/common/array/array.ts
Original file line number Diff line number Diff line change
Expand Up @@ -84,6 +84,17 @@ export function arraysContainSameValues(array1: string[], array2: string[]): boo
return JSON.stringify(array1Copy) === JSON.stringify(array2Copy);
}

/**
* Check if array1 contains all elements of array2. Even if array1 contains more elements
* than array2, it will still return true if array1 contains all elements of array2.
* @param array1 an array of strings
* @param array2 an array of strings
* @returns whether array1 contains all elements of array2
*/
export function arrayContainsAll(array1: string[], array2: string[]): boolean {
return array2.every((value) => array1.includes(value));
}

export function reduceByUniqueId(objArr: any[]): any[] {
const idToObj = {};
const result = [];
Expand Down
6 changes: 5 additions & 1 deletion src/assets/wise5/components/common/cRater/CRaterIdea.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,18 @@ export class CRaterIdea {
detected?: boolean;
characterOffsets: any[];
text?: string;
tags?: string[];

constructor(name: string, detected?: boolean, text?: string) {
constructor(name: string, detected?: boolean, text?: string, tags?: string[]) {
this.name = name;
if (detected) {
this.detected = detected;
}
if (text) {
this.text = text;
}
if (tags) {
this.tags = tags;
}
}
}
46 changes: 46 additions & 0 deletions src/assets/wise5/components/common/cRater/CRaterRubric.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,14 @@ import { CRaterIdea } from './CRaterIdea';
export class CRaterRubric {
description: string = '';
ideas: CRaterIdea[] = [];
ideasSummaryGroups?: any;
ideaColors?: { tags: string[]; colorValue: string }[];

constructor(rubric: any = { description: '', ideas: [] }) {
this.description = rubric.description;
this.ideas = rubric.ideas;
this.ideasSummaryGroups = rubric.ideasSummaryGroups ?? DEFAULT_IDEAS_SUMMARY_GROUPS;
this.ideaColors = rubric.ideaColors;
}

getIdea(ideaId: string): CRaterIdea {
Expand All @@ -16,6 +20,14 @@ export class CRaterRubric {
hasRubricData(): boolean {
return (this.description ?? '') !== '' || this.ideas.length > 0;
}

getInitialIdeasSummaryGroups(): any[] {
return this.ideasSummaryGroups.initial;
}

getAdditionalIdeasSummaryGroups(): any[] {
return this.ideasSummaryGroups.additional;
}
}

export function getUniqueIdeas(responses: any[], rubric: CRaterRubric): CRaterIdea[] {
Expand All @@ -34,3 +46,37 @@ export function getUniqueIdeas(responses: any[], rubric: CRaterRubric): CRaterId
);
return uniqueIdeas;
}

export const DEFAULT_IDEAS_SUMMARY_GROUPS = {
initial: [
{
maxIdeas: 3,
title: $localize`Most Common`,
tags: [],
sort: {
field: 'count',
order: 'desc'
}
},
{
maxIdeas: 3,
title: $localize`Unique Ideas`,
tags: [],
sort: {
field: 'count',
order: 'asc'
}
}
],
additional: [
{
title: $localize`All Ideas`,
tags: [],
sort: {
field: 'count',
order: 'desc'
},
showUndetectedIdeas: true
}
]
};
65 changes: 63 additions & 2 deletions src/assets/wise5/components/common/cRater/IdeaData.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,73 @@ export type IdeaData = {
id: string;
text: string;
count: number;
tags?: string[];
color?: string;
};

export function ideaDataToCRaterIdea(ideaData: IdeaData): CRaterIdea {
return new CRaterIdea(ideaData.id, undefined, ideaData.text);
return new CRaterIdea(ideaData.id, undefined, ideaData.text, ideaData.tags);
}

export function cRaterIdeaToIdeaData(cRaterIdea: CRaterIdea): IdeaData {
return { id: cRaterIdea.name, text: cRaterIdea.text, count: 0 };
return { id: cRaterIdea.name, text: cRaterIdea.text, count: 0, tags: cRaterIdea.tags };
}

export function sortIdeasByCount(ideas: IdeaData[], sortOrder: 'asc' | 'desc'): IdeaData[] {
return ideas.sort((a, b) => (sortOrder === 'asc' ? a.count - b.count : b.count - a.count));
}

export function sortIdeasById(ideas: IdeaData[]): IdeaData[] {
const sorted = ideas
.filter((idea) => !stringContainsLetters(idea.id))
.sort((a, b) => Number(a.id) - Number(b.id));
const sortedIdeasWithLetters = getSortedIdeasWithLetters(ideas);
return insertIdeasWithLetters(sorted, sortedIdeasWithLetters);
}

function getSortedIdeasWithLetters(ideas: IdeaData[]): IdeaData[] {
return ideas
.filter((idea) => stringContainsLetters(idea.id))
.sort((a, b) => compareByStringNumericPrefix(a, b));
}

function stringContainsLetters(str: string): boolean {
return Array.from(str).some((char) => isNaN(Number(char)));
}

function compareByStringNumericPrefix(idea: IdeaData, otherIdea: IdeaData): number {
const prefixDif = stringNumericPrefix(idea.id) - stringNumericPrefix(otherIdea.id);
return prefixDif === 0 ? idea.id.localeCompare(otherIdea.id) : prefixDif;
}

function insertIdeasWithLetters(
sorted: IdeaData[],
sortedIdeasWithLetters: IdeaData[]
): IdeaData[] {
for (let i = 0; i < sorted.length; i++) {
while (
sortedIdeasWithLetters.length > 0 &&
Number(sorted.at(i).id) > stringNumericPrefix(sortedIdeasWithLetters.at(0).id)
) {
const ideaWithLetter = sortedIdeasWithLetters.at(0);
sortedIdeasWithLetters = sortedIdeasWithLetters.slice(1, sortedIdeasWithLetters.length);
sorted.splice(i, 0, ideaWithLetter);
i++;
}
}
return sorted;
}

function stringNumericPrefix(str: string): number {
let numericPrefix = '';
const strArray = Array.from(str);
for (let charIndex = 0; charIndex < strArray.length; charIndex++) {
const char = strArray.at(charIndex);
if (isNaN(Number(char))) {
break;
} else {
numericPrefix = numericPrefix.concat(char);
}
}
return Number(numericPrefix);
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,14 @@
import { Component, Inject } from '@angular/core';
import { CRaterIdea } from '../CRaterIdea';
import { cRaterIdeaToIdeaData, ideaDataToCRaterIdea } from '../IdeaData';
import { cRaterIdeaToIdeaData, ideaDataToCRaterIdea, sortIdeasById } from '../IdeaData';
import { CRaterRubric } from '../CRaterRubric';
import { IdeasSortingService } from '../../../../services/ideasSortingService';
import { MatIconModule } from '@angular/material/icon';
import { MAT_DIALOG_DATA, MatDialogModule, MatDialogRef } from '@angular/material/dialog';
import { RubricEventService } from './RubricEventService';
import { MatButtonModule } from '@angular/material/button';

@Component({
imports: [MatButtonModule, MatDialogModule, MatIconModule],
providers: [IdeasSortingService],
selector: 'crater-rubric',
templateUrl: './crater-rubric.component.html',
styleUrl: './crater-rubric.component.scss'
Expand All @@ -21,14 +19,13 @@ export class CRaterRubricComponent {
constructor(
@Inject(MAT_DIALOG_DATA) protected cRaterRubric: CRaterRubric,
private dialogRef: MatDialogRef<CRaterRubricComponent>,
private ideasSortingService: IdeasSortingService,
private rubricEventService: RubricEventService
) {}

ngOnInit(): void {
this.ideas = this.ideasSortingService
.sortById(this.cRaterRubric.ideas.map(cRaterIdeaToIdeaData))
.map(ideaDataToCRaterIdea);
this.ideas = sortIdeasById(this.cRaterRubric.ideas.map(cRaterIdeaToIdeaData)).map(
ideaDataToCRaterIdea
);
this.rubricEventService.rubricToggled();
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Injectable } from '@angular/core';
import { ComputerAvatarService } from '../../services/computerAvatarService';
import { ComponentService } from '../componentService';
import { DEFAULT_IDEAS_SUMMARY_GROUPS } from '../common/cRater/CRaterRubric';

@Injectable()
export class DialogGuidanceService extends ComponentService {
Expand All @@ -21,7 +22,11 @@ export class DialogGuidanceService extends ComponentService {
component.computerAvatarSettings =
this.computerAvatarService.getDefaultComputerAvatarSettings();
component.version = 2;
component.cRaterRubric = { ideas: [] };
component.cRaterRubric = {
ideas: [],
ideaColors: [],
ideasSummaryGroups: DEFAULT_IDEAS_SUMMARY_GROUPS
};
return component;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ import { EditFeedbackRulesComponent } from '../../common/feedbackRule/edit-feedb
import { OpenResponseContent } from '../OpenResponseContent';
import { CRaterItemSelectComponent } from '../../common/cRater/crater-item-select/crater-item-select.component';
import { EditCRaterInfoComponent } from '../../common/cRater/edit-crater-info/edit-crater-info.component';
import { DEFAULT_IDEAS_SUMMARY_GROUPS } from '../../common/cRater/CRaterRubric';

@Component({
imports: [
Expand Down Expand Up @@ -95,7 +96,9 @@ export class EditOpenResponseAdvancedComponent extends EditAdvancedComponentComp
enableMultipleAttemptScoringRules: false,
multipleAttemptScoringRules: [],
rubric: {
ideas: []
ideas: [],
ideaColors: [],
ideaSummaryGroups: DEFAULT_IDEAS_SUMMARY_GROUPS
}
};
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
<mat-expansion-panel
[disabled]="idea.count === 0"
[hideToggle]="idea.count === 0"
[style.background-color]="idea.color"
(opened)="toggleDetails()"
>
<mat-expansion-panel-header>
<mat-panel-title>
<span class="font-normal text-sm">{{ idea.id }}. {{ idea.text }}</span>
<span class="text-sm">{{ idea.id }}. {{ idea.text }}</span>
</mat-panel-title>
<mat-panel-description>
<span class="font-normal text-sm flex items-center">
Expand All @@ -14,10 +15,10 @@
</mat-panel-description>
</mat-expansion-panel-header>
<div class="text-sm bg-white p-1 rounded">
Sample responses:
<span i18n>Sample responses:</span>
<ul>
@for (response of responses; track response.timestamp) {
<li>"{{ response.text }}"</li>
<li i18n>"{{ response.text }}"</li>
}
</ul>
</div>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -56,18 +56,24 @@ describe('IdeaSummaryComponent', () => {
component.idea = {
id: 'idea1',
text: 'Test Idea',
count: 5
count: 5,
color: 'red'
};
});

describe('initial state', () => {
it('should initialize with expanded as false', () => {
expect(component['expanded']).toBe(false);
});

it('should initialize with empty responses array', () => {
expect(component['responses']).toEqual([]);
});
});

describe('when expanding for the first time', () => {
beforeEach(() => {
component['expanded'] = false;
component['responses'] = [];
});

Expand Down
Loading
Loading