- Markiert die aktuelle Grid-Ausgabe als Referenz fuer Regressionstests.
- {isGroundTruth && ' Diese Session ist bereits als Ground Truth markiert.'}
-
Keine Session ausgewaehlt.
Kein Grid vorhanden.
+ }
+
+ const imageUrl = `${KLAUSUR_API}/api/v1/ocr-pipeline/sessions/${sessionId}/image/cropped`
+
+ return (
+
+ {/* GT Header Bar */}
+
+
+
+ Ground Truth
+ {isGroundTruth && (bereits markiert) }
+
+
+ Pruefen Sie das Ergebnis und markieren Sie es als Referenz fuer Regressionstests.
+
+
+
+ {dirty && (
+
+ {saving ? 'Speichere...' : 'Speichern'}
+
+ )}
+
+ {markSaving ? 'Speichere...' : isGroundTruth ? 'GT aktualisieren' : 'Als Ground Truth markieren'}
+
+
+
{message && (
-
+
{message}
)}
+
+ {/* Stats */}
+
+
+ {grid.summary.total_zones} Zone(n), {grid.summary.total_columns} Spalten,{' '}
+ {grid.summary.total_rows} Zeilen, {grid.summary.total_cells} Zellen
+
+ setShowImage(!showImage)}
+ className={`px-2.5 py-1 rounded text-xs border transition-colors ${
+ showImage
+ ? 'bg-teal-50 dark:bg-teal-900/30 border-teal-200 dark:border-teal-700 text-teal-700 dark:text-teal-300'
+ : 'bg-gray-50 dark:bg-gray-800 border-gray-200 dark:border-gray-700 text-gray-500 dark:text-gray-400'
+ }`}
+ >
+ {showImage ? 'Bild ausblenden' : 'Bild einblenden'}
+
+
+
+ {/* Split View: Image left + Grid right */}
+
+ {showImage && (
+
{}}
+ onHorizontalsChange={() => {}}
+ onCommitUndo={() => {}}
+ onSplitColumnAt={() => {}}
+ onDeleteColumn={() => {}}
+ />
+ )}
+
+
+ {(() => {
+ const groups: GridZone[][] = []
+ for (const zone of grid.zones) {
+ const prev = groups[groups.length - 1]
+ if (prev && zone.vsplit_group != null && prev[0].vsplit_group === zone.vsplit_group) {
+ prev.push(zone)
+ } else {
+ groups.push([zone])
+ }
+ }
+ return groups.map((group) => (
+
+
1 ? 'flex gap-2' : ''}`}>
+ {group.map((zone) => (
+
1 ? 'flex-1 min-w-0' : ''} bg-white dark:bg-gray-800 rounded-lg border border-gray-200 dark:border-gray-700`}
+ >
+
+
+ ))}
+
+
+ ))
+ })()}
+
+
+
+ {/* Keyboard tips */}
+
+ Tab: naechste Zelle
+ Ctrl+Z/Y: Undo/Redo
+ Ctrl+S: Speichern
+
)
}
diff --git a/klausur-service/backend/ocr_pipeline_session_store.py b/klausur-service/backend/ocr_pipeline_session_store.py
index 9645ef2..dc80c55 100644
--- a/klausur-service/backend/ocr_pipeline_session_store.py
+++ b/klausur-service/backend/ocr_pipeline_session_store.py
@@ -262,14 +262,22 @@ async def list_sessions_db(
document_category, doc_type,
parent_session_id, box_index,
document_group_id, page_number,
- created_at, updated_at
+ created_at, updated_at,
+ ground_truth
FROM ocr_pipeline_sessions
{where}
ORDER BY created_at DESC
LIMIT $1
""", limit)
- return [_row_to_dict(row) for row in rows]
+ results = []
+ for row in rows:
+ d = _row_to_dict(row)
+ # Derive is_ground_truth flag from JSONB, then drop the heavy field
+ gt = d.pop("ground_truth", None) or {}
+ d["is_ground_truth"] = bool(gt.get("build_grid_reference"))
+ results.append(d)
+ return results
async def get_sub_sessions(parent_session_id: str) -> List[Dict[str, Any]]: