[split-required] Split final 43 files (500-668 LOC) to complete refactoring

klausur-service (11 files):
- cv_gutter_repair, ocr_pipeline_regression, upload_api
- ocr_pipeline_sessions, smart_spell, nru_worksheet_generator
- ocr_pipeline_overlays, mail/aggregator, zeugnis_api
- cv_syllable_detect, self_rag

backend-lehrer (17 files):
- classroom_engine/suggestions, generators/quiz_generator
- worksheets_api, llm_gateway/comparison, state_engine_api
- classroom/models (→ 4 submodules), services/file_processor
- alerts_agent/api/wizard+digests+routes, content_generators/pdf
- classroom/routes/sessions, llm_gateway/inference
- classroom_engine/analytics, auth/keycloak_auth
- alerts_agent/processing/rule_engine, ai_processor/print_versions

agent-core (5 files):
- brain/memory_store, brain/knowledge_graph, brain/context_manager
- orchestrator/supervisor, sessions/session_manager

admin-lehrer (5 components):
- GridOverlay, StepGridReview, DevOpsPipelineSidebar
- DataFlowDiagram, sbom/wizard/page

website (2 files):
- DependencyMap, lehrer/abitur-archiv

Other: nibis_ingestion, grid_detection_service, export-doclayout-onnx

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Benjamin Admin
2026-04-25 09:41:42 +02:00
parent 451365a312
commit bd4b956e3c
113 changed files with 13790 additions and 14148 deletions

View File

@@ -0,0 +1,180 @@
'use client'
/**
* StepGridReview Stats Bar & OCR Quality Controls
*
* Extracted from StepGridReview.tsx to stay under 500 LOC.
*/
import type { GridZone } from '@/components/grid-editor/types'
interface GridSummary {
total_zones: number
total_columns: number
total_rows: number
total_cells: number
}
interface DictionaryDetection {
is_dictionary: boolean
confidence: number
}
interface PageNumber {
text?: string
number?: number | null
}
interface ReviewStatsBarProps {
summary: GridSummary
dictionaryDetection?: DictionaryDetection | null
pageNumber?: PageNumber | null
lowConfCount: number
acceptedCount: number
totalRows: number
ocrEnhance: boolean
ocrMaxCols: number
ocrMinConf: number
visionFusion: boolean
documentCategory: string
durationSeconds: number
showImage: boolean
onOcrEnhanceChange: (v: boolean) => void
onOcrMaxColsChange: (v: number) => void
onOcrMinConfChange: (v: number) => void
onVisionFusionChange: (v: boolean) => void
onDocumentCategoryChange: (v: string) => void
onAcceptAll: () => void
onAutoCorrect: () => number
onToggleImage: () => void
}
export function ReviewStatsBar({
summary,
dictionaryDetection,
pageNumber,
lowConfCount,
acceptedCount,
totalRows,
ocrEnhance,
ocrMaxCols,
ocrMinConf,
visionFusion,
documentCategory,
durationSeconds,
showImage,
onOcrEnhanceChange,
onOcrMaxColsChange,
onOcrMinConfChange,
onVisionFusionChange,
onDocumentCategoryChange,
onAcceptAll,
onAutoCorrect,
onToggleImage,
}: ReviewStatsBarProps) {
return (
<div className="flex items-center gap-4 text-xs flex-wrap">
<span className="text-gray-500 dark:text-gray-400">
{summary.total_zones} Zone(n), {summary.total_columns} Spalten,{' '}
{summary.total_rows} Zeilen, {summary.total_cells} Zellen
</span>
{dictionaryDetection?.is_dictionary && (
<span className="px-2 py-0.5 rounded-full bg-blue-50 dark:bg-blue-900/20 text-blue-600 dark:text-blue-400 border border-blue-200 dark:border-blue-800">
Woerterbuch ({Math.round(dictionaryDetection.confidence * 100)}%)
</span>
)}
{pageNumber?.text && (
<span className="px-1.5 py-0.5 rounded bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 border border-gray-200 dark:border-gray-600">
S. {pageNumber.number ?? pageNumber.text}
</span>
)}
{lowConfCount > 0 && (
<span className="px-2 py-0.5 rounded-full bg-red-50 dark:bg-red-900/20 text-red-600 dark:text-red-400 border border-red-200 dark:border-red-800">
{lowConfCount} niedrige Konfidenz
</span>
)}
<span className="text-gray-400 dark:text-gray-500">
{acceptedCount}/{totalRows} Zeilen akzeptiert
</span>
{acceptedCount < totalRows && (
<button
onClick={onAcceptAll}
className="text-teal-600 dark:text-teal-400 hover:text-teal-700 dark:hover:text-teal-300"
>
Alle akzeptieren
</button>
)}
{/* OCR Quality Steps */}
<span className="text-gray-400 dark:text-gray-500">|</span>
<label className="flex items-center gap-1 cursor-pointer" title="Step 3: CLAHE + Bilateral-Filter Enhancement">
<input type="checkbox" checked={ocrEnhance} onChange={(e) => onOcrEnhanceChange(e.target.checked)} className="rounded w-3 h-3" />
<span className="text-gray-500 dark:text-gray-400">CLAHE</span>
</label>
<label className="flex items-center gap-1" title="Step 2: Max Spaltenanzahl (0=unbegrenzt)">
<span className="text-gray-500 dark:text-gray-400">MaxCol:</span>
<select value={ocrMaxCols} onChange={(e) => onOcrMaxColsChange(Number(e.target.value))} className="px-1 py-0.5 text-xs rounded border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-700 dark:text-gray-300">
<option value={0}>off</option>
<option value={2}>2</option>
<option value={3}>3</option>
<option value={4}>4</option>
<option value={5}>5</option>
</select>
</label>
<label className="flex items-center gap-1" title="Step 1: Min OCR Confidence (0=auto)">
<span className="text-gray-500 dark:text-gray-400">MinConf:</span>
<select value={ocrMinConf} onChange={(e) => onOcrMinConfChange(Number(e.target.value))} className="px-1 py-0.5 text-xs rounded border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-700 dark:text-gray-300">
<option value={0}>auto</option>
<option value={20}>20</option>
<option value={30}>30</option>
<option value={40}>40</option>
<option value={50}>50</option>
<option value={60}>60</option>
</select>
</label>
<span className="text-gray-400 dark:text-gray-500">|</span>
<label className="flex items-center gap-1 cursor-pointer" title="Step 4: Vision-LLM Fusion">
<input type="checkbox" checked={visionFusion} onChange={(e) => onVisionFusionChange(e.target.checked)} className="rounded w-3 h-3 accent-orange-500" />
<span className={`${visionFusion ? 'text-orange-500 dark:text-orange-400 font-medium' : 'text-gray-500 dark:text-gray-400'}`}>Vision-LLM</span>
</label>
<label className="flex items-center gap-1" title="Dokumenttyp fuer Vision-LLM Prompt">
<span className="text-gray-500 dark:text-gray-400">Typ:</span>
<select value={documentCategory} onChange={(e) => onDocumentCategoryChange(e.target.value)} className="px-1 py-0.5 text-xs rounded border border-gray-200 dark:border-gray-600 bg-white dark:bg-gray-700 text-gray-700 dark:text-gray-300">
<option value="vokabelseite">Vokabelseite</option>
<option value="woerterbuch">Woerterbuch</option>
<option value="arbeitsblatt">Arbeitsblatt</option>
<option value="buchseite">Buchseite</option>
<option value="sonstiges">Sonstiges</option>
</select>
</label>
<div className="ml-auto flex items-center gap-2">
<button
onClick={() => {
const n = onAutoCorrect()
if (n === 0) alert('Keine Muster-Korrekturen gefunden.')
else alert(`${n} Zelle(n) korrigiert (Muster-Vervollstaendigung).`)
}}
className="px-2.5 py-1 rounded text-xs border border-purple-200 dark:border-purple-700 bg-purple-50 dark:bg-purple-900/20 text-purple-700 dark:text-purple-300 hover:bg-purple-100 dark:hover:bg-purple-900/40 transition-colors"
title="Erkennt Muster wie p.70, p.71 und vervollstaendigt partielle Eintraege wie .65 zu p.65"
>
Auto-Korrektur
</button>
<button
onClick={onToggleImage}
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'}
</button>
<span className="text-gray-400 dark:text-gray-500">
{durationSeconds.toFixed(1)}s
</span>
</div>
</div>
)
}