Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 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
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;
}
}
}
8 changes: 8 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;
this.ideaColors = rubric.ideaColors;
}

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

hasIdeasSummaryGroups(): boolean {
return this.ideasSummaryGroups != null;
}
}

export function getUniqueIdeas(responses: any[], rubric: CRaterRubric): CRaterIdea[] {
Expand Down
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
Expand Up @@ -116,6 +116,7 @@ export class DialogGuidanceStudentComponent extends ComponentStudent {
}

ngOnDestroy(): void {
super.ngOnDestroy();
this.cRaterPingService.stopPinging(this.getItemId());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
<div
class="px-2 py-1 rounded-md bg-gray-100 mb-1 text-sm"
(click)="toggleDetails()"
[style.background-color]="idea.color"
>
<b>{{ idea.id }}.</b> {{ idea.text }} (<mat-icon class="mat-18">person</mat-icon>{{ idea.count }})
@if (expanded) {
<mat-icon class="mat-18">expand_less</mat-icon>
<div>
@for (response of responses; track response.timestamp) {
<mat-icon class="mat-18">person</mat-icon>{{ response.text }}<br />
}
</div>
} @else {
<mat-icon class="mat-18">expand_more</mat-icon>
}
</div>
Loading