fix(ocr-compare): Replace Ollama call in grid analysis with heuristic from comparison results
Ollama crashes when two concurrent vision requests hit the 32B model (compare-ocr + analyze-grid). The grid analysis was redundantly calling Ollama again even though compare-ocr already extracted all vocabulary. - compare-ocr now saves vocabulary in session for reuse - analyze-grid builds grid from session data (no Ollama, instant response) - Grid button disabled until comparison results are available - Added export-to-editor functionality Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -139,6 +139,10 @@ export default function OCRComparePage() {
|
||||
const [currentBlockNumber, setCurrentBlockNumber] = useState(1)
|
||||
const [blockReviewData, setBlockReviewData] = useState<Record<number, BlockReviewData>>({})
|
||||
|
||||
// Export State
|
||||
const [isExporting, setIsExporting] = useState(false)
|
||||
const [exportSuccess, setExportSuccess] = useState(false)
|
||||
|
||||
const KLAUSUR_API = '/klausur-api'
|
||||
|
||||
// Load session history
|
||||
@@ -535,6 +539,72 @@ export default function OCRComparePage() {
|
||||
}
|
||||
}, [gridData])
|
||||
|
||||
// Export to Worksheet Editor
|
||||
const handleExportToEditor = useCallback(async () => {
|
||||
if (!gridData || !sessionId) return
|
||||
|
||||
setIsExporting(true)
|
||||
setExportSuccess(false)
|
||||
|
||||
try {
|
||||
// Convert grid cells (percent coordinates) to mm for A4
|
||||
const A4_WIDTH_MM = 210
|
||||
const A4_HEIGHT_MM = 297
|
||||
|
||||
const words = gridData.cells.flat()
|
||||
.filter(cell => cell.status !== 'empty' && cell.text)
|
||||
.map(cell => ({
|
||||
text: cell.text,
|
||||
x_mm: (cell.x / 100) * A4_WIDTH_MM,
|
||||
y_mm: (cell.y / 100) * A4_HEIGHT_MM,
|
||||
width_mm: (cell.width / 100) * A4_WIDTH_MM,
|
||||
height_mm: (cell.height / 100) * A4_HEIGHT_MM,
|
||||
column_type: cell.column_type || 'unknown',
|
||||
logical_row: cell.row,
|
||||
confidence: cell.confidence,
|
||||
}))
|
||||
|
||||
const detectedColumns = gridData.column_types.map((type, idx) => ({
|
||||
column_type: type,
|
||||
x_start_mm: (gridData.column_boundaries[idx] / 100) * A4_WIDTH_MM,
|
||||
x_end_mm: (gridData.column_boundaries[idx + 1] / 100) * A4_WIDTH_MM,
|
||||
}))
|
||||
|
||||
const exportData = {
|
||||
version: '1.0',
|
||||
source: 'ocr-compare',
|
||||
exported_at: new Date().toISOString(),
|
||||
session_id: sessionId,
|
||||
page_number: selectedPage + 1,
|
||||
page_dimensions: {
|
||||
width_mm: A4_WIDTH_MM,
|
||||
height_mm: A4_HEIGHT_MM,
|
||||
format: 'A4',
|
||||
},
|
||||
words,
|
||||
detected_columns: detectedColumns,
|
||||
}
|
||||
|
||||
const res = await fetch(
|
||||
`${KLAUSUR_API}/api/v1/vocab/sessions/${sessionId}/ocr-export/${selectedPage + 1}`,
|
||||
{
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify(exportData),
|
||||
}
|
||||
)
|
||||
|
||||
if (res.ok) {
|
||||
setExportSuccess(true)
|
||||
setTimeout(() => setExportSuccess(false), 3000)
|
||||
}
|
||||
} catch (e) {
|
||||
console.error('Export failed:', e)
|
||||
} finally {
|
||||
setIsExporting(false)
|
||||
}
|
||||
}, [gridData, sessionId, selectedPage, KLAUSUR_API])
|
||||
|
||||
// Count non-empty blocks
|
||||
const nonEmptyBlockCount = useMemo(() => {
|
||||
if (!gridData) return 0
|
||||
@@ -831,6 +901,35 @@ export default function OCRComparePage() {
|
||||
)}
|
||||
</button>
|
||||
)}
|
||||
|
||||
{/* Export to Editor Button */}
|
||||
<button
|
||||
onClick={handleExportToEditor}
|
||||
disabled={isExporting}
|
||||
className={`w-full px-4 py-2 rounded-lg font-medium text-sm transition-colors ${
|
||||
exportSuccess
|
||||
? 'bg-green-100 text-green-700'
|
||||
: 'bg-green-600 text-white hover:bg-green-700'
|
||||
}`}
|
||||
>
|
||||
<span className="flex items-center justify-center gap-2">
|
||||
{isExporting ? (
|
||||
<svg className="animate-spin w-4 h-4" fill="none" viewBox="0 0 24 24">
|
||||
<circle className="opacity-25" cx="12" cy="12" r="10" stroke="currentColor" strokeWidth="4" />
|
||||
<path className="opacity-75" fill="currentColor" d="M4 12a8 8 0 018-8V0C5.373 0 0 5.373 0 12h4z" />
|
||||
</svg>
|
||||
) : exportSuccess ? (
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M5 13l4 4L19 7" />
|
||||
</svg>
|
||||
) : (
|
||||
<svg className="w-4 h-4" fill="none" stroke="currentColor" viewBox="0 0 24 24">
|
||||
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 16v1a3 3 0 003 3h10a3 3 0 003-3v-1m-4-8l-4-4m0 0L8 8m4-4v12" />
|
||||
</svg>
|
||||
)}
|
||||
{exportSuccess ? 'Exportiert!' : 'Zum Editor exportieren'}
|
||||
</span>
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
@@ -1015,11 +1114,25 @@ export default function OCRComparePage() {
|
||||
</div>
|
||||
<div className="p-4">
|
||||
{thumbnails[selectedPage] ? (
|
||||
<img
|
||||
src={thumbnails[selectedPage]}
|
||||
alt={`Seite ${selectedPage + 1}`}
|
||||
className={`rounded-lg border border-slate-200 ${isFullscreen ? 'max-h-[80vh] mx-auto' : 'w-full max-w-2xl mx-auto'}`}
|
||||
/>
|
||||
gridData && showGridOverlay ? (
|
||||
<GridOverlay
|
||||
grid={gridData}
|
||||
imageUrl={thumbnails[selectedPage]}
|
||||
onCellClick={handleCellClick}
|
||||
selectedCell={selectedCell}
|
||||
showEmpty={false}
|
||||
showNumbers={blockReviewMode}
|
||||
showTextLabels={true}
|
||||
highlightedBlockNumber={blockReviewMode ? currentBlockNumber : null}
|
||||
className={`rounded-lg border border-slate-200 overflow-hidden ${isFullscreen ? 'max-h-[80vh] mx-auto' : 'w-full max-w-2xl mx-auto'}`}
|
||||
/>
|
||||
) : (
|
||||
<img
|
||||
src={thumbnails[selectedPage]}
|
||||
alt={`Seite ${selectedPage + 1}`}
|
||||
className={`rounded-lg border border-slate-200 ${isFullscreen ? 'max-h-[80vh] mx-auto' : 'w-full max-w-2xl mx-auto'}`}
|
||||
/>
|
||||
)
|
||||
) : (
|
||||
<div className="h-96 bg-slate-100 rounded-lg flex items-center justify-center text-slate-500">
|
||||
Kein Bild verfuegbar
|
||||
@@ -1116,6 +1229,7 @@ export default function OCRComparePage() {
|
||||
selectedCell={selectedCell}
|
||||
showEmpty={false}
|
||||
showNumbers={blockReviewMode}
|
||||
showTextLabels={!blockReviewMode}
|
||||
highlightedBlockNumber={blockReviewMode ? currentBlockNumber : null}
|
||||
className="rounded-lg border border-slate-200 overflow-hidden"
|
||||
/>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user