CSV funktionert
This commit is contained in:
@@ -84,16 +84,107 @@ function renderResult(result) {
|
|||||||
renderTree(treeEl, result, "result", true);
|
renderTree(treeEl, result, "result", true);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fetchCSV() {
|
||||||
|
const res = await fetch("/api/latest-snapshot");
|
||||||
|
if (!res.ok) throw new Error("Fehler beim Laden des Snapshots");
|
||||||
|
|
||||||
|
let data;
|
||||||
|
if (res.headers.get("content-type")?.includes("application/json")) {
|
||||||
|
data = await res.json();
|
||||||
|
} else {
|
||||||
|
const csvData = await res.text();
|
||||||
|
data = {
|
||||||
|
filename: "latest.csv",
|
||||||
|
mtime: new Date().toISOString(),
|
||||||
|
content: csvData
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const lines = data.content.trim().split(/\r?\n/).filter(Boolean);
|
||||||
|
if (lines.length < 2) {
|
||||||
|
throw new Error("Keine oder unvollständige Daten");
|
||||||
|
}
|
||||||
|
|
||||||
|
const headers = lines[0].split(",").map(h => h.trim());
|
||||||
|
|
||||||
|
const rows = lines.slice(1).map(line => {
|
||||||
|
const cells = line.split(",");
|
||||||
|
const obj = {};
|
||||||
|
headers.forEach((h, i) => {
|
||||||
|
const raw = (cells[i] ?? "").trim();
|
||||||
|
const numeric = Number(raw);
|
||||||
|
obj[h] = raw !== "" && Number.isFinite(numeric) ? numeric : raw;
|
||||||
|
});
|
||||||
|
return obj;
|
||||||
|
});
|
||||||
|
|
||||||
|
return { data, headers, rows };
|
||||||
|
}
|
||||||
|
|
||||||
|
async function renderSnapshot() {
|
||||||
|
const table = document.getElementById("snapshot-table");
|
||||||
|
if (!table) return;
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { data, headers, rows } = await fetchCSV();
|
||||||
|
|
||||||
|
// Info anzeigen
|
||||||
|
const infoEl = document.getElementById("snapshot-info");
|
||||||
|
if (infoEl) {
|
||||||
|
infoEl.textContent = `Datei: ${data.filename}, Geändert: ${new Date(data.mtime).toLocaleString()}, Zeilen: ${rows.length}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tabelle leeren
|
||||||
|
table.innerHTML = "";
|
||||||
|
|
||||||
|
// Header
|
||||||
|
const thead = document.createElement("thead");
|
||||||
|
const headerRow = document.createElement("tr");
|
||||||
|
headers.forEach(h => {
|
||||||
|
const th = document.createElement("th");
|
||||||
|
th.textContent = h;
|
||||||
|
headerRow.appendChild(th);
|
||||||
|
});
|
||||||
|
thead.appendChild(headerRow);
|
||||||
|
table.appendChild(thead);
|
||||||
|
|
||||||
|
// Body
|
||||||
|
const tbody = document.createElement("tbody");
|
||||||
|
rows.forEach(row => {
|
||||||
|
const tr = document.createElement("tr");
|
||||||
|
headers.forEach(h => {
|
||||||
|
const td = document.createElement("td");
|
||||||
|
let value = row[h];
|
||||||
|
if (typeof value === 'number') {
|
||||||
|
if (h === 'id' || h === 'seen_by') {
|
||||||
|
value = Math.round(value);
|
||||||
|
} else {
|
||||||
|
value = value.toFixed(1);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
td.textContent = value;
|
||||||
|
tr.appendChild(td);
|
||||||
|
});
|
||||||
|
tbody.appendChild(tr);
|
||||||
|
});
|
||||||
|
table.appendChild(tbody);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Fehler beim Rendern des Snapshots:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
async function onCalculateClick() {
|
async function onCalculateClick() {
|
||||||
clearTextarea("analysis-log");
|
clearTextarea("analysis-log");
|
||||||
clearTextarea("result-json");
|
clearTextarea("result-json");
|
||||||
clearElement("result-tree");
|
clearElement("result-tree");
|
||||||
|
clearElement("snapshot-table");
|
||||||
|
|
||||||
appendLog("Starte Berechnung...");
|
appendLog("Starte Berechnung...");
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const result = await window.calculate();
|
const result = await window.calculate();
|
||||||
renderResult(result);
|
renderResult(result);
|
||||||
|
await renderSnapshot();
|
||||||
appendLog("Result angezeigt.");
|
appendLog("Result angezeigt.");
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
appendLog(`Fehler: ${err.message}`);
|
appendLog(`Fehler: ${err.message}`);
|
||||||
|
|||||||
@@ -74,7 +74,13 @@
|
|||||||
|
|
||||||
<!-- SNAPSHOT (vorbereitet, aber leer) -->
|
<!-- SNAPSHOT (vorbereitet, aber leer) -->
|
||||||
<div class="section full">
|
<div class="section full">
|
||||||
<h2>Neuester Snapshot</h2>
|
<h2>Snapshot CSV</h2>
|
||||||
|
<div id="snapshot-info"></div>
|
||||||
|
<table id="snapshot-table"></table>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="section full">
|
||||||
|
<h2>Snapshot Image</h2>
|
||||||
<div id="snapshot-info"></div>
|
<div id="snapshot-info"></div>
|
||||||
<table id="snapshot-table"></table>
|
<table id="snapshot-table"></table>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@@ -175,4 +175,40 @@ textarea {
|
|||||||
#result-tree .tree-leaf {
|
#result-tree .tree-leaf {
|
||||||
margin-left: 18px;
|
margin-left: 18px;
|
||||||
white-space: pre-wrap;
|
white-space: pre-wrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ===== SNAPSHOT TABLE ===== */
|
||||||
|
|
||||||
|
#snapshot-table {
|
||||||
|
width: 100%;
|
||||||
|
border-collapse: collapse;
|
||||||
|
margin-top: 12px;
|
||||||
|
font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
||||||
|
font-size: 12px;
|
||||||
|
line-height: 1.2;
|
||||||
|
}
|
||||||
|
|
||||||
|
#snapshot-table th,
|
||||||
|
#snapshot-table td {
|
||||||
|
padding: 4px 8px;
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
|
||||||
|
#snapshot-table th {
|
||||||
|
background: #1e293b;
|
||||||
|
color: var(--accent);
|
||||||
|
font-weight: bold;
|
||||||
|
border-bottom: 1px solid #334155;
|
||||||
|
}
|
||||||
|
|
||||||
|
#snapshot-table td {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#snapshot-table tbody tr:nth-child(even) {
|
||||||
|
background: #0f172a;
|
||||||
|
}
|
||||||
|
|
||||||
|
#snapshot-table tbody tr:hover {
|
||||||
|
background: #1e293b;
|
||||||
}
|
}
|
||||||
@@ -185,6 +185,10 @@ app.get('/api/events', (req, res) => {
|
|||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
//snapshot_video0_1775319258906_two_cam.csv
|
||||||
|
//snapshot_video0_1775319258906_two_cam_annotated.jpg
|
||||||
|
|
||||||
// Neuester Snapshot-Endpunkt
|
// Neuester Snapshot-Endpunkt
|
||||||
app.get('/api/latest-snapshot', (req, res) => {
|
app.get('/api/latest-snapshot', (req, res) => {
|
||||||
const snapshotsDir = path.join(path.resolve('public'), 'snapshots');
|
const snapshotsDir = path.join(path.resolve('public'), 'snapshots');
|
||||||
@@ -197,18 +201,35 @@ app.get('/api/latest-snapshot', (req, res) => {
|
|||||||
path: path.join(snapshotsDir, file),
|
path: path.join(snapshotsDir, file),
|
||||||
mtime: fs.statSync(path.join(snapshotsDir, file)).mtime
|
mtime: fs.statSync(path.join(snapshotsDir, file)).mtime
|
||||||
})).sort((a, b) => b.mtime - a.mtime);
|
})).sort((a, b) => b.mtime - a.mtime);
|
||||||
|
|
||||||
if (csvFiles.length === 0) {
|
if (csvFiles.length === 0) {
|
||||||
return res.status(404).json({ error: 'Keine CSV-Dateien gefunden' });
|
return res.status(404).json({ error: 'Keine CSV-Dateien gefunden' });
|
||||||
}
|
}
|
||||||
const latestFile = csvFiles[0];
|
const latestFile = csvFiles[0];
|
||||||
|
const baseName = path.basename(latestFile.name, path.extname(latestFile.name));
|
||||||
|
const imageFilename = `${baseName}_annotated.jpg`;
|
||||||
|
const imagePath = path.join(snapshotsDir, imageFilename);
|
||||||
|
|
||||||
fs.readFile(latestFile.path, 'utf8', (err, data) => {
|
fs.readFile(latestFile.path, 'utf8', (err, data) => {
|
||||||
if (err) {
|
if (err) {
|
||||||
return res.status(500).json({ error: 'Fehler beim Lesen der Datei' });
|
return res.status(500).json({ error: 'Fehler beim Lesen der Datei' });
|
||||||
}
|
}
|
||||||
res.json({
|
|
||||||
|
const response = {
|
||||||
filename: latestFile.name,
|
filename: latestFile.name,
|
||||||
mtime: latestFile.mtime.toISOString(),
|
mtime: latestFile.mtime.toISOString(),
|
||||||
content: data
|
content: data
|
||||||
|
};
|
||||||
|
|
||||||
|
fs.readFile(imagePath, { encoding: 'base64' }, (jpgErr, jpgBase64) => {
|
||||||
|
if (!jpgErr && jpgBase64) {
|
||||||
|
response.imageFile = {
|
||||||
|
filename: imageFilename,
|
||||||
|
mimeType: 'image/jpeg',
|
||||||
|
contentBase64: jpgBase64
|
||||||
|
};
|
||||||
|
}
|
||||||
|
res.json(response);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|||||||
Reference in New Issue
Block a user