Created
August 26, 2024 03:50
-
-
Save sjgknight/31851bf2a3cfa96afdbee24e10259d1a to your computer and use it in GitHub Desktop.
Single page tool to take CSVs and map across columns into an LR mermaid flowchart.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<meta name="description" content="Tool to take CSVs and map across columns into an LR mermaid flowchart."> | |
<meta name="author" content="sjgknight with prompting of ChatGPT and customisation"> | |
<meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
<title>CSV to Mermaid Converter</title> | |
<!-- Load Mermaid.js --> | |
<script type="module"> | |
let mermaidImport = undefined | |
// Import Mermaid if not already imported | |
mermaidImport ||= await import('https://cdnjs.cloudflare.com/ajax/libs/mermaid/10.7.0/mermaid.esm.min.mjs'); | |
const mermaid = mermaidImport.default; | |
// Initialize Mermaid | |
mermaid.initialize({ | |
startOnLoad: false, | |
securityLevel: 'loose', | |
maxTextSize: 90000, | |
flowchart: { | |
htmlLabels: false, | |
curve: 'monotoneX', | |
}, | |
}); | |
window.mermaid = mermaid; // Expose Mermaid to global scope for later use | |
</script> | |
</head> | |
<body> | |
<h1>CSV to Mermaid Converter</h1> | |
<input type="file" id="csvFileInput" accept=".csv" /> | |
<h2>Mermaid Output</h2> | |
<div id="mermaidContainer"> | |
<!-- Mermaid diagram will be rendered inside this container --> | |
<pre><code id="mermaid" class="mermaid"></code></pre> | |
</div> | |
<h2>JSON Data</h2> | |
<pre id="jsonOutput"></pre> <!-- Element for JSON output --> | |
<script> | |
document.getElementById('csvFileInput').addEventListener('change', handleFileSelect); | |
function handleFileSelect(event) { | |
const file = event.target.files[0]; | |
const reader = new FileReader(); | |
reader.onload = function(e) { | |
const csvData = e.target.result; | |
const jsonData = csvToJson(csvData); | |
// Print JSON data to the page | |
document.getElementById('jsonOutput').textContent = JSON.stringify(jsonData, null, 2); | |
const mermaidSyntax = processJson(jsonData); | |
// Set the Mermaid syntax with proper formatting | |
//document.getElementById('debug').textContent = mermaidSyntax; | |
displayMermaidDiagram(mermaidSyntax); | |
}; | |
reader.readAsText(file); | |
} | |
function csvToJson(csvData) { | |
const rows = csvData.trim().split('\n').map(row => splitCSVRow(row)); | |
const headers = rows[0]; | |
const data = rows.slice(1); | |
const cellMap = {}; // To store the first occurrence mapping | |
// Map cell values to names based on their first occurrence | |
const mappedData = data.map((row, rowIndex) => { | |
return row.map((cell, colIndex) => { | |
const cellAddress = `${String.fromCharCode(65 + colIndex)}${rowIndex + 2}`; | |
const cellValue = cellMap[cell.trim()] || cellAddress; // Use cellAddress as unique node identifier | |
if (!cellMap[cell.trim()]) { | |
cellMap[cell.trim()] = cellAddress; | |
} | |
return { | |
value: cellValue, // Unique node identifier | |
ref: cellAddress, | |
nodeValue: cell.trim() // Original cell value for display | |
}; | |
}); | |
}); | |
return { headers, data: mappedData }; | |
} | |
function processJson(jsonData) { | |
const { headers, data } = jsonData; | |
const nodes = []; | |
const connections = []; | |
const nodeReferences = {}; // Maps cell values to unique references | |
// Generate nodes | |
headers.forEach((header, colIndex) => { | |
nodes.push(`subgraph ${String.fromCharCode(65 + colIndex)}["\`${sanitizeText(header)}\`"]`); | |
data.forEach((row) => { | |
const cell = row[colIndex]; | |
const cellValue = cell.value; // Unique node identifier | |
const cellRef = cell.ref; | |
if (cellValue && !nodeReferences[cellValue]) { | |
nodeReferences[cellValue] = cellRef; | |
nodes.push(`${cellRef}["\`${cell.nodeValue}\`"]`); // Display original cell value | |
} | |
}); | |
nodes.push('end'); | |
}); | |
// Generate connections based on value field | |
data.forEach((row) => { | |
let previousCellValue = null; | |
headers.forEach((header, colIndex) => { | |
const currentCell = row[colIndex]; | |
const currentCellValue = currentCell.value; | |
if (previousCellValue) { | |
connections.push(`${previousCellValue} --> ${currentCellValue}`); | |
} | |
previousCellValue = currentCellValue; | |
}); | |
}); | |
return [...nodes, ...connections].join('\n'); | |
} | |
function splitCSVRow(row) { | |
const pattern = /("[^"]*"|[^",\s][^,]*)(?=\s*,|\s*$)/g; | |
const matches = []; | |
let match; | |
while ((match = pattern.exec(row)) !== null) { | |
matches.push(match[1].replace(/(^"|"$)/g, '')); // Remove surrounding double quotes if present | |
} | |
return matches; | |
} | |
function sanitizeText(text) { | |
// Remove any internal double quotes, trim spaces, and ensure valid Mermaid syntax | |
return text.replace(/"/g, '').trim(); | |
} | |
function displayMermaidDiagram(mermaidSyntax) { | |
const mermaidOutput = document.getElementById('mermaid'); | |
// Set the Mermaid syntax with proper formatting | |
mermaidOutput.textContent = `flowchart TD\n${mermaidSyntax}`; | |
// Initialize Mermaid to render the diagram | |
mermaid.init(undefined, document.querySelectorAll('code.mermaid')); | |
} | |
</script> | |
</body> | |
</html> |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment