pappitti commited on
Commit
5b91336
·
1 Parent(s): d26f541

cleaning and tweaking

Browse files
README.md CHANGED
@@ -4,4 +4,12 @@ fetch data and build the database: `npm db:rebuild`
4
  This will read the fils from the HuggingFace repository and create a DuckDB database at the root of the project
5
 
6
  run the app ;
7
- `npm dev`
 
 
 
 
 
 
 
 
 
4
  This will read the fils from the HuggingFace repository and create a DuckDB database at the root of the project
5
 
6
  run the app ;
7
+ `npm dev`
8
+
9
+ TODO:
10
+ - clean unused modules and components
11
+ - loading
12
+ - DB indexing
13
+ - click on waterfall
14
+ - move all interfaces and types to types.ts
15
+ - test on new device
api/reclassification.ts CHANGED
@@ -3,7 +3,7 @@ import db from '../src/lib/db.js';
3
  import { jsonResponse } from './utils.js';
4
 
5
  export default async function handler(req: IncomingMessage, res: ServerResponse) {
6
- // We need to parse query parameters from the URL
7
  const url = new URL(req.url!, `http://${req.headers.host}`);
8
  const judge1 = url.searchParams.get('judge1');
9
  const judge2 = url.searchParams.get('judge2');
@@ -28,7 +28,7 @@ export default async function handler(req: IncomingMessage, res: ServerResponse)
28
  sql, judge1, judge2, theme, theme
29
  );
30
 
31
- // The matrix logic is identical
32
  const transitionMatrix: Record<string, Record<string, number>> = {};
33
  for (const row of rows) {
34
  if (!transitionMatrix[row.judge1_compliance]) {
 
3
  import { jsonResponse } from './utils.js';
4
 
5
  export default async function handler(req: IncomingMessage, res: ServerResponse) {
6
+ // parsing query parameters from the URL
7
  const url = new URL(req.url!, `http://${req.headers.host}`);
8
  const judge1 = url.searchParams.get('judge1');
9
  const judge2 = url.searchParams.get('judge2');
 
28
  sql, judge1, judge2, theme, theme
29
  );
30
 
31
+ // matrix logic
32
  const transitionMatrix: Record<string, Record<string, number>> = {};
33
  for (const row of rows) {
34
  if (!transitionMatrix[row.judge1_compliance]) {
package.json CHANGED
@@ -19,6 +19,7 @@
19
  "plotly.js": "^3.0.1",
20
  "react": "^19.1.0",
21
  "react-dom": "^19.1.0",
 
22
  "react-plotly.js": "^2.6.0"
23
  },
24
  "devDependencies": {
 
19
  "plotly.js": "^3.0.1",
20
  "react": "^19.1.0",
21
  "react-dom": "^19.1.0",
22
+ "react-markdown": "8.0.6",
23
  "react-plotly.js": "^2.6.0"
24
  },
25
  "devDependencies": {
src/App.tsx CHANGED
@@ -1,4 +1,3 @@
1
- // packages/frontend/src/App.tsx
2
  import { useState, useEffect } from 'react';
3
  // import { Container, Typography, Box } from '@mui/material';
4
  import Waterfall from './components/Waterfall.js';
@@ -9,20 +8,17 @@ import type { Theme, TransitionMatrix, AssessmentItem } from './types';
9
  import FilterBar from './components/Filterbar';
10
 
11
  function App() {
12
- // State to hold our fetched data for the filters
13
  const [themes, setThemes] = useState<Theme[]>([]);
14
  const [judges, setJudges] = useState<string[]>([]);
15
  const [matrix, setMatrix] = useState<TransitionMatrix | null>(null);
16
  const [error, setError] = useState<string | null>(null);
17
  const [isLoading, setIsLoading] = useState(false);
18
 
19
- // State for the currently selected filters
20
  const [selectedTheme, setSelectedTheme] = useState<string>('');
21
  const [selectedJudge1, setSelectedJudge1] = useState<string>('');
22
  const [selectedJudge2, setSelectedJudge2] = useState<string>('');
23
-
24
  const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
25
-
26
  const [selectedItems, setSelectedItems] = useState<AssessmentItem[]>([]);
27
 
28
 
@@ -49,9 +45,9 @@ function App() {
49
  }
50
  };
51
  loadFilters();
52
- }, []); // The empty array ensures this runs only once on mount
53
 
54
-
55
  useEffect(() => {
56
  if (!selectedJudge1 || !selectedJudge2) return;
57
 
@@ -72,6 +68,7 @@ function App() {
72
  fetchData();
73
  }, [selectedTheme, selectedJudge1, selectedJudge2]);
74
 
 
75
  const handleCellClick = (fromCategory: string, toCategory: string) => {
76
  if (selectedJudge1 && selectedJudge2 && fromCategory && toCategory) {
77
  getAssessmentItems(selectedJudge1, selectedJudge2, fromCategory, toCategory, selectedTheme)
 
 
1
  import { useState, useEffect } from 'react';
2
  // import { Container, Typography, Box } from '@mui/material';
3
  import Waterfall from './components/Waterfall.js';
 
8
  import FilterBar from './components/Filterbar';
9
 
10
  function App() {
11
+
12
  const [themes, setThemes] = useState<Theme[]>([]);
13
  const [judges, setJudges] = useState<string[]>([]);
14
  const [matrix, setMatrix] = useState<TransitionMatrix | null>(null);
15
  const [error, setError] = useState<string | null>(null);
16
  const [isLoading, setIsLoading] = useState(false);
17
 
 
18
  const [selectedTheme, setSelectedTheme] = useState<string>('');
19
  const [selectedJudge1, setSelectedJudge1] = useState<string>('');
20
  const [selectedJudge2, setSelectedJudge2] = useState<string>('');
 
21
  const [selectedCategory, setSelectedCategory] = useState<string | null>(null);
 
22
  const [selectedItems, setSelectedItems] = useState<AssessmentItem[]>([]);
23
 
24
 
 
45
  }
46
  };
47
  loadFilters();
48
+ }, []);
49
 
50
+ // Data fetching logic
51
  useEffect(() => {
52
  if (!selectedJudge1 || !selectedJudge2) return;
53
 
 
68
  fetchData();
69
  }, [selectedTheme, selectedJudge1, selectedJudge2]);
70
 
71
+ // Handle cell click to fetch assessment items
72
  const handleCellClick = (fromCategory: string, toCategory: string) => {
73
  if (selectedJudge1 && selectedJudge2 && fromCategory && toCategory) {
74
  getAssessmentItems(selectedJudge1, selectedJudge2, fromCategory, toCategory, selectedTheme)
src/components/Heatmap.tsx CHANGED
@@ -16,7 +16,7 @@ const Heatmap: React.FC<HeatmapProps> = ({ matrix, judge1, judge2, onCellClick }
16
  ) || 1; // Avoid division by zero
17
 
18
  const getOpacity = (value: number) => {
19
- return value === 0 ? 0.1 : 0.1 + (value / maxValue) * 0.9;
20
  };
21
 
22
  return (
@@ -41,13 +41,13 @@ const Heatmap: React.FC<HeatmapProps> = ({ matrix, judge1, judge2, onCellClick }
41
  <div
42
  key={`${fromCat}-${toCat}`}
43
  className="heatmap-cell"
44
- style={{
45
- backgroundColor: COLOR_MAP[toCat],
46
- opacity: getOpacity(value)
47
- }}
48
  onClick={() => onCellClick(fromCat, toCat)}
49
  title={`${fromCat} → ${toCat}: ${value}`}
50
  >
 
 
 
 
51
  {value > 0 && <span className="cell-value">{value}</span>}
52
  </div>
53
  );
 
16
  ) || 1; // Avoid division by zero
17
 
18
  const getOpacity = (value: number) => {
19
+ return value === 0 ? 0.05 : 0.05 + (value / maxValue) * 0.95;
20
  };
21
 
22
  return (
 
41
  <div
42
  key={`${fromCat}-${toCat}`}
43
  className="heatmap-cell"
 
 
 
 
44
  onClick={() => onCellClick(fromCat, toCat)}
45
  title={`${fromCat} → ${toCat}: ${value}`}
46
  >
47
+ <div
48
+ className='heatmap-cell-bg'
49
+ style={{ backgroundColor: COLOR_MAP[toCat], opacity: getOpacity(value) }}>
50
+ </div>
51
  {value > 0 && <span className="cell-value">{value}</span>}
52
  </div>
53
  );
src/components/itemList.tsx CHANGED
@@ -1,3 +1,4 @@
 
1
  import type { AssessmentItem } from '../types';
2
 
3
  interface AssessmentItemsProps {
@@ -30,7 +31,7 @@ const AssessmentItems: React.FC<AssessmentItemsProps> = ({ items, selectedCatego
30
 
31
  <div className="item-answer">
32
  <h4>LLM Response ({item.model})</h4>
33
- <p>{item.response}</p>
34
  </div>
35
  <h4>Assessments</h4>
36
  <div className="item-assessments">
@@ -45,7 +46,7 @@ const AssessmentItems: React.FC<AssessmentItemsProps> = ({ items, selectedCatego
45
  </span>
46
  </div>
47
  <div className="assessment-analysis">
48
- {assessment.judge_analysis || 'No analysis provided'}
49
  </div>
50
  </div>
51
  ))}
 
1
+ import ReactMarkdown from 'react-markdown';
2
  import type { AssessmentItem } from '../types';
3
 
4
  interface AssessmentItemsProps {
 
31
 
32
  <div className="item-answer">
33
  <h4>LLM Response ({item.model})</h4>
34
+ <div className='markdown-content'><ReactMarkdown>{item.response}</ReactMarkdown></div>
35
  </div>
36
  <h4>Assessments</h4>
37
  <div className="item-assessments">
 
46
  </span>
47
  </div>
48
  <div className="assessment-analysis">
49
+ <ReactMarkdown>{assessment.judge_analysis || 'No analysis provided'}</ReactMarkdown>
50
  </div>
51
  </div>
52
  ))}
src/index.css CHANGED
@@ -206,9 +206,16 @@ body {
206
  display: flex;
207
  align-items: start;
208
  justify-content: center;
 
209
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
210
  }
211
 
 
 
 
 
 
 
212
  .bar-value {
213
  position: absolute;
214
  bottom: -2rem;
@@ -369,6 +376,7 @@ body {
369
  }
370
 
371
  .heatmap-cell {
 
372
  border-radius: 6px;
373
  cursor: pointer;
374
  display: flex;
@@ -377,6 +385,8 @@ body {
377
  transition: all 0.2s ease;
378
  border: 1px solid rgba(255, 255, 255, 0.2);
379
  position: relative;
 
 
380
  }
381
 
382
  .heatmap-cell:hover {
@@ -385,11 +395,28 @@ body {
385
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
386
  }
387
 
 
 
 
 
 
388
  .cell-value {
 
 
 
 
389
  color: white;
390
  font-weight: 600;
391
- font-size: 0.875rem;
392
- text-shadow: 0 1px 2px rgba(0, 0, 0, 0.5);
 
 
 
 
 
 
 
 
393
  }
394
 
395
  /* Assessment Items Styles */
@@ -518,6 +545,64 @@ body {
518
  font-size: 0.875rem;
519
  }
520
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
521
  /* Responsive Design */
522
  @media (max-width: 1200px) {
523
  .charts-container {
 
206
  display: flex;
207
  align-items: start;
208
  justify-content: center;
209
+ transition: all 0.2s ease;
210
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
211
  }
212
 
213
+ .waterfall-bar:hover {
214
+ transform: scale(1.05);
215
+ z-index: 10;
216
+ box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
217
+ }
218
+
219
  .bar-value {
220
  position: absolute;
221
  bottom: -2rem;
 
376
  }
377
 
378
  .heatmap-cell {
379
+ position: relative;
380
  border-radius: 6px;
381
  cursor: pointer;
382
  display: flex;
 
385
  transition: all 0.2s ease;
386
  border: 1px solid rgba(255, 255, 255, 0.2);
387
  position: relative;
388
+ border-radius: 6px;
389
+ overflow: hidden;
390
  }
391
 
392
  .heatmap-cell:hover {
 
395
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
396
  }
397
 
398
+ .heatmap-cell-bg {
399
+ width: 100%;
400
+ height: 100%;
401
+ }
402
+
403
  .cell-value {
404
+ position:absolute;
405
+ z-index: 1;
406
+ width: 100%;
407
+ height: 100%;
408
  color: white;
409
  font-weight: 600;
410
+ font-size: 0.875rem;;
411
+ text-shadow: 1px 1px 0 rgba(0, 0, 0, 0.5);
412
+ display: flex ;
413
+ align-items: center;
414
+ justify-content: center;
415
+ }
416
+
417
+ .cell-value:hover{
418
+ color: #6b7280;
419
+ text-shadow: none;
420
  }
421
 
422
  /* Assessment Items Styles */
 
545
  font-size: 0.875rem;
546
  }
547
 
548
+ /* Markdown Styles */
549
+ .markdown-content {
550
+ line-height: 1.6;
551
+ color: #333;
552
+ }
553
+
554
+ .markdown-content h1 {
555
+ color: #2c3e50;
556
+ border-bottom: 2px solid #eee;
557
+ padding-bottom: 0.3rem;
558
+ }
559
+
560
+ .markdown-content h2 {
561
+ color: #34495e;
562
+ margin-top: 2rem;
563
+ }
564
+
565
+ .markdown-content h3 {
566
+ color: #4b5563;
567
+ margin-top: 1.5rem;
568
+ margin-bottom: unset;
569
+ padding-bottom: 0.5rem;
570
+ border-bottom: none;
571
+ }
572
+
573
+ .markdown-content p{
574
+ margin: 0.25rem 0;
575
+ color: #6b7280;
576
+ }
577
+
578
+ .markdown-content pre {
579
+ background: #f8f9fa;
580
+ padding: 1rem;
581
+ border-radius: 8px;
582
+ overflow-x: auto;
583
+ }
584
+
585
+ .markdown-content blockquote {
586
+ border-left: 4px solid #3498db;
587
+ margin: 1rem 0;
588
+ padding-left: 1rem;
589
+ color: #666;
590
+ }
591
+
592
+ .markdown-content ul,
593
+ .markdown-content ol {
594
+ margin: 0.5rem 0;
595
+ padding-left: 1.5rem;
596
+ }
597
+
598
+ .markdown-content li {
599
+ color : #6b7280;
600
+ }
601
+
602
+ .markdown-content hr {
603
+ margin : 1rem 0;
604
+ }
605
+
606
  /* Responsive Design */
607
  @media (max-width: 1200px) {
608
  .charts-container {
src/lib/db.ts CHANGED
@@ -1,7 +1,6 @@
1
  import duckdb from 'duckdb';
2
  import path from 'path';
3
 
4
- // DB is one level up
5
  const ROOT_DIR = process.cwd(); // cwd() = Current Working Directory
6
  const dbPath = path.join(ROOT_DIR, 'database.duckdb');
7
 
 
1
  import duckdb from 'duckdb';
2
  import path from 'path';
3
 
 
4
  const ROOT_DIR = process.cwd(); // cwd() = Current Working Directory
5
  const dbPath = path.join(ROOT_DIR, 'database.duckdb');
6
 
src/lib/ingest.ts CHANGED
@@ -1,6 +1,6 @@
1
  import fs from 'fs';
2
  import path from 'path';
3
- import { fileURLToPath } from 'url'; // <-- IMPORT THIS
4
  import duckdb from 'duckdb';
5
 
6
 
@@ -13,8 +13,7 @@ export const DATA_SOURCES = {
13
  assessments: 'https://huggingface.co/datasets/PITTI/speechmap-assessments/resolve/main/consolidated_assessments.parquet',
14
  };
15
 
16
- // --- DATABASE HELPER ---
17
- // (This function is already perfect, no changes needed)
18
  function query(db: duckdb.Database, sql: string): Promise<any[]> {
19
  return new Promise((resolve, reject) => {
20
  db.all(sql, (err, res) => {
@@ -24,7 +23,7 @@ function query(db: duckdb.Database, sql: string): Promise<any[]> {
24
  });
25
  }
26
 
27
- // --- STANDALONE SCRIPT LOGIC ---
28
  async function rebuildDatabase() {
29
  console.log('--- Starting full database rebuild with DuckDB ---');
30
 
@@ -42,7 +41,7 @@ async function rebuildDatabase() {
42
  await query(db, 'INSTALL json; LOAD json;');
43
 
44
  console.log('Creating database schema...');
45
- // (Your schema and ingestion logic is fine, no changes needed)
46
  await query(db, `
47
  CREATE TABLE themes (slug VARCHAR PRIMARY KEY, name VARCHAR);
48
  CREATE TABLE questions (uuid VARCHAR PRIMARY KEY, id VARCHAR, category VARCHAR, domain VARCHAR, question VARCHAR, theme VARCHAR);
 
1
  import fs from 'fs';
2
  import path from 'path';
3
+ import { fileURLToPath } from 'url';
4
  import duckdb from 'duckdb';
5
 
6
 
 
13
  assessments: 'https://huggingface.co/datasets/PITTI/speechmap-assessments/resolve/main/consolidated_assessments.parquet',
14
  };
15
 
16
+
 
17
  function query(db: duckdb.Database, sql: string): Promise<any[]> {
18
  return new Promise((resolve, reject) => {
19
  db.all(sql, (err, res) => {
 
23
  });
24
  }
25
 
26
+
27
  async function rebuildDatabase() {
28
  console.log('--- Starting full database rebuild with DuckDB ---');
29
 
 
41
  await query(db, 'INSTALL json; LOAD json;');
42
 
43
  console.log('Creating database schema...');
44
+ // TODO : Indexing?
45
  await query(db, `
46
  CREATE TABLE themes (slug VARCHAR PRIMARY KEY, name VARCHAR);
47
  CREATE TABLE questions (uuid VARCHAR PRIMARY KEY, id VARCHAR, category VARCHAR, domain VARCHAR, question VARCHAR, theme VARCHAR);
src/types.ts CHANGED
@@ -2,7 +2,7 @@ export interface Question {
2
  uuid: string;
3
  id: string;
4
  category: string;
5
- domain?: string;
6
  question: string;
7
  theme?: string; // Optional, can be null
8
  }
@@ -12,8 +12,8 @@ export interface Response {
12
  q_uuid: string; // Foreign key to Question
13
  model?: string;
14
  timestamp?: string; // ISO date string
15
- api_provider?: string;
16
- provider?: string;
17
  content: string;
18
  matched: boolean; // Boolean, but stored as integer in SQLite
19
  origin?: string; // Optional, can be null
 
2
  uuid: string;
3
  id: string;
4
  category: string;
5
+ domain?: string; // Optional, can be null
6
  question: string;
7
  theme?: string; // Optional, can be null
8
  }
 
12
  q_uuid: string; // Foreign key to Question
13
  model?: string;
14
  timestamp?: string; // ISO date string
15
+ api_provider?: string; // Optional, can be null
16
+ provider?: string; // Optional, can be null
17
  content: string;
18
  matched: boolean; // Boolean, but stored as integer in SQLite
19
  origin?: string; // Optional, can be null
src/utils/apiUtils.ts CHANGED
@@ -4,20 +4,16 @@ interface ApiError {
4
  error: string;
5
  }
6
 
7
- // --- Reusable Fetch Helper ---
8
  // This helper centralizes our fetch logic and error handling.
9
  async function fetchAPI<T>(url: string, options?: RequestInit): Promise<T> {
10
  const response = await fetch(url, options);
11
 
12
- // Manually check for HTTP errors, as fetch doesn't reject on them
13
  if (!response.ok) {
14
- // Try to get a more specific error message from the response body
15
  const errorBody = (await response.json().catch(() => ({ error: 'An unknown error occurred' }))) as ApiError;
16
  const errorMessage = errorBody.error || `HTTP error! Status: ${response.status}`;
17
  throw new Error(errorMessage);
18
  }
19
 
20
- // If the request was successful, parse and return the JSON body
21
  return response.json() as Promise<T>;
22
  }
23
 
@@ -28,11 +24,12 @@ export const getThemes = (): Promise<Theme[]> => {
28
  return fetchAPI<Theme[]>('/api/themes');
29
  };
30
 
 
31
  export const getJudges = (): Promise<string[]> => {
32
  return fetchAPI<string[]>('/api/judges');
33
  };
34
 
35
- // New function to get the reclassification data
36
  export const getReclassificationData = (
37
  judge1: string,
38
  judge2: string,
@@ -52,6 +49,7 @@ export const getReclassificationData = (
52
  return fetchAPI<TransitionMatrix>(`/api/reclassification?${params.toString()}`);
53
  };
54
 
 
55
  export const getAssessmentItems = (
56
  judge1: string,
57
  judge2: string,
@@ -59,7 +57,7 @@ export const getAssessmentItems = (
59
  toCategory: string,
60
  theme?: string
61
  ): Promise<any[]> => {
62
- // Build the query string from the parameters
63
  const params = new URLSearchParams({
64
  judge1,
65
  judge2,
@@ -67,7 +65,6 @@ export const getAssessmentItems = (
67
  toCategory,
68
  });
69
 
70
- // Only add the theme parameter if it's provided
71
  if (theme) {
72
  params.append('theme', theme);
73
  }
 
4
  error: string;
5
  }
6
 
 
7
  // This helper centralizes our fetch logic and error handling.
8
  async function fetchAPI<T>(url: string, options?: RequestInit): Promise<T> {
9
  const response = await fetch(url, options);
10
 
 
11
  if (!response.ok) {
 
12
  const errorBody = (await response.json().catch(() => ({ error: 'An unknown error occurred' }))) as ApiError;
13
  const errorMessage = errorBody.error || `HTTP error! Status: ${response.status}`;
14
  throw new Error(errorMessage);
15
  }
16
 
 
17
  return response.json() as Promise<T>;
18
  }
19
 
 
24
  return fetchAPI<Theme[]>('/api/themes');
25
  };
26
 
27
+
28
  export const getJudges = (): Promise<string[]> => {
29
  return fetchAPI<string[]>('/api/judges');
30
  };
31
 
32
+
33
  export const getReclassificationData = (
34
  judge1: string,
35
  judge2: string,
 
49
  return fetchAPI<TransitionMatrix>(`/api/reclassification?${params.toString()}`);
50
  };
51
 
52
+
53
  export const getAssessmentItems = (
54
  judge1: string,
55
  judge2: string,
 
57
  toCategory: string,
58
  theme?: string
59
  ): Promise<any[]> => {
60
+
61
  const params = new URLSearchParams({
62
  judge1,
63
  judge2,
 
65
  toCategory,
66
  });
67
 
 
68
  if (theme) {
69
  params.append('theme', theme);
70
  }
src/utils/chartUtils.ts CHANGED
@@ -1,8 +1,6 @@
1
- // packages/frontend/src/lib/chartUtils.ts
2
  import type { TransitionMatrix } from '../types.js';
3
- import type { Data } from 'plotly.js';
4
 
5
- // --- Constants (mirrored from your Python script) ---
6
  export const CATEGORIES = ["COMPLETE", "EVASIVE", "DENIAL", "ERROR" /*, "UNKNOWN"*/];
7
  export const COLOR_MAP: Record<string, string> = {
8
  "BASE": 'rgba(0,0,0,0)', // Transparent for the base of flow bars
@@ -13,7 +11,7 @@ export const COLOR_MAP: Record<string, string> = {
13
  /*"UNKNOWN": "#6b7280"*/
14
  };
15
 
16
- // --- Type Definitions for our Chart Data Structures ---
17
  interface Segment {
18
  category_label: string;
19
  value: number;
@@ -24,8 +22,6 @@ interface PlotStage {
24
  segments: Segment[];
25
  }
26
 
27
- // --- Main Logic: Port of your Python `create_waterfall_stages` function ---
28
-
29
  export function generateWaterfallData(
30
  matrix: TransitionMatrix,
31
  judge1Name: string,
@@ -103,17 +99,17 @@ export function generateWaterfallData(
103
  }
104
 
105
 
106
- export function generateHeatmapData(matrix: TransitionMatrix) : Data {
107
- // Create a 2D array (z-axis) for the heatmap values
108
- const z = CATEGORIES.map(j1Cat =>
109
- CATEGORIES.map(j2Cat => matrix[j1Cat]?.[j2Cat] || 0)
110
- );
111
- return {
112
- type: 'heatmap' as const, // <--- ADD THIS
113
- z,
114
- x: CATEGORIES,
115
- y: CATEGORIES,
116
- colorscale: 'Viridis' as const, // Optional: add a colorscale for better visuals
117
- hoverongaps: false,
118
- };
119
- }
 
 
1
  import type { TransitionMatrix } from '../types.js';
 
2
 
3
+
4
  export const CATEGORIES = ["COMPLETE", "EVASIVE", "DENIAL", "ERROR" /*, "UNKNOWN"*/];
5
  export const COLOR_MAP: Record<string, string> = {
6
  "BASE": 'rgba(0,0,0,0)', // Transparent for the base of flow bars
 
11
  /*"UNKNOWN": "#6b7280"*/
12
  };
13
 
14
+
15
  interface Segment {
16
  category_label: string;
17
  value: number;
 
22
  segments: Segment[];
23
  }
24
 
 
 
25
  export function generateWaterfallData(
26
  matrix: TransitionMatrix,
27
  judge1Name: string,
 
99
  }
100
 
101
 
102
+ // export function generateHeatmapData(matrix: TransitionMatrix) : Data {
103
+ // // Create a 2D array (z-axis) for the heatmap values
104
+ // const z = CATEGORIES.map(j1Cat =>
105
+ // CATEGORIES.map(j2Cat => matrix[j1Cat]?.[j2Cat] || 0)
106
+ // );
107
+ // return {
108
+ // type: 'heatmap' as const, // <--- ADD THIS
109
+ // z,
110
+ // x: CATEGORIES,
111
+ // y: CATEGORIES,
112
+ // colorscale: 'Viridis' as const, // Optional: add a colorscale for better visuals
113
+ // hoverongaps: false,
114
+ // };
115
+ // }