@@ -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+ // }
308344function 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