Skip to content

Commit 6cd9c58

Browse files
committed
fixed type errors and error mode
1 parent 962a93b commit 6cd9c58

2 files changed

Lines changed: 168 additions & 58 deletions

File tree

src/App.tsx

Lines changed: 101 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -264,54 +264,110 @@ function App() {
264264
</div>
265265
</div>
266266
</div>
267-
<div>
268-
<h4 className="font-medium mb-2">Edge Colors</h4>
269-
<div className="space-y-2">
270-
<div className="flex items-center">
271-
<div className="w-4 h-4 bg-red-500 mr-2"></div>
272-
<span>ERROR</span>
273-
</div>
274-
<div className="flex items-center">
275-
<div className="w-4 h-4 bg-green-500 mr-2"></div>
276-
<span>OK</span>
277-
</div>
278-
<div className="flex items-center">
279-
<div className="w-4 h-4 bg-blue-500 mr-2"></div>
280-
<span>INITIAL_HINT/HINT_LEVEL_CHANGE</span>
267+
{errorMode ? (
268+
<div>
269+
<h4 className="font-medium mb-2">Edge Colors (Error Mode)</h4>
270+
<div className="space-y-2">
271+
<div className="flex items-center">
272+
<div className="w-4 h-4 bg-red-500 mr-2"></div>
273+
<span>ERROR</span>
274+
</div>
275+
<div className="flex items-center">
276+
<div className="w-4 h-4 bg-blue-500 mr-2"></div>
277+
<span>INITIAL_HINT / HINT_LEVEL_CHANGE</span>
278+
</div>
279+
<div className="flex items-center">
280+
<div className="w-4 h-4 bg-yellow-500 mr-2"></div>
281+
<span>JIT / FREEBIE_JIT</span>
282+
</div>
283+
<div className="flex items-center">
284+
<div className="w-4 h-4 bg-black mr-2"></div>
285+
<span>Only OK → Black</span>
286+
</div>
287+
<Popover>
288+
<PopoverTrigger>
289+
<div
290+
className="text-sm text-blue-600 hover:text-blue-800 cursor-help">
291+
How are edge colors calculated?
292+
</div>
293+
</PopoverTrigger>
294+
<PopoverContent className="w-80">
295+
<div className="space-y-2">
296+
<h4 className="font-medium">Edge Color Calculation (Error
297+
Mode)</h4>
298+
<p className="text-sm">
299+
When in error-focused mode, only non-OK outcomes
300+
contribute to the color:
301+
</p>
302+
<ul className="text-sm list-disc pl-4 space-y-1">
303+
<li>Only ERROR, hints, and JIT are included in the
304+
blend
305+
</li>
306+
<li>OK is ignored unless it's the only outcome — then
307+
the edge is black
308+
</li>
309+
<li>Final color includes 90% opacity</li>
310+
</ul>
311+
</div>
312+
</PopoverContent>
313+
</Popover>
281314
</div>
282-
<div className="flex items-center">
283-
<div className="w-4 h-4 bg-yellow-500 mr-2"></div>
284-
<span>JIT/FREEBIE_JIT</span>
315+
</div>
316+
) : (
317+
<div>
318+
<h4 className="font-medium mb-2">Edge Colors</h4>
319+
<div className="space-y-2">
320+
<div className="flex items-center">
321+
<div className="w-4 h-4 bg-red-500 mr-2"></div>
322+
<span>ERROR</span>
323+
</div>
324+
<div className="flex items-center">
325+
<div className="w-4 h-4 bg-green-500 mr-2"></div>
326+
<span>OK</span>
327+
</div>
328+
<div className="flex items-center">
329+
<div className="w-4 h-4 bg-blue-500 mr-2"></div>
330+
<span>INITIAL_HINT / HINT_LEVEL_CHANGE</span>
331+
</div>
332+
<div className="flex items-center">
333+
<div className="w-4 h-4 bg-yellow-500 mr-2"></div>
334+
<span>JIT / FREEBIE_JIT</span>
335+
</div>
336+
<Popover>
337+
<PopoverTrigger>
338+
<div
339+
className="text-sm text-blue-600 hover:text-blue-800 cursor-help">
340+
How are edge colors calculated?
341+
</div>
342+
</PopoverTrigger>
343+
<PopoverContent className="w-80">
344+
<div className="space-y-2">
345+
<h4 className="font-medium">Edge Color Calculation</h4>
346+
<p className="text-sm">
347+
When an edge has multiple outcomes, its color is
348+
calculated
349+
as a weighted average:
350+
</p>
351+
<ul className="text-sm list-disc pl-4 space-y-1">
352+
<li>Each outcome's color is weighted by its frequency
353+
</li>
354+
<li>For example, if an edge has 70% OK (green) and 30%
355+
ERROR
356+
(red), the resulting color will be a blend of these
357+
colors
358+
</li>
359+
<li>The final color includes 90% opacity to show
360+
overlapping
361+
edges
362+
</li>
363+
</ul>
364+
</div>
365+
</PopoverContent>
366+
</Popover>
285367
</div>
286-
<Popover>
287-
<PopoverTrigger>
288-
<div
289-
className="text-sm text-blue-600 hover:text-blue-800 cursor-help">
290-
How are edge colors calculated?
291-
</div>
292-
</PopoverTrigger>
293-
<PopoverContent className="w-80">
294-
<div className="space-y-2">
295-
<h4 className="font-medium">Edge Color Calculation</h4>
296-
<p className="text-sm">
297-
When an edge has multiple outcomes, its color is calculated
298-
as a weighted average:
299-
</p>
300-
<ul className="text-sm list-disc pl-4 space-y-1">
301-
<li>Each outcome's color is weighted by its frequency</li>
302-
<li>For example, if an edge has 70% OK (green) and 30% ERROR
303-
(red), the resulting color will be a blend of these
304-
colors
305-
</li>
306-
<li>The final color includes 90% opacity to show overlapping
307-
edges
308-
</li>
309-
</ul>
310-
</div>
311-
</PopoverContent>
312-
</Popover>
313368
</div>
314-
</div>
369+
)}
370+
315371
</div>
316372
</div>
317373
</div>

src/components/GraphvizProcessing.ts

Lines changed: 67 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export const loadAndSortData = (csvData: string): CSVRow[] => {
5353
* @param selfLoops - A boolean to include self-loops.
5454
* @returns A dictionary mapping session IDs to sequences of step names.
5555
*/
56-
export const createStepSequences = (sortedData: CSVRow[], selfLoops: boolean): { [key: string]: string[] } => {
56+
export const createStepSequences = (sortedData: CSVRow[], selfLoops: boolean): { [key: string]: { [key: string]: string[] } } => {
5757
return sortedData.reduce((acc, row) => {
5858
const studentId: string = row['Anon Student Id'];
5959
const problemName: string = row['Problem Name'];
@@ -75,7 +75,7 @@ export const createStepSequences = (sortedData: CSVRow[], selfLoops: boolean): {
7575
* @param sortedData - The sorted CSV rows.
7676
* @returns A dictionary mapping session IDs to sequences of outcomes.
7777
*/
78-
export const createOutcomeSequences = (sortedData: CSVRow[]): { [key: string]: string[] } => {
78+
export const createOutcomeSequences = (sortedData: CSVRow[]): { [key: string]: { [key: string]: string[] } } => {
7979
return sortedData.reduce((acc, row) => {
8080
const studentId = row['Anon Student Id'];
8181
const problemName = row['Problem Name'];
@@ -305,14 +305,50 @@ export function calculateColor(rank: number, totalSteps: number): string {
305305
* @param errorMode
306306
* @returns A color representing the most frequent outcome.
307307
*/
308+
// function calculateEdgeColors(outcomes: { [outcome: string]: number }, errorMode: boolean): string {
309+
// const colorMap: { [key: string]: string } = errorMode ? {
310+
// 'ERROR': '#ff0000', // Red
311+
// // 'OK': '#ffffff', // Black
312+
// 'INITIAL_HINT': '#0000ff', // Blue
313+
// 'HINT_LEVEL_CHANGE': '#0000ff',
314+
// 'JIT': '#ffff00', // Yellow
315+
// 'FREEBIE_JIT': '#ffff00'
316+
// } : {
317+
// 'ERROR': '#ff0000',
318+
// 'OK': '#00ff00', // Green
319+
// 'INITIAL_HINT': '#0000ff',
320+
// 'HINT_LEVEL_CHANGE': '#0000ff',
321+
// 'JIT': '#ffff00',
322+
// 'FREEBIE_JIT': '#ffff00'
323+
// };
324+
//
325+
// if (Object.keys(outcomes).length === 0) {
326+
// return '#00000000'; // Transparent black
327+
// }
328+
//
329+
// const totalCount = Object.values(outcomes).reduce((sum, count) => sum + count, 0);
330+
// let weightedR = 0, weightedG = 0, weightedB = 0;
331+
//
332+
// Object.entries(outcomes).forEach(([outcome, count]) => {
333+
// const color = colorMap[outcome] || '#000000'; // Default to black if outcome is not found
334+
// const [r, g, b] = [1, 3, 5].map(i => parseInt(color.slice(i, i + 2), 16)); // Extract RGB values
335+
// const weight = count / totalCount;
336+
// weightedR += r * weight;
337+
// weightedG += g * weight;
338+
// weightedB += b * weight;
339+
// });
340+
//
341+
// // Convert RGB values to hex and add alpha transparency
342+
// return `#${Math.round(weightedR).toString(16).padStart(2, '0')}${Math.round(weightedG).toString(16).padStart(2, '0')}${Math.round(weightedB).toString(16).padStart(2, '0')}90`;
343+
// }
308344
function calculateEdgeColors(outcomes: { [outcome: string]: number }, errorMode: boolean): string {
309345
const colorMap: { [key: string]: string } = errorMode ? {
310346
'ERROR': '#ff0000', // Red
311-
// 'OK': '#000000', // Black
312347
'INITIAL_HINT': '#0000ff', // Blue
313348
'HINT_LEVEL_CHANGE': '#0000ff',
314349
'JIT': '#ffff00', // Yellow
315350
'FREEBIE_JIT': '#ffff00'
351+
// 'OK' intentionally excluded
316352
} : {
317353
'ERROR': '#ff0000',
318354
'OK': '#00ff00', // Green
@@ -323,23 +359,41 @@ function calculateEdgeColors(outcomes: { [outcome: string]: number }, errorMode:
323359
};
324360

325361
if (Object.keys(outcomes).length === 0) {
326-
return '#00000000'; // Transparent black
362+
return '#00000000'; // Transparent
327363
}
328364

329-
const totalCount = Object.values(outcomes).reduce((sum, count) => sum + count, 0);
330365
let weightedR = 0, weightedG = 0, weightedB = 0;
366+
let totalCount = 0;
367+
let contributingOutcomes = 0;
331368

332369
Object.entries(outcomes).forEach(([outcome, count]) => {
333-
const color = colorMap[outcome] || '#000000'; // Default to black if outcome is not found
334-
const [r, g, b] = [1, 3, 5].map(i => parseInt(color.slice(i, i + 2), 16)); // Extract RGB values
335-
const weight = count / totalCount;
336-
weightedR += r * weight;
337-
weightedG += g * weight;
338-
weightedB += b * weight;
370+
if (errorMode && outcome === 'OK') return; // Skip OK in errorMode
371+
372+
const color = colorMap[outcome];
373+
if (!color) return; // Skip if not in colorMap
374+
375+
const [r, g, b] = [1, 3, 5].map(i => parseInt(color.slice(i, i + 2), 16));
376+
weightedR += r * count;
377+
weightedG += g * count;
378+
weightedB += b * count;
379+
totalCount += count;
380+
contributingOutcomes++;
339381
});
340382

341-
// Convert RGB values to hex and add alpha transparency
342-
return `#${Math.round(weightedR).toString(16).padStart(2, '0')}${Math.round(weightedG).toString(16).padStart(2, '0')}${Math.round(weightedB).toString(16).padStart(2, '0')}90`;
383+
// If nothing contributed but 'OK' was present (and we're in errorMode), return black
384+
if (contributingOutcomes === 0 && errorMode && outcomes['OK']) {
385+
return '#00000090'; // Black with alpha
386+
}
387+
388+
if (totalCount === 0) {
389+
return '#00000000'; // Fully transparent fallback
390+
}
391+
392+
const rHex = Math.round(weightedR / totalCount).toString(16).padStart(2, '0');
393+
const gHex = Math.round(weightedG / totalCount).toString(16).padStart(2, '0');
394+
const bHex = Math.round(weightedB / totalCount).toString(16).padStart(2, '0');
395+
396+
return `#${rHex}${gHex}${bHex}90`; // Final color
343397
}
344398

345399
/**

0 commit comments

Comments
 (0)